fix: rebuild codex client on provider profile switch

This commit is contained in:
Codex
2026-05-26 07:16:04 +08:00
parent 082b44860e
commit b2b2a0e4df
2 changed files with 85 additions and 3 deletions
@@ -804,6 +804,66 @@ test("Code Agent PC gateway prompt reaches Codex stdio instead of internal hardw
}
});
test("Codex stdio manager rebuilds app-server client when provider profile config changes", async () => {
const calls = [];
const createdModels = [];
const closedModels = [];
const fakeCodex = await createFakeCodexCommand();
const codexHome = await prepareFakeCodexHome();
const baseEnv = {
PATH: process.env.PATH,
OPENAI_API_KEY: "test-openai-key-material",
CODEX_HOME: codexHome,
HWLAB_CODE_AGENT_PROVIDER: "codex-stdio",
HWLAB_CODE_AGENT_CODEX_COMMAND: fakeCodex.command,
HWLAB_CODE_AGENT_CODEX_STDIO_ENABLED: "1",
HWLAB_CODE_AGENT_CODEX_STDIO_SUPERVISOR: "repo-owned",
HWLAB_CODE_AGENT_CODEX_SANDBOX: "danger-full-access",
HWLAB_CODE_AGENT_WORKSPACE: process.cwd()
};
const manager = createCodexStdioSessionManager({
idFactory: () => `ses_profile_${createdModels.length + 1}`,
createRpcClient: async ({ env }) => {
const model = env.HWLAB_CODE_AGENT_MODEL;
createdModels.push(model);
const client = createFakeAppServerClient({ calls, responses: [`reply from ${model}`] });
client.close = () => closedModels.push(model);
return client;
}
});
try {
await manager.chat({
conversationId: "cnv_profile_deepseek",
traceId: "trc_profile_deepseek",
message: "first",
env: {
...baseEnv,
HWLAB_CODE_AGENT_MODEL: "deepseek-chat",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://hwlab-deepseek-proxy.hwlab-dev.svc.cluster.local:4000/v1/responses"
}
});
await manager.chat({
conversationId: "cnv_profile_codex",
traceId: "trc_profile_codex",
message: "second",
env: {
...baseEnv,
HWLAB_CODE_AGENT_MODEL: "gpt-5.5",
HWLAB_CODE_AGENT_OPENAI_BASE_URL: "http://172.26.26.227:17680/v1/responses"
}
});
assert.deepEqual(createdModels, ["deepseek-chat", "gpt-5.5"]);
assert.deepEqual(closedModels, ["deepseek-chat"]);
assert.equal(calls.filter((call) => call.method === "initialize").length, 2);
assert.deepEqual(calls.filter((call) => call.method === "turn/start").map((call) => call.args.model), ["deepseek-chat", "gpt-5.5"]);
} finally {
await rm(fakeCodex.root, { recursive: true, force: true });
await rm(codexHome, { recursive: true, force: true });
}
});
test("Code Agent Keil gateway prompt is not blocked by M3 IO intent guard", async () => {
const calls = [];
let providerCalled = false;
+25 -3
View File
@@ -112,6 +112,7 @@ export function createCodexStdioSessionManager(options = {}) {
const sessions = new Map();
const conversations = new Map();
let rpcClient = options.rpcClient ?? null;
let rpcClientSignature = options.rpcClient ? "injected" : null;
let rpcStartedAt = null;
let rpcInitialized = false;
let protocolProbe = null;
@@ -964,18 +965,30 @@ export function createCodexStdioSessionManager(options = {}) {
}
async function ensureRpcClient({ env, availability, timeoutMs } = {}) {
if (rpcClient && typeof rpcClient.startTurn === "function") return rpcClient;
const args = codexAppServerArgs(env);
const childEnv = childProcessEnv(env);
const nextSignature = rpcClientConfigSignature({ availability, args, childEnv });
if (rpcClient && typeof rpcClient.startTurn === "function" && rpcClientSignature === nextSignature) return rpcClient;
if (rpcClient && rpcClientSignature !== nextSignature && typeof rpcClient.close === "function") {
rpcClient.close();
}
if (rpcClientSignature !== nextSignature) {
rpcClient = null;
rpcInitialized = false;
rpcClientSignature = null;
}
rpcClient = options.createRpcClient
? await options.createRpcClient({ env, availability })
: createCodexAppServerJsonLineClient({
command: availability.command,
args: codexAppServerArgs(env),
env: childProcessEnv(env),
args,
env: childEnv,
cwd: availability.workspace
});
if (typeof rpcClient.initialize === "function") {
await rpcClient.initialize(effectiveTimeout(timeoutMs));
}
rpcClientSignature = nextSignature;
rpcInitialized = true;
rpcStartedAt = timestampFor(nowDefault);
return rpcClient;
@@ -3550,6 +3563,15 @@ function codexProviderNoProxyEntries(env = process.env) {
}
}
function rpcClientConfigSignature({ availability, args = [], childEnv = {} } = {}) {
return JSON.stringify({
command: availability?.command ?? null,
workspace: availability?.workspace ?? null,
codexHome: childEnv.CODEX_HOME ?? null,
args
});
}
function mergeNoProxy(...parts) {
const entries = [];
const seen = new Set();