Skip to content

Commit 51de5b2

Browse files
authored
feat: Support 1M context window and model-aware effort options (#1245)
1. Add 1M context window support for Opus 4.6 and Sonnet 4.6 models 2. Use model-aware default context window instead of hardcoded 200k fallback 3. Increase JSONL hydration token budget to 800k for 1M-context models 4. Show effort dropdown only for models that support it (Opus 4.5+, Sonnet 4.6) 5. Restrict "Max" effort option to Opus 4.6 only 6. Rebuild effort config options dynamically when switching models
1 parent 2dbdfd9 commit 51de5b2

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

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

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ import { fetchMcpToolMetadata } from "./mcp/tool-metadata";
5959
import { canUseTool } from "./permissions/permission-handlers";
6060
import { getAvailableSlashCommands } from "./session/commands";
6161
import { parseMcpServers } from "./session/mcp-config";
62-
import { DEFAULT_MODEL, toSdkModelId } from "./session/models";
62+
import {
63+
DEFAULT_MODEL,
64+
getDefaultContextWindow,
65+
getEffortOptions,
66+
toSdkModelId,
67+
} from "./session/models";
6368
import {
6469
buildSessionOptions,
6570
buildSystemPrompt,
@@ -337,7 +342,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
337342
(m) => m.contextWindow,
338343
);
339344
const contextWindowSize =
340-
contextWindows.length > 0 ? Math.min(...contextWindows) : 200000;
345+
contextWindows.length > 0
346+
? Math.min(...contextWindows)
347+
: getDefaultContextWindow(this.session.modelId ?? "");
341348

342349
// Send usage_update notification
343350
if (lastAssistantTotalUsage !== null) {
@@ -509,6 +516,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
509516
const sdkModelId = toSdkModelId(params.modelId);
510517
await this.session.query.setModel(sdkModelId);
511518
this.session.modelId = params.modelId;
519+
this.rebuildEffortConfigOption(params.modelId);
512520
await this.updateConfigOption("model", params.modelId);
513521
return {};
514522
}
@@ -559,6 +567,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
559567
const sdkModelId = toSdkModelId(params.value);
560568
await this.session.query.setModel(sdkModelId);
561569
this.session.modelId = params.value;
570+
this.rebuildEffortConfigOption(params.value);
562571
} else if (params.configId === "effort") {
563572
const newEffort = params.value as EffortLevel;
564573
this.session.effort = newEffort;
@@ -865,7 +874,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
865874
description: mode.description ?? undefined,
866875
}));
867876

868-
return [
877+
const configOptions: SessionConfigOption[] = [
869878
{
870879
id: "mode",
871880
name: "Approval Preset",
@@ -885,21 +894,67 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
885894
category: "model" as SessionConfigOptionCategory,
886895
description: "Choose which model Claude should use",
887896
},
888-
{
897+
];
898+
899+
const effortOptions = getEffortOptions(modelOptions.currentModelId);
900+
if (effortOptions) {
901+
configOptions.push({
889902
id: "effort",
890903
name: "Effort",
891904
type: "select",
892905
currentValue: currentEffort,
893-
options: [
894-
{ value: "low", name: "Low" },
895-
{ value: "medium", name: "Medium" },
896-
{ value: "high", name: "High" },
897-
{ value: "max", name: "Max" },
898-
],
906+
options: effortOptions,
899907
category: "thought_level" as SessionConfigOptionCategory,
900908
description: "Controls how much effort Claude puts into its response",
901-
},
902-
];
909+
});
910+
}
911+
912+
return configOptions;
913+
}
914+
915+
private rebuildEffortConfigOption(modelId: string): void {
916+
const effortOptions = getEffortOptions(modelId);
917+
const existingEffort = this.session.configOptions.find(
918+
(o) => o.id === "effort",
919+
);
920+
921+
if (!effortOptions) {
922+
this.session.configOptions = this.session.configOptions.filter(
923+
(o) => o.id !== "effort",
924+
);
925+
if (this.session.effort) {
926+
this.session.effort = undefined;
927+
this.session.queryOptions.effort = undefined;
928+
}
929+
return;
930+
}
931+
932+
const currentValue = existingEffort?.currentValue ?? "high";
933+
const isValidValue = effortOptions.some((o) => o.value === currentValue);
934+
const resolvedValue = isValidValue ? currentValue : "high";
935+
936+
if (resolvedValue !== currentValue && this.session.effort) {
937+
this.session.effort = resolvedValue as EffortLevel;
938+
this.session.queryOptions.effort = resolvedValue as EffortLevel;
939+
}
940+
941+
const effortConfig: SessionConfigOption = {
942+
id: "effort",
943+
name: "Effort",
944+
type: "select",
945+
currentValue: resolvedValue,
946+
options: effortOptions,
947+
category: "thought_level" as SessionConfigOptionCategory,
948+
description: "Controls how much effort Claude puts into its response",
949+
};
950+
951+
if (existingEffort) {
952+
this.session.configOptions = this.session.configOptions.map((o) =>
953+
o.id === "effort" ? effortConfig : o,
954+
);
955+
} else {
956+
this.session.configOptions.push(effortConfig);
957+
}
903958
}
904959

