Skip to content

Commit 81070b2

Browse files
fix(codex): restore gateway models after reconnect
Use the gateway model catalog when the native app-server returns stale model metadata. Generated-By: PostHog Code Task-Id: dfa9be76-95bf-4166-8adc-529483e3d525
1 parent e02e0c1 commit 81070b2

4 files changed

Lines changed: 48 additions & 4 deletions

File tree

packages/agent/src/adapters/acp-connection.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ function createCodexConnection(config: AcpConnectionConfig): AcpConnection {
230230
},
231231
model: codexOptions.model,
232232
reasoningEffort: codexOptions.reasoningEffort,
233+
allowedModelIds: config.allowedModelIds,
233234
processCallbacks: config.processCallbacks,
234235
onStructuredOutput: config.onStructuredOutput,
235236
logger: config.logger?.child("CodexAppServerAgent"),

packages/agent/src/adapters/codex-app-server/codex-app-server-agent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ export interface CodexAppServerAgentOptions {
203203
processOptions: CodexAppServerProcessOptions;
204204
model?: string;
205205
reasoningEffort?: string;
206+
allowedModelIds?: ReadonlySet<string>;
206207
processCallbacks?: ProcessSpawnedCallback;
207208
logger?: Logger;
208209
onStructuredOutput?: (output: Record<string, unknown>) => Promise<void>;
@@ -265,6 +266,7 @@ export class CodexAppServerAgent extends BaseAcpAgent {
265266
this.config = new SessionConfigState(
266267
options.model ?? DEFAULT_CODEX_MODEL,
267268
options.reasoningEffort,
269+
options.allowedModelIds,
268270
);
269271
this.onStructuredOutput = options.onStructuredOutput;
270272
this.developerInstructions = options.processOptions.developerInstructions;

packages/agent/src/adapters/codex-app-server/session-config.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,26 @@ describe("SessionConfigState", () => {
134134
config.options.find((option) => option.category === "mode")?.currentValue,
135135
).toBe("full-access");
136136
});
137+
138+
it("uses gateway models when the app-server model list is stale", () => {
139+
const config = new SessionConfigState(
140+
"gpt-5.5",
141+
undefined,
142+
new Set(["gpt-5.5", "gpt-5.6-sol"]),
143+
);
144+
145+
config.loadModels([
146+
{ id: "gpt-5.5", model: "gpt-5.5", displayName: "GPT-5.5" },
147+
]);
148+
149+
const modelOption = config.options.find(
150+
(option) => option.category === "model",
151+
);
152+
expect(modelOption?.type === "select" ? modelOption.options : []).toEqual([
153+
{ name: "GPT-5.5", value: "gpt-5.5" },
154+
{ name: "gpt-5.6-sol", value: "gpt-5.6-sol" },
155+
]);
156+
});
137157
});
138158

139159
describe("buildConfigOptions", () => {

packages/agent/src/adapters/codex-app-server/session-config.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,16 @@ export class SessionConfigState {
229229
private models: Array<{ id: string; name: string }> = [];
230230
private efforts: string[] = [];
231231
private _options: SessionConfigOption[] = [];
232+
private readonly allowedModelIds?: ReadonlySet<string>;
232233

233-
constructor(model: string, effort?: string) {
234+
constructor(
235+
model: string,
236+
effort?: string,
237+
allowedModelIds?: ReadonlySet<string>,
238+
) {
234239
this._model = model;
235240
this._effort = effort;
241+
this.allowedModelIds = allowedModelIds?.size ? allowedModelIds : undefined;
236242
this.rebuild();
237243
}
238244

@@ -262,8 +268,12 @@ export class SessionConfigState {
262268
): { modeChanged: boolean } {
263269
let modeChanged = false;
264270
if (typeof value === "string") {
265-
if (configId === "model") this._model = value;
266-
else if (configId === "effort") this._effort = value;
271+
if (
272+
configId === "model" &&
273+
(!this.allowedModelIds || this.allowedModelIds.has(value))
274+
) {
275+
this._model = value;
276+
} else if (configId === "effort") this._effort = value;
267277
else if (configId === "mode") {
268278
this._mode = resolveCodexMode(value);
269279
modeChanged = true;
@@ -279,13 +289,24 @@ export class SessionConfigState {
279289
* populate efforts, so fall back to the shared codex model→effort map.
280290
*/
281291
loadModels(rawModels: RawModel[]): void {
282-
this.models = rawModels
292+
const liveModels = rawModels
283293
.filter((m) => !m?.hidden)
284294
.filter((m) => isOpenAIModel(m as unknown as GatewayModel))
285295
.map((m) => ({
286296
id: (m.id ?? m.model) as string,
287297
name: (m.displayName ?? m.id ?? m.model) as string,
288298
}));
299+
if (this.allowedModelIds) {
300+
const liveModelsById = new Map(
301+
liveModels.map((model) => [model.id, model]),
302+
);
303+
this.models = [...this.allowedModelIds].map(
304+
(modelId) =>
305+
liveModelsById.get(modelId) ?? { id: modelId, name: modelId },
306+
);
307+
} else {
308+
this.models = liveModels;
309+
}
289310
const current = rawModels.find(
290311
(m) => m.id === this._model || m.model === this._model,
291312
);

0 commit comments

Comments
 (0)