Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 8033e36

Browse files
committed
make session config authoritative on reconnect
1 parent 892bb29 commit 8033e36

4 files changed

Lines changed: 94 additions & 3 deletions

File tree

‎packages/agent/src/adapters/claude/claude-agent.resume-model.test.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,59 @@ describe("ClaudeAcpAgent session creation", () => {
317317
},
318318
);
319319

320+
// The shared claude settings file is cross-session state: the session's own
321+
// pick must win over it, with the file only as a fallback.
322+
it.each([
323+
{
324+
name: "prefers meta.model over the settings file model",
325+
sessionId: "0197a000-0000-7000-8000-000000000011",
326+
settingsModel: "claude-opus-4-8",
327+
model: "claude-fable-5",
328+
expectedSetModel: "claude-fable-5",
329+
expectedCurrentValue: "claude-fable-5",
330+
},
331+
{
332+
name: "falls back to the settings file model without meta.model",
333+
sessionId: "0197a000-0000-7000-8000-000000000012",
334+
settingsModel: "claude-fable-5",
335+
model: undefined,
336+
expectedSetModel: "claude-fable-5",
337+
expectedCurrentValue: "claude-fable-5",
338+
},
339+
])(
340+
"$name",
341+
async ({
342+
sessionId,
343+
settingsModel,
344+
model,
345+
expectedSetModel,
346+
expectedCurrentValue,
347+
}) => {
348+
const settingsPath = path.join(configDir, "settings.json");
349+
writeFileSync(settingsPath, JSON.stringify({ model: settingsModel }));
350+
try {
351+
const agent = makeAgent();
352+
353+
const response = await agent.resumeSession({
354+
sessionId,
355+
cwd,
356+
mcpServers: [],
357+
_meta: { taskRunId: "run-1", ...(model ? { model } : {}) },
358+
});
359+
360+
expect(createdQueries).toHaveLength(1);
361+
expect(createdQueries[0].setModel).toHaveBeenCalledWith(
362+
expectedSetModel,
363+
);
364+
expect(getModelConfigOption(response)?.currentValue).toBe(
365+
expectedCurrentValue,
366+
);
367+
} finally {
368+
rmSync(settingsPath, { force: true });
369+
}
370+
},
371+
);
372+
320373
// New sessions pass the model to the SDK at spawn, never via setModel. The
321374
// Codex-model row guards the desync that surfaced as "picked gpt-5.5, session
322375
// ran Opus": a non-Anthropic id on the Claude adapter must fall back to the

‎packages/agent/src/adapters/claude/claude-agent.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1963,8 +1963,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
19631963
const settingsManager = new SettingsManager(cwd);
19641964
await settingsManager.initialize();
19651965

1966+
// The session's explicit pick outranks the shared claude settings file:
1967+
// that file is cross-session state and must only ever be a fallback.
19661968
const earlyModelId =
1967-
settingsManager.getSettings().model || meta?.model || "";
1969+
meta?.model || settingsManager.getSettings().model || "";
19681970

19691971
// Register the in-process general local-tools MCP server. Tools self-gate
19701972
// via the registry (e.g. signed-commit is cloud-only and needs a GH token),
@@ -2198,7 +2200,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
21982200

21992201
const [rawModelOptions] = await Promise.all([
22002202
this.getModelConfigOptions(
2201-
settingsManager.getSettings().model || meta?.model || undefined,
2203+
meta?.model || settingsManager.getSettings().model || undefined,
22022204
this.options?.gatewayEnv?.anthropicBaseUrl,
22032205
this.options?.gatewayEnv?.anthropicAuthToken,
22042206
),
@@ -2263,8 +2265,8 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
22632265
}
22642266

22652267
const resolvedModelId = resolveInitialModelId(modelOptions, [
2266-
settingsManager.getSettings().model,
22672268
meta?.model,
2269+
settingsManager.getSettings().model,
22682270
]);
22692271
session.modelId = resolvedModelId;
22702272
session.lastContextWindowSize =

‎packages/core/src/sessions/sessionService.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ import {
4949
type Task,
5050
} from "@posthog/shared/domain-types";
5151
import type { SpeechKind, SpeechSource } from "../speech/identifiers";
52+
import {
53+
CONTEXT_WINDOW_OPTION_CATEGORY,
54+
FAST_MODE_OPTION_CATEGORY,
55+
} from "../task-detail/previewConfig";
5256
import {
5357
isNotification,
5458
POSTHOG_NOTIFICATIONS,
@@ -2002,6 +2006,33 @@ export class SessionService {
20022006
const persistedModel =
20032007
modelOpt?.type === "select" ? modelOpt.currentValue : undefined;
20042008

2009+
// Same for effort, context window and fast mode: the session's own
2010+
// persisted config is authoritative on resume.
2011+
const effortOpt = getConfigOptionByCategory(
2012+
persistedConfigOptions,
2013+
"thought_level",
2014+
);
2015+
const persistedEffort =
2016+
effortOpt?.type === "select" &&
2017+
effortLevelSchema.safeParse(effortOpt.currentValue).success
2018+
? (effortOpt.currentValue as EffortLevel)
2019+
: undefined;
2020+
const contextOpt = getConfigOptionByCategory(
2021+
persistedConfigOptions,
2022+
CONTEXT_WINDOW_OPTION_CATEGORY,
2023+
);
2024+
const persistedContextWindow =
2025+
contextOpt?.type === "select" &&
2026+
(contextOpt.currentValue === "200k" || contextOpt.currentValue === "1m")
2027+
? contextOpt.currentValue
2028+
: undefined;
2029+
const fastOpt = getConfigOptionByCategory(
2030+
persistedConfigOptions,
2031+
FAST_MODE_OPTION_CATEGORY,
2032+
);
2033+
const persistedFastMode =
2034+
fastOpt?.type === "select" ? fastOpt.currentValue === "on" : undefined;
2035+
20052036
this.d.trpc.workspace.verify
20062037
.query({ taskId })
20072038
.then((workspaceResult) => {
@@ -2037,6 +2068,9 @@ export class SessionService {
20372068
adapter: resolvedAdapter,
20382069
permissionMode: persistedMode,
20392070
model: persistedModel,
2071+
effort: persistedEffort,
2072+
contextWindow: persistedContextWindow,
2073+
fastMode: persistedFastMode,
20402074
customInstructions: customInstructions || undefined,
20412075
});
20422076

‎packages/workspace-server/src/services/agent/schemas.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ export const reconnectSessionInput = z.object({
231231
model: z.string().optional(),
232232
customInstructions: customInstructionsField,
233233
effort: effortLevelSchema.optional(),
234+
contextWindow: z.enum(["200k", "1m"]).optional(),
235+
fastMode: z.boolean().optional(),
234236
jsonSchema: z.record(z.string(), z.unknown()).nullish(),
235237
/** See startSessionInput.rtkEnabled. */
236238
rtkEnabled: z.boolean().optional(),

0 commit comments

Comments
 (0)