From 7a1eae245b085060436fb4f4fc6bc6177fc75e9d Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 1 Nov 2026 17:51:37 +0100 Subject: [PATCH] fix(server): every opening path hands registration the options it launched with Registration took the launch options as optional and reread the defaults when a path did not supply them, as provider-session import did. It is now required, so create, resume, reload and import each resolve once before opening. The option helpers take named inputs. --- .../src/server/agent/agent-manager.test.ts | 40 +++++++++++++ .../server/src/server/agent/agent-manager.ts | 56 +++++++++++-------- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/packages/server/src/server/agent/agent-manager.test.ts b/packages/server/src/server/agent/agent-manager.test.ts index cdc052c037..b34f68d2d1 100644 --- a/packages/server/src/server/agent/agent-manager.test.ts +++ b/packages/server/src/server/agent/agent-manager.test.ts @@ -4567,6 +4567,46 @@ test("resumeAgentFromPersistence keeps metadata config, applies overrides, and p }); }); +test("importProviderSession reports the defaults the session launched with, not defaults edited during the import", async () => { + const workdir = mkdtempSync(join(tmpdir(), "agent-manager-import-options-")); + let tier = "default"; + class ImportClient extends TestAgentClient { + effectiveOptions(own: ProviderOptions | undefined): ProviderOptions { + return { service_tier: tier, ...own }; + } + + async importSession(input: ImportProviderSessionInput) { + // The user edits the default in config.json after the provider has launched. + tier = "fast"; + return { + session: new TestAgentSession({ provider: "codex", cwd: workdir }), + config: { provider: "codex" as const, cwd: workdir }, + persistence: { + provider: "codex" as const, + sessionId: input.providerHandleId, + nativeHandle: input.providerHandleId, + metadata: { provider: "codex", cwd: workdir }, + }, + timeline: [], + }; + } + } + const manager = new AgentManager({ + clients: { codex: new ImportClient() }, + registry: new AgentStorage(join(workdir, "agents"), logger), + logger, + }); + + const imported = await manager.importProviderSession({ + provider: "codex", + providerHandleId: "thread-selected", + cwd: workdir, + workspaceId: "ws-imported", + }); + + expect(imported.effectiveOptions).toEqual({ service_tier: "default" }); +}); + test("importProviderSession imports the selected session without listing and publishes ready state", async () => { const workdir = mkdtempSync(join(tmpdir(), "agent-manager-import-session-")); const storagePath = join(workdir, "agents"); diff --git a/packages/server/src/server/agent/agent-manager.ts b/packages/server/src/server/agent/agent-manager.ts index 5fb27d8e73..a68510cc17 100644 --- a/packages/server/src/server/agent/agent-manager.ts +++ b/packages/server/src/server/agent/agent-manager.ts @@ -1411,7 +1411,7 @@ export class AgentManager { ); const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext); const createOptions = this.buildCreateSessionOptions(options); - const launchedOptions = this.launchOptionsFor(client, storedConfig.providerOptions); + const launchedOptions = this.launchOptionsFor({ client, own: storedConfig.providerOptions }); const session = await client.createSession(providerLaunchConfig, launchContext, createOptions); await this.requireExternalMcpSupport(session, providerLaunchConfig, launchContext); const agent = await this.registerSession(session, storedConfig, resolvedAgentId, { @@ -1538,7 +1538,7 @@ export class AgentManager { }, ); const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext); - const launchedOptions = this.launchOptionsFor(client, storedConfig.providerOptions); + const launchedOptions = this.launchOptionsFor({ client, own: storedConfig.providerOptions }); const session = await client.resumeSession( handle, providerLaunchConfig, @@ -1603,6 +1603,7 @@ export class AgentManager { }, ); const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext); + const launchedOptions = this.launchOptionsFor({ client, own: storedConfig.providerOptions }); const imported = await client.importSession( { providerHandleId: input.providerHandleId, @@ -1621,6 +1622,7 @@ export class AgentManager { handedToRegistration = true; const agent = await this.registerSession(imported.session, importedConfig, resolvedAgentId, { + launchedOptions, labels: input.labels, workspaceId: input.workspaceId, timelineRows, @@ -1726,7 +1728,7 @@ export class AgentManager { this.assertAcceptingAgentRegistrations(); this.paseoToolPolicies.set(agentId, paseoToolPolicy); - const launchedOptions = this.launchOptionsFor(client, storedConfig.providerOptions); + const launchedOptions = this.launchOptionsFor({ client, own: storedConfig.providerOptions }); session = handle ? await client.resumeSession(handle, providerLaunchConfig, launchContext) : await client.createSession(providerLaunchConfig, launchContext); @@ -2309,22 +2311,30 @@ export class AgentManager { // Changes the provider made on its own while applying are older than its final answer. await this.drainSessionEvents(agentId); } - this.storeOptionsPatch(agent, { ...patch, ...ended }); + this.storeOptionsPatch({ agent, patch: { ...patch, ...ended } }); this.updateEffectiveOptions({ agent, sent, reported: ended }); this.touchUpdatedAt(agent); this.emitState(agent); } /** The config as the opened session left it: its own difference from the options it was given. */ - private withOpenedOptions(config: AgentSessionConfig, session: AgentSession): AgentSessionConfig { - const opened = session.openOptionsPatch; + private withOpenedOptions(input: { + config: AgentSessionConfig; + session: AgentSession; + }): AgentSessionConfig { + const { config } = input; + const opened = input.session.openOptionsPatch; if (!opened || Object.keys(opened).length === 0) return config; const own = applyProviderOptionsPatch({ options: config.providerOptions, patch: opened }); return { ...config, providerOptions: Object.keys(own).length > 0 ? own : undefined }; } - private storeOptionsPatch(agent: ManagedAgent, patch: ProviderOptionsPatch): void { - const own = applyProviderOptionsPatch({ options: agent.config.providerOptions, patch }); + private storeOptionsPatch(input: { agent: ManagedAgent; patch: ProviderOptionsPatch }): void { + const { agent } = input; + const own = applyProviderOptionsPatch({ + options: agent.config.providerOptions, + patch: input.patch, + }); agent.config.providerOptions = Object.keys(own).length > 0 ? own : undefined; } @@ -2350,21 +2360,20 @@ export class AgentManager { */ private openedEffectiveOptions(input: { session: AgentSession; - openedConfig: AgentSessionConfig; - launchedOptions: ProviderOptions | undefined; + launchedOptions: ProviderOptions; }): ProviderOptions { - const { session, openedConfig, launchedOptions } = input; return applyProviderOptionsPatch({ - options: - launchedOptions ?? - this.effectiveOptionsFor(openedConfig.provider, openedConfig.providerOptions), - patch: session.openOptionsPatch ?? {}, + options: input.launchedOptions, + patch: input.session.openOptionsPatch ?? {}, }); } /** The options a client opens with: its own defaults merged under the agent's. */ - private launchOptionsFor(client: AgentClient, own: ProviderOptions | undefined): ProviderOptions { - return client.effectiveOptions?.(own) ?? own ?? {}; + private launchOptionsFor(input: { + client: AgentClient; + own: ProviderOptions | undefined; + }): ProviderOptions { + return input.client.effectiveOptions?.(input.own) ?? input.own ?? {}; } private effectiveOptionsFor( @@ -2372,7 +2381,7 @@ export class AgentManager { own: ProviderOptions | undefined, ): ProviderOptions { const client = this.clients.get(provider); - return client ? this.launchOptionsFor(client, own) : (own ?? {}); + return client ? this.launchOptionsFor({ client, own }) : (own ?? {}); } async setTitle(agentId: string, title: string): Promise { @@ -3889,7 +3898,7 @@ export class AgentManager { session: AgentSession, openedConfig: AgentSessionConfig, agentId: string, - options?: { + options: { createdAt?: Date; updatedAt?: Date; lastUserMessageAt?: Date | null; @@ -3912,7 +3921,7 @@ export class AgentManager { * starts from these rather than reading the defaults again, which may have been edited * while the provider was opening. */ - launchedOptions?: ProviderOptions; + launchedOptions: ProviderOptions; restoring?: boolean; initialTitle?: string | null; publishWhenReady?: boolean; @@ -3924,7 +3933,7 @@ export class AgentManager { let registered = false; try { this.assertAcceptingAgentRegistrations(); - const config = this.withOpenedOptions(openedConfig, session); + const config = this.withOpenedOptions({ config: openedConfig, session }); const resolvedAgentId = validateAgentId(agentId, "registerSession"); if (this.agents.has(resolvedAgentId)) { throw new Error(`Agent with id ${resolvedAgentId} already exists`); @@ -3948,8 +3957,7 @@ export class AgentManager { config, effectiveOptions: this.openedEffectiveOptions({ session, - openedConfig, - launchedOptions: options?.launchedOptions, + launchedOptions: options.launchedOptions, }), now, durableTimelineHasRows, @@ -4793,7 +4801,7 @@ export class AgentManager { flags: StreamEventFlags; }): void { const { agent, patch, flags } = params; - this.storeOptionsPatch(agent, patch); + this.storeOptionsPatch({ agent, patch }); this.updateEffectiveOptions({ agent, sent: {}, reported: patch }); flags.shouldDispatchEvent = false; this.emitState(agent);