905960
private async sendAvailableCommandsUpdate(): Promise<void> {

packages/agent/src/adapters/claude/session/jsonl-hydration.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as path from "node:path";
55
import type { ContentBlock } from "@agentclientprotocol/sdk";
66
import type { PostHogAPIClient } from "../../../posthog-api";
77
import type { StoredEntry } from "../../../types";
8+
import { supports1MContext } from "./models";
89

910
interface ConversationTurn {
1011
role: "user" | "assistant";
@@ -188,6 +189,7 @@ export function rebuildConversation(
188189

189190
const CHARS_PER_TOKEN = 4;
190191
const DEFAULT_MAX_TOKENS = 150_000;
192+
const LARGE_CONTEXT_MAX_TOKENS = 800_000;
191193

192194
function estimateTurnTokens(turn: ConversationTurn): number {
193195
let chars = 0;
@@ -546,7 +548,10 @@ export async function hydrateSessionJsonl(params: {
546548
return;
547549
}
548550

549-
const conversation = selectRecentTurns(allTurns);
551+
const maxTokens = supports1MContext(params.model ?? "")
552+
? LARGE_CONTEXT_MAX_TOKENS
553+
: DEFAULT_MAX_TOKENS;
554+
const conversation = selectRecentTurns(allTurns, maxTokens);
550555
log.info("Selected recent turns for hydration", {
551556
totalTurns: allTurns.length,
552557
selectedTurns: conversation.length,

packages/agent/src/adapters/claude/session/models.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,53 @@ const GATEWAY_TO_SDK_MODEL: Record<string, string> = {
1111
export function toSdkModelId(modelId: string): string {
1212
return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
1313
}
14+
15+
const MODELS_WITH_1M_CONTEXT = new Set([
16+
"claude-opus-4-6",
17+
"claude-sonnet-4-6",
18+
]);
19+
20+
export function supports1MContext(modelId: string): boolean {
21+
return MODELS_WITH_1M_CONTEXT.has(modelId);
22+
}
23+
24+
export function getDefaultContextWindow(modelId: string): number {
25+
return supports1MContext(modelId) ? 1_000_000 : 200_000;
26+
}
27+
28+
const MODELS_WITH_EFFORT = new Set([
29+
"claude-opus-4-5",
30+
"claude-opus-4-6",
31+
"claude-sonnet-4-6",
32+
]);
33+
34+
const MODELS_WITH_MAX_EFFORT = new Set(["claude-opus-4-6"]);
35+
36+
export function supportsEffort(modelId: string): boolean {
37+
return MODELS_WITH_EFFORT.has(modelId);
38+
}
39+
40+
export function supportsMaxEffort(modelId: string): boolean {
41+
return MODELS_WITH_MAX_EFFORT.has(modelId);
42+
}
43+
44+
interface EffortOption {
45+
value: string;
46+
name: string;
47+
}
48+
49+
export function getEffortOptions(modelId: string): EffortOption[] | null {
50+
if (!supportsEffort(modelId)) return null;
51+
52+
const options: EffortOption[] = [
53+
{ value: "low", name: "Low" },
54+
{ value: "medium", name: "Medium" },
55+
{ value: "high", name: "High" },
56+
];
57+
58+
if (supportsMaxEffort(modelId)) {
59+
options.push({ value: "max", name: "Max" });
60+
}
61+
62+
return options;
63+
}

0 commit comments

Comments
 (0)