From e054b905b5192b7c18aa18c281f3f5af2ce56802 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 8 Nov 2026 14:18:41 +0100 Subject: [PATCH] Convert legacy options when resolving a stored agent's effective options --- .../src/server/agent/agent-manager.test.ts | 40 ++++++++++++++++++- .../server/src/server/agent/agent-manager.ts | 9 ++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/server/src/server/agent/agent-manager.test.ts b/packages/server/src/server/agent/agent-manager.test.ts index 839d4f2a95..c55150b51f 100644 --- a/packages/server/src/server/agent/agent-manager.test.ts +++ b/packages/server/src/server/agent/agent-manager.test.ts @@ -19,10 +19,10 @@ import { type AgentManagerEvent, type ManagedAgent, } from "./agent-manager.js"; -import { AgentStorage } from "./agent-storage.js"; +import { AgentStorage, parseStoredAgentRecord } from "./agent-storage.js"; import { InMemoryAgentTimelineStore } from "./agent-timeline-store.js"; import { limitAgentTimelineItemContent } from "./agent-timeline-content.js"; -import { toAgentPayload } from "./agent-projections.js"; +import { buildStoredAgentPayload, toAgentPayload } from "./agent-projections.js"; import type { ProviderOptions } from "@getpaseo/protocol/agent-types"; import { projectTimelineRows } from "./timeline-projection.js"; import { getOpenAgentTabLabel, PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels"; @@ -13564,6 +13564,42 @@ describe("agent options", () => { expect(toAgentPayload(agent!).options).toEqual({ service_tier: "default", web_search: true }); }); + test("a stored legacy Codex record reads its saved Fast speed as the speed its control selects", () => { + const manager = new AgentManager({ clients: { codex: new OptionsClient() }, logger }); + // Written by a daemon that took `fast_mode`; never reopened since the upgrade. + const record = parseStoredAgentRecord({ + id: "agent-legacy-fast", + provider: "codex", + cwd: "/repo", + createdAt: "2026-01-01T00:00:00.000Z", + updatedAt: "2026-01-01T00:00:00.000Z", + lastStatus: "closed", + config: { featureValues: { fast_mode: true } }, + features: [ + { + type: "select", + id: "service_tier", + label: "Speed", + value: "fast", + options: [ + { id: "default", label: "Normal", isDefault: true }, + { id: "fast", label: "Fast" }, + ], + }, + ], + persistence: { provider: "codex", sessionId: "thread-1" }, + }); + + const payload = buildStoredAgentPayload(record, ["codex"], (provider, own) => + manager.effectiveOptionsFor(provider, own), + ); + + expect(payload.options).toEqual({ service_tier: "fast" }); + expect(payload.features).toEqual([ + expect.objectContaining({ id: "service_tier", value: "fast" }), + ]); + }); + test("setAgentOptions hands the provider only the keys the patch sets", async () => { const { manager, client, storage, agentId } = await createOptionsAgent({ web_search: true }); diff --git a/packages/server/src/server/agent/agent-manager.ts b/packages/server/src/server/agent/agent-manager.ts index 99a20d37f5..4d2db2024a 100644 --- a/packages/server/src/server/agent/agent-manager.ts +++ b/packages/server/src/server/agent/agent-manager.ts @@ -2575,10 +2575,15 @@ export class AgentManager { return input.client.effectiveOptions?.(input.own) ?? input.own ?? {}; } - /** What an agent of this provider runs with, given its own options: the defaults under them. */ + /** + * What an agent of this provider runs with, given its own options: the defaults under them. + * Stored records are read through here before they are reopened, so the legacy conversion + * runs here too; a record converted on open passes through unchanged. + */ effectiveOptionsFor(provider: AgentProvider, own: ProviderOptions | undefined): ProviderOptions { const client = this.clients.get(provider); - return client ? this.launchOptionsFor({ client, own }) : (own ?? {}); + const converted = this.convertLegacyOptions(provider, own); + return client ? this.launchOptionsFor({ client, own: converted }) : (converted ?? {}); } async setTitle(agentId: string, title: string): Promise {