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

Commit 4eae17a

Browse files
Merge branch 'main' into posthog-code/fix-task-archive-pin-failures
2 parents 72ea06a + 9ad6664 commit 4eae17a

66 files changed

Lines changed: 3033 additions & 477 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/code/snapshots.yml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -537,21 +537,17 @@ snapshots:
537537
git-dialogs--sync-success--light:
538538
hash: v1.k4693efd2.8d79d77c9338aeaa73734836f0e17fde987f6d3239914f4013889d3110e5088d.Rq-w2o1uhes_a5hUWqMXeiDkfnoPCZPt932LkEQXCco
539539
loops-loopslistview--comprehensive--dark:
540-
hash: v1.k4693efd2.c128012b7f910839e65e8eaf14d5456c93f90d77747d0625ca35fddd8d5adf7a.fuugB5Ov6boP7fg6fWP7A7IG2yYnPUAAWLKZLuV8rZ0
540+
hash: v1.k4693efd2.25744a9471ec664c70e346ac5cb330c025a031626bc486abe8820b7b6dbc1a19.Shh8Yl7SdombnNc4-uJjlIbx-n4C0MGXxtOGB1bjJTY
541541
loops-loopslistview--comprehensive--light:
542-
hash: v1.k4693efd2.4d25a48e0bd7e60d3bbb433e246486e4c9ac95979db86e20eb8bf5cb119d1eb2.EzrENjVHwVMyV00pEA-71TeVomJtchiym5DqWFkujlA
542+
hash: v1.k4693efd2.ef8e012cc506a0bf5e761ec8e89943820d4b391f7023f506da7d79586b49a356.ATiN8Y0hUmfGvr0rUytYWX-fITyHRTgeN1i_HOOGZuc
543543
loops-loopslistview--long-mixed-list--dark:
544-
hash: v1.k4693efd2.fdbf0c24cb386c4345279db3e648efe72aa8a2a984d491361522f62074a771c9.2QmRxRMWYFFfBFrbMm0Dm9rWcvd1lfRbc0egfzoESnY
544+
hash: v1.k4693efd2.c9915e22655fe1f6b77eabaf11d8876648c387ceebac6cd8be1880e7255bd7a5.y-gyTKSp7wYbuS57TABRbEyMSL9_U1wvHYvbyCeAbiQ
545545
loops-loopslistview--long-mixed-list--light:
546-
hash: v1.k4693efd2.aa17d9b6f40fbd3914bd742951db9f90add0bd233c54170803c8483aeb45b43b.yXpqqGczOq1uW5k4Ie7ddNTnne-cFTglZ9cL800pXB4
547-
loops-loopslistview--shared-page-header--dark:
548-
hash: v1.k4693efd2.14ab860dcb0df51b4019f012288e45f3a2c4c23fa08e809efef8c04b5687fe36.qoMLHprZmV2wON156NBbl3sqP84a4nXgdYm1H6WpTVM
549-
loops-loopslistview--shared-page-header--light:
550-
hash: v1.k4693efd2.23be5408ff3b4a912a0ce0457af8638124b99a01c88a499d0f4204cee2c62487.dGBP1K08MA2Fvhml-BPl5eFra8U83JXmy04NK9wOQnM
546+
hash: v1.k4693efd2.5fec9e1a3c14f1bc319cd7e5abc24b3d66ed787986a103b5330f49341d4983ad.mpD-eTS6vkSbsJdOYaHXASHQd5IiJGWRCb6uDGlzhpk
551547
loops-loopslistview--with-builder-sessions--dark:
552-
hash: v1.k4693efd2.df4a1a4764e1de7b69905814b09c30d9b43f705f5e66c7ad83829c567d4faa1a.jthufoX_KPbbxScjj3pPBA4lEtT9Ym6zUCH-v1759GM
548+
hash: v1.k4693efd2.06b404e4016ba0eb86769331a574bc640be1ad26ebf3a04d0256334d1da041a3.fPfF8c2wNFREfy8dVRdsw7W9Fpqj7HEtuaERMroNHMA
553549
loops-loopslistview--with-builder-sessions--light:
554-
hash: v1.k4693efd2.eb65c38bb0de7efedbc0d7a6c27a6695528e2e6b60949617e7d22335b1e68c8b.fJr9uykvc8S7LI_ASXk5KRqxXCwIqh6ld9zfzs4shP0
550+
hash: v1.k4693efd2.30f89915a12cadf313216403fb0f3e221f3c33cee763f0692ae2b96b4332e2b9.j00vexnH4QS0wJAHn7pQNpeITYYRtOxlvB2ngyzqU0c
555551
primitives-pageheader--title-only--dark:
556552
hash: v1.k4693efd2.f7f8c560b1e5cf20050189e10fc0f129e555ba672300a7a09bef5827c171a743.yP9Pp_k51agu_9Ri55Y7uCAPBQ1UTeUQJA9U9dPMKfQ
557553
primitives-pageheader--title-only--light:

