fix(acpx-engine): check the final env for Codex host-key auth selection
The Codex api-key auth-request check ran on the run's explicit env, before the launch merged the projected host environment in. A local Codex launch could inherit OPENAI_API_KEY or CODEX_API_KEY from the host after that check, so the launched process had the credential but never got the required api-key authentication request. Move the check into resolveRuntimeEnv, after the host projection and explicit env merge produce the final environment. This covers every launch path (local, runner-less fallback, remote sandbox), because they all resolve their launch env through this one function. Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
73d49c3741
commit
c2e517d948
|
|
@ -1572,6 +1572,35 @@ describe("shared ACPX engine runtime behavior", () => {
|
|||
},
|
||||
);
|
||||
|
||||
it.each(["OPENAI_API_KEY", "CODEX_API_KEY"] as const)(
|
||||
"selects Codex ACP API-key authentication when only the host process provides %s",
|
||||
async (apiKeyName) => {
|
||||
const root = await makeTempRoot();
|
||||
const codexHome = path.join(root, "codex-home");
|
||||
await fs.mkdir(codexHome, { recursive: true });
|
||||
|
||||
// Simulate a local launch that inherits a provider key from the host
|
||||
// process environment. No adapter config sets the key directly, so the
|
||||
// launched env only receives it through host projection.
|
||||
vi.stubEnv(apiKeyName, "sk-host-inherited-key");
|
||||
try {
|
||||
const { sessionInputs } = await runExecutor({
|
||||
agent: "codex",
|
||||
stateDir: path.join(root, "state"),
|
||||
env: { CODEX_HOME: codexHome },
|
||||
paperclipRuntimeSkills: [],
|
||||
paperclipSkillSync: { desiredSkills: [] },
|
||||
});
|
||||
|
||||
const env = (sessionInputs[0]!.sessionOptions as { env: Record<string, string> }).env;
|
||||
expect(env[apiKeyName]).toBe("sk-host-inherited-key");
|
||||
expect(env.DEFAULT_AUTH_REQUEST).toBe(JSON.stringify({ methodId: "api-key" }));
|
||||
} finally {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("busts the session fingerprint when resolved adapter env changes but not across wakes", async () => {
|
||||
const root = await makeTempRoot();
|
||||
const stateDir = path.join(root, "state");
|
||||
|
|
|
|||
|
|
@ -1967,17 +1967,6 @@ async function buildRuntime(input: {
|
|||
// are absent from tempKeysApplied and keep their compatibility protection.
|
||||
if (!scratchKeys.has(key) || value !== scratch.dir) resolvedAdapterEnv[key] = value;
|
||||
}
|
||||
// codex-acp supports both key names, but ACP clients must select its
|
||||
// api-key authentication method during session creation. Without this
|
||||
// request, the server advertises authentication and rejects session/new even
|
||||
// though the credential is present in the launched process environment.
|
||||
if (
|
||||
acpxAgent === "codex" &&
|
||||
(env.OPENAI_API_KEY || env.CODEX_API_KEY) &&
|
||||
!env.DEFAULT_AUTH_REQUEST
|
||||
) {
|
||||
env.DEFAULT_AUTH_REQUEST = JSON.stringify({ methodId: "api-key" });
|
||||
}
|
||||
if (authToken) env.PAPERCLIP_API_KEY = authToken;
|
||||
// For the claude agent, set model via ANTHROPIC_MODEL at startup rather than
|
||||
// via session/set_config_option — the ACP server's set_config_option handler
|
||||
|
|
@ -2629,11 +2618,25 @@ function resolveRuntimeEnv(
|
|||
env,
|
||||
(options.platform ?? process.platform) === "win32",
|
||||
);
|
||||
return Object.fromEntries(
|
||||
const finalEnv = Object.fromEntries(
|
||||
Object.entries(mergedEnv).filter(
|
||||
(entry): entry is [string, string] => typeof entry[1] === "string",
|
||||
),
|
||||
);
|
||||
// codex-acp supports both key names, but ACP clients must select its
|
||||
// api-key authentication method during session creation. Without this
|
||||
// request, the server advertises authentication and rejects session/new even
|
||||
// though the credential is present in the launched process environment. Check
|
||||
// the final merged environment, not just the explicit run config, so a host
|
||||
// key the local launch inherits still selects this default.
|
||||
if (
|
||||
acpxAgent === "codex" &&
|
||||
(finalEnv.OPENAI_API_KEY || finalEnv.CODEX_API_KEY) &&
|
||||
!finalEnv.DEFAULT_AUTH_REQUEST
|
||||
) {
|
||||
finalEnv.DEFAULT_AUTH_REQUEST = JSON.stringify({ methodId: "api-key" });
|
||||
}
|
||||
return finalEnv;
|
||||
}
|
||||
|
||||
function mergeRuntimeEnvironment(
|
||||
|
|
|
|||
Loading…
Reference in New Issue