apps/mobile/src/app/settings/index.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { Text } from "@components/text";
2+
import {
3+
EFFORT_LEVEL_LABELS,
4+
EFFORT_LEVELS,
5+
} from "@posthog/shared/domain-types";
26
import { router } from "expo-router";
37
import { ArrowSquareOut, CaretRight, SpeakerHigh } from "phosphor-react-native";
48
import { useState } from "react";
@@ -103,11 +107,10 @@ const REASONING_EFFORT_OPTIONS: ReadonlyArray<{
103107
label: "Last used",
104108
description: "Remember the effort level you picked last time",
105109
},
106-
{ value: "low", label: "Low" },
107-
{ value: "medium", label: "Medium" },
108-
{ value: "high", label: "High" },
109-
{ value: "xhigh", label: "Extra High" },
110-
{ value: "max", label: "Max" },
110+
...EFFORT_LEVELS.map((value) => ({
111+
value,
112+
label: EFFORT_LEVEL_LABELS[value],
113+
})),
111114
];
112115

113116
function themeLabel(theme: ThemePreference): string {

apps/mobile/src/features/preferences/stores/preferencesStore.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { EffortLevel } from "@posthog/shared/domain-types";
12
import AsyncStorage from "@react-native-async-storage/async-storage";
23
import { Platform } from "react-native";
34
import { create } from "zustand";
@@ -40,13 +41,7 @@ export type CompletionSound =
4041

4142
export type InitialTaskMode = "plan" | "last_used";
4243

43-
export type DefaultReasoningEffort =
44-
| "low"
45-
| "medium"
46-
| "high"
47-
| "xhigh"
48-
| "max"
49-
| "last_used";
44+
export type DefaultReasoningEffort = EffortLevel | "last_used";
5045

5146
interface PreferencesState {
5247
pingsEnabled: boolean;

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
@@ -323,6 +323,59 @@ describe("ClaudeAcpAgent session creation", () => {
323323
},
324324
);
325325

326+
// The shared claude settings file is cross-session state: the session's own
327+
// pick must win over it, with the file only as a fallback.
328+
it.each([
329+
{
330+
name: "prefers meta.model over the settings file model",
331+
sessionId: "0197a000-0000-7000-8000-000000000011",
332+
settingsModel: "claude-opus-4-8",
333+
model: "claude-fable-5",
334+
expectedSetModel: "claude-fable-5",
335+
expectedCurrentValue: "claude-fable-5",
336+
},
337+
{
338+
name: "falls back to the settings file model without meta.model",
339+
sessionId: "0197a000-0000-7000-8000-000000000012",
340+
settingsModel: "claude-fable-5",
341+
model: undefined,
342+
expectedSetModel: "claude-fable-5",
343+
expectedCurrentValue: "claude-fable-5",
344+
},
345+
])(
346+
"$name",
347+
async ({
348+
sessionId,
349+
settingsModel,
350+
model,
351+
expectedSetModel,
352+
expectedCurrentValue,
353+
}) => {
354+
const settingsPath = path.join(configDir, "settings.json");
355+
writeFileSync(settingsPath, JSON.stringify({ model: settingsModel }));
356+
try {
357+
const agent = makeAgent();
358+
359+
const response = await agent.resumeSession({
360+
sessionId,
361+
cwd,
362+
mcpServers: [],
363+
_meta: { taskRunId: "run-1", ...(model ? { model } : {}) },
364+
});
365+
366+
expect(createdQueries).toHaveLength(1);
367+
expect(createdQueries[0].setModel).toHaveBeenCalledWith(
368+
expectedSetModel,
369+
);
370+
expect(getModelConfigOption(response)?.currentValue).toBe(
371+
expectedCurrentValue,
372+
);
373+
} finally {
374+
rmSync(settingsPath, { force: true });
375+
}
376+
},
377+
);
378+
326379
// New sessions pass the model to the SDK at spawn, never via setModel. The
327380
// Codex-model row guards the desync that surfaced as "picked gpt-5.5, session
328381
// 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: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,12 @@ import {
115115
resolveInitialModelId,
116116
} from "./session/model-config";
117117
import {
118+
CONTEXT_WINDOW_1M_BETA,
119+
CONTEXT_WINDOW_200K_TOKENS,
118120
DEFAULT_EFFORT,
119121
DEFAULT_MODEL,
120122
fastModeStateEnabled,
123+
getContextWindowOptions,
121124
getEffortOptions,
122125
resolveEffortForModel,
123126
resolveModelPreference,
@@ -131,6 +134,8 @@ import {
131134
buildSystemPrompt,
132135
type GatewayEnv,
133136
type ProcessSpawnedInfo,
137+
toEffortFlagSettings,
138+
toSdkEffort,
134139
} from "./session/options";
135140
import { SettingsManager } from "./session/settings";
136141
import {
@@ -1752,15 +1757,24 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
17521757
this.session.lastContextWindowSize =
17531758
this.getContextWindowForModel(resolvedValue);
17541759
this.rebuildEffortConfigOption(resolvedValue);
1760+
this.rebuildContextWindowConfigOption(resolvedValue);
17551761
this.rebuildFastModeConfigOption(resolvedValue);
17561762
} else if (params.configId === "effort") {
17571763
const newEffort = resolvedValue as EffortLevel;
17581764
this.session.effort = newEffort;
1759-
this.session.queryOptions.effort = newEffort;
1760-
await this.session.query.applyFlagSettings({
1761-
// @ts-expect-error SDK Settings.effortLevel omits "max" but runtime accepts it
1762-
effortLevel: newEffort,
1763-
});
1765+
this.session.queryOptions.effort = toSdkEffort(newEffort);
1766+
await this.session.query.applyFlagSettings(
1767+
toEffortFlagSettings(newEffort),
1768+
);
1769+
} else if (params.configId === "context_window") {
1770+
const enable1M = resolvedValue === "1m";
1771+
// queryOptions is read per prompt, so this applies from the next turn.
1772+
this.session.queryOptions.betas = enable1M
1773+
? [CONTEXT_WINDOW_1M_BETA]
1774+
: undefined;
1775+
this.session.lastContextWindowSize = enable1M
1776+
? this.getContextWindowForModel(this.session.modelId ?? "")
1777+
: CONTEXT_WINDOW_200K_TOKENS;
17641778
} else if (params.configId === "fast") {
17651779
// SDK flag first: a rejected control request leaves state untouched.
17661780
const enabled = resolvedValue === "on";
@@ -1949,8 +1963,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
19491963
const settingsManager = new SettingsManager(cwd);
19501964
await settingsManager.initialize();
19511965

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.
19521968
const earlyModelId =
1953-
settingsManager.getSettings().model || meta?.model || "";
1969+
meta?.model || settingsManager.getSettings().model || "";
19541970

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

21852201
const [rawModelOptions] = await Promise.all([
21862202
this.getModelConfigOptions(
2187-
settingsManager.getSettings().model || meta?.model || undefined,
2203+
meta?.model || settingsManager.getSettings().model || undefined,
21882204
this.options?.gatewayEnv?.anthropicBaseUrl,
21892205
this.options?.gatewayEnv?.anthropicAuthToken,
21902206
),
@@ -2249,13 +2265,15 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
22492265
}
22502266

22512267
const resolvedModelId = resolveInitialModelId(modelOptions, [
2252-
settingsManager.getSettings().model,
22532268
meta?.model,
2269+
settingsManager.getSettings().model,
22542270
]);
22552271
modelOptions.currentModelId = resolvedModelId;
22562272
session.modelId = resolvedModelId;
22572273
session.lastContextWindowSize =
2258-
this.getContextWindowForModel(resolvedModelId);
2274+
meta?.contextWindow === "200k"
2275+
? CONTEXT_WINDOW_200K_TOKENS
2276+
: this.getContextWindowForModel(resolvedModelId);
22592277

22602278
const resolvedSdkModel = toSdkModelId(resolvedModelId);
22612279

@@ -2270,17 +2288,26 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
22702288
// Keep thinking enabled by default for effort-capable models (see
22712289
// DEFAULT_EFFORT).
22722290
const resolvedEffort = resolveEffortForModel(resolvedModelId, effort);
2273-
if (resolvedEffort && resolvedEffort !== effort) {
2291+
// Ultracode re-applies even when the requested effort stands: the flag
2292+
// only reaches the session through applyFlagSettings.
2293+
if (
2294+
resolvedEffort &&
2295+
(resolvedEffort !== effort || resolvedEffort === "ultracode")
2296+
) {
22742297
this.session.effort = resolvedEffort;
2275-
this.session.queryOptions.effort = resolvedEffort;
2276-
await this.session.query.applyFlagSettings({
2277-
// @ts-expect-error SDK Settings.effortLevel omits "max" but runtime accepts it
2278-
effortLevel: resolvedEffort,
2279-
});
2298+
this.session.queryOptions.effort = toSdkEffort(resolvedEffort);
2299+
await this.session.query.applyFlagSettings(
2300+
toEffortFlagSettings(resolvedEffort),
2301+
);
2302+
}
2303+
2304+
if (supports1MContext(resolvedModelId) && meta?.contextWindow !== "200k") {
2305+
options.betas = [CONTEXT_WINDOW_1M_BETA];
22802306
}
22812307

2282-
if (supports1MContext(resolvedModelId)) {
2283-
options.betas = ["context-1m-2025-08-07"];
2308+
if (meta?.fastMode && supportsFastMode(resolvedModelId)) {
2309+
this.session.fastModeEnabled = true;
2310+
await this.session.query.applyFlagSettings({ fastMode: true });
22842311
}
22852312

22862313
const availableModes = getAvailableModes();
@@ -2460,23 +2487,67 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
24602487
});
24612488
}
24622489

2490+
const contextOption = this.contextWindowConfigOption(
2491+
modelOptions.currentModelId,
2492+
);
2493+
if (contextOption) {
2494+
configOptions.push(contextOption);
2495+
}
2496+
24632497
if (supportsFastMode(modelOptions.currentModelId)) {
24642498
configOptions.push(this.fastModeConfigOption(fastModeEnabled ?? false));
24652499
}
24662500

24672501
return configOptions;
24682502
}
24692503

2504+
private contextWindowConfigOption(
2505+
modelId: string,
2506+
): SessionConfigOption | null {
2507+
const contextOptions = getContextWindowOptions(modelId);
2508+
if (!contextOptions) return null;
2509+
const is1M = this.session.queryOptions.betas?.includes(
2510+
CONTEXT_WINDOW_1M_BETA,
2511+
);
2512+
return {
2513+
id: "context_window",
2514+
name: "Context Window",
2515+
type: "select",
2516+
currentValue: is1M ? "1m" : "200k",
2517+
options: contextOptions,
2518+
category: "_context_window" as SessionConfigOptionCategory,
2519+
description: "Choose the context window size for this session",
2520+
};
2521+
}
2522+
2523+
private rebuildContextWindowConfigOption(modelId: string): void {
2524+
const withoutContext = this.session.configOptions.filter(
2525+
(o) => o.id !== "context_window",
2526+
);
2527+
if (!supports1MContext(modelId)) {
2528+
this.session.queryOptions.betas = undefined;
2529+
this.session.configOptions = withoutContext;
2530+
return;
2531+
}
2532+
// Switching onto a 1M-capable model restores the default 1M window.
2533+
this.session.queryOptions.betas = [CONTEXT_WINDOW_1M_BETA];
2534+
const contextOption = this.contextWindowConfigOption(modelId);
2535+
this.session.configOptions = contextOption
2536+
? [...withoutContext, contextOption]
2537+
: withoutContext;
2538+
}
2539+
24702540
private fastModeConfigOption(enabled: boolean): SessionConfigOption {
24712541
return {
24722542
id: "fast",
2473-
name: "Fast mode",
2543+
name: "Fast Mode",
24742544
type: "select",
24752545
currentValue: enabled ? "on" : "off",
24762546
options: [
24772547
{ value: "on", name: "On" },
24782548
{ value: "off", name: "Off" },
24792549
],
2550+
category: "_fast_mode" as SessionConfigOptionCategory,
24802551
description: "Faster responses on supported models",
24812552
};
24822553
}
@@ -2564,6 +2635,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
25642635
this.session.queryOptions.effort = undefined;
25652636
void this.session.query.applyFlagSettings({
25662637
effortLevel: undefined,
2638+
ultracode: false,
25672639
});
25682640
}
25692641
return;
@@ -2578,12 +2650,12 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
25782650
// Set the default when none is chosen yet (see DEFAULT_EFFORT), or re-apply
25792651
// when the prior level is invalid for the newly selected model.
25802652
if (!this.session.effort || resolvedValue !== currentValue) {
2581-
this.session.effort = resolvedValue as EffortLevel;
2582-
this.session.queryOptions.effort = resolvedValue as EffortLevel;
2583-
void this.session.query.applyFlagSettings({
2584-
// @ts-expect-error SDK Settings.effortLevel omits "max" but runtime accepts it
2585-
effortLevel: resolvedValue,
2586-
});
2653+
const resolvedEffort = resolvedValue as EffortLevel;
2654+
this.session.effort = resolvedEffort;
2655+
this.session.queryOptions.effort = toSdkEffort(resolvedEffort);
2656+
void this.session.query.applyFlagSettings(
2657+
toEffortFlagSettings(resolvedEffort),
2658+
);
25872659
}
25882660

25892661
const effortConfig: SessionConfigOption = {

0 commit comments

Comments
 (0)