@oh-my-pi/pi-coding-agent 13.9.2 → 13.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/examples/sdk/02-custom-model.ts +2 -1
- package/package.json +7 -7
- package/src/cli/args.ts +6 -5
- package/src/cli/list-models.ts +2 -2
- package/src/commands/launch.ts +3 -3
- package/src/config/model-registry.ts +85 -39
- package/src/config/model-resolver.ts +47 -21
- package/src/config/settings-schema.ts +56 -2
- package/src/discovery/helpers.ts +2 -2
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/loader.ts +3 -2
- package/src/extensibility/extensions/types.ts +10 -7
- package/src/extensibility/hooks/types.ts +2 -0
- package/src/main.ts +5 -22
- package/src/memories/index.ts +7 -3
- package/src/modes/components/footer.ts +10 -8
- package/src/modes/components/model-selector.ts +33 -38
- package/src/modes/components/settings-defs.ts +31 -2
- package/src/modes/components/settings-selector.ts +16 -5
- package/src/modes/components/status-line/context-thresholds.ts +68 -0
- package/src/modes/components/status-line/segments.ts +11 -12
- package/src/modes/components/thinking-selector.ts +7 -7
- package/src/modes/components/tree-selector.ts +3 -2
- package/src/modes/controllers/command-controller.ts +11 -26
- package/src/modes/controllers/event-controller.ts +16 -3
- package/src/modes/controllers/input-controller.ts +4 -2
- package/src/modes/controllers/selector-controller.ts +5 -4
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/rpc/rpc-client.ts +5 -10
- package/src/modes/rpc/rpc-types.ts +5 -5
- package/src/modes/theme/theme.ts +8 -3
- package/src/priority.json +1 -0
- package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
- package/src/prompts/system/system-prompt.md +18 -2
- package/src/prompts/tools/hashline.md +139 -83
- package/src/sdk.ts +22 -14
- package/src/session/agent-session.ts +259 -117
- package/src/session/agent-storage.ts +14 -14
- package/src/session/compaction/compaction.ts +500 -13
- package/src/session/messages.ts +12 -1
- package/src/session/session-manager.ts +77 -19
- package/src/slash-commands/builtin-registry.ts +48 -0
- package/src/task/agents.ts +3 -2
- package/src/task/executor.ts +2 -2
- package/src/task/types.ts +2 -1
- package/src/thinking.ts +87 -0
- package/src/tools/browser.ts +15 -6
- package/src/tools/fetch.ts +118 -100
- package/src/web/search/providers/exa.ts +74 -3
|
@@ -24,15 +24,17 @@ import {
|
|
|
24
24
|
type AgentState,
|
|
25
25
|
type AgentTool,
|
|
26
26
|
INTENT_FIELD,
|
|
27
|
+
ThinkingLevel,
|
|
27
28
|
} from "@oh-my-pi/pi-agent-core";
|
|
28
29
|
import type {
|
|
29
30
|
AssistantMessage,
|
|
31
|
+
Effort,
|
|
30
32
|
ImageContent,
|
|
31
33
|
Message,
|
|
32
34
|
Model,
|
|
33
35
|
ProviderSessionState,
|
|
36
|
+
ServiceTier,
|
|
34
37
|
TextContent,
|
|
35
|
-
ThinkingLevel,
|
|
36
38
|
ToolCall,
|
|
37
39
|
ToolChoice,
|
|
38
40
|
Usage,
|
|
@@ -40,11 +42,10 @@ import type {
|
|
|
40
42
|
} from "@oh-my-pi/pi-ai";
|
|
41
43
|
import {
|
|
42
44
|
calculateRateLimitBackoffMs,
|
|
43
|
-
|
|
45
|
+
getSupportedEfforts,
|
|
44
46
|
isContextOverflow,
|
|
45
47
|
modelsAreEqual,
|
|
46
48
|
parseRateLimitReason,
|
|
47
|
-
supportsXhigh,
|
|
48
49
|
} from "@oh-my-pi/pi-ai";
|
|
49
50
|
import { abortableSleep, getAgentDbPath, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
50
51
|
import type { AsyncJob, AsyncJobManager } from "../async";
|
|
@@ -87,6 +88,7 @@ import { executePython as executePythonCommand, type PythonResult } from "../ipy
|
|
|
87
88
|
import { getCurrentThemeName, theme } from "../modes/theme/theme";
|
|
88
89
|
import { normalizeDiff, normalizeToLF, ParseError, previewPatch, stripBom } from "../patch";
|
|
89
90
|
import type { PlanModeState } from "../plan-mode/state";
|
|
91
|
+
import autoHandoffThresholdFocusPrompt from "../prompts/system/auto-handoff-threshold-focus.md" with { type: "text" };
|
|
90
92
|
import planModeActivePrompt from "../prompts/system/plan-mode-active.md" with { type: "text" };
|
|
91
93
|
import planModeReferencePrompt from "../prompts/system/plan-mode-reference.md" with { type: "text" };
|
|
92
94
|
import planModeToolDecisionReminderPrompt from "../prompts/system/plan-mode-tool-decision-reminder.md" with {
|
|
@@ -94,6 +96,7 @@ import planModeToolDecisionReminderPrompt from "../prompts/system/plan-mode-tool
|
|
|
94
96
|
};
|
|
95
97
|
import ttsrInterruptTemplate from "../prompts/system/ttsr-interrupt.md" with { type: "text" };
|
|
96
98
|
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
99
|
+
import { resolveThinkingLevelForModel, toReasoningEffort } from "../thinking";
|
|
97
100
|
import type { CheckpointState } from "../tools/checkpoint";
|
|
98
101
|
import { outputMeta } from "../tools/output-meta";
|
|
99
102
|
import { resolveToCwd } from "../tools/path-utils";
|
|
@@ -130,9 +133,10 @@ import { getLatestCompactionEntry } from "./session-manager";
|
|
|
130
133
|
/** Session-specific events that extend the core AgentEvent */
|
|
131
134
|
export type AgentSessionEvent =
|
|
132
135
|
| AgentEvent
|
|
133
|
-
| { type: "auto_compaction_start"; reason: "threshold" | "overflow" }
|
|
136
|
+
| { type: "auto_compaction_start"; reason: "threshold" | "overflow"; action: "context-full" | "handoff" }
|
|
134
137
|
| {
|
|
135
138
|
type: "auto_compaction_end";
|
|
139
|
+
action: "context-full" | "handoff";
|
|
136
140
|
result: CompactionResult | undefined;
|
|
137
141
|
aborted: boolean;
|
|
138
142
|
willRetry: boolean;
|
|
@@ -163,7 +167,9 @@ export interface AgentSessionConfig {
|
|
|
163
167
|
/** Async background jobs launched by tools */
|
|
164
168
|
asyncJobManager?: AsyncJobManager;
|
|
165
169
|
/** Models to cycle through with Ctrl+P (from --models flag) */
|
|
166
|
-
scopedModels?: Array<{ model: Model; thinkingLevel
|
|
170
|
+
scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
171
|
+
/** Initial session thinking selector. */
|
|
172
|
+
thinkingLevel?: ThinkingLevel;
|
|
167
173
|
/** Prompt templates for expansion */
|
|
168
174
|
promptTemplates?: PromptTemplate[];
|
|
169
175
|
/** File-based slash commands for expansion */
|
|
@@ -205,12 +211,14 @@ export interface PromptOptions {
|
|
|
205
211
|
toolChoice?: ToolChoice;
|
|
206
212
|
/** Send as developer/system message instead of user. Providers that support it use the developer role; others fall back to user. */
|
|
207
213
|
synthetic?: boolean;
|
|
214
|
+
/** Skip pre-send compaction checks for this prompt (internal use for maintenance flows). */
|
|
215
|
+
skipCompactionCheck?: boolean;
|
|
208
216
|
}
|
|
209
217
|
|
|
210
218
|
/** Result from cycleModel() */
|
|
211
219
|
export interface ModelCycleResult {
|
|
212
220
|
model: Model;
|
|
213
|
-
thinkingLevel: ThinkingLevel;
|
|
221
|
+
thinkingLevel: ThinkingLevel | undefined;
|
|
214
222
|
/** Whether cycling through scoped models (--models flag) or all available */
|
|
215
223
|
isScoped: boolean;
|
|
216
224
|
}
|
|
@@ -218,7 +226,7 @@ export interface ModelCycleResult {
|
|
|
218
226
|
/** Result from cycleRoleModels() */
|
|
219
227
|
export interface RoleModelCycleResult {
|
|
220
228
|
model: Model;
|
|
221
|
-
thinkingLevel: ThinkingLevel;
|
|
229
|
+
thinkingLevel: ThinkingLevel | undefined;
|
|
222
230
|
role: ModelRole;
|
|
223
231
|
}
|
|
224
232
|
|
|
@@ -245,6 +253,12 @@ export interface SessionStats {
|
|
|
245
253
|
/** Result from handoff() */
|
|
246
254
|
export interface HandoffResult {
|
|
247
255
|
document: string;
|
|
256
|
+
savedPath?: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
interface HandoffOptions {
|
|
260
|
+
autoTriggered?: boolean;
|
|
261
|
+
signal?: AbortSignal;
|
|
248
262
|
}
|
|
249
263
|
|
|
250
264
|
/** Internal marker for hook messages queued through the agent loop */
|
|
@@ -254,6 +268,8 @@ export interface HandoffResult {
|
|
|
254
268
|
|
|
255
269
|
/** Standard thinking levels */
|
|
256
270
|
|
|
271
|
+
const AUTO_HANDOFF_THRESHOLD_FOCUS = renderPromptTemplate(autoHandoffThresholdFocusPrompt);
|
|
272
|
+
|
|
257
273
|
const noOpUIContext: ExtensionUIContext = {
|
|
258
274
|
select: async (_title, _options, _dialogOptions) => undefined,
|
|
259
275
|
confirm: async (_title, _message, _dialogOptions) => false,
|
|
@@ -292,7 +308,8 @@ export class AgentSession {
|
|
|
292
308
|
readonly settings: Settings;
|
|
293
309
|
|
|
294
310
|
#asyncJobManager: AsyncJobManager | undefined = undefined;
|
|
295
|
-
#scopedModels: Array<{ model: Model; thinkingLevel
|
|
311
|
+
#scopedModels: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
312
|
+
#thinkingLevel: ThinkingLevel | undefined;
|
|
296
313
|
#promptTemplates: PromptTemplate[];
|
|
297
314
|
#slashCommands: FileSlashCommand[];
|
|
298
315
|
|
|
@@ -393,6 +410,7 @@ export class AgentSession {
|
|
|
393
410
|
this.settings = config.settings;
|
|
394
411
|
this.#asyncJobManager = config.asyncJobManager;
|
|
395
412
|
this.#scopedModels = config.scopedModels ?? [];
|
|
413
|
+
this.#thinkingLevel = config.thinkingLevel;
|
|
396
414
|
this.#promptTemplates = config.promptTemplates ?? [];
|
|
397
415
|
this.#slashCommands = config.slashCommands ?? [];
|
|
398
416
|
this.#extensionRunner = config.extensionRunner;
|
|
@@ -1395,10 +1413,15 @@ export class AgentSession {
|
|
|
1395
1413
|
};
|
|
1396
1414
|
await this.#extensionRunner.emit(extensionEvent);
|
|
1397
1415
|
} else if (event.type === "auto_compaction_start") {
|
|
1398
|
-
await this.#extensionRunner.emit({
|
|
1416
|
+
await this.#extensionRunner.emit({
|
|
1417
|
+
type: "auto_compaction_start",
|
|
1418
|
+
reason: event.reason,
|
|
1419
|
+
action: event.action,
|
|
1420
|
+
});
|
|
1399
1421
|
} else if (event.type === "auto_compaction_end") {
|
|
1400
1422
|
await this.#extensionRunner.emit({
|
|
1401
1423
|
type: "auto_compaction_end",
|
|
1424
|
+
action: event.action,
|
|
1402
1425
|
result: event.result,
|
|
1403
1426
|
aborted: event.aborted,
|
|
1404
1427
|
willRetry: event.willRetry,
|
|
@@ -1526,8 +1549,12 @@ export class AgentSession {
|
|
|
1526
1549
|
}
|
|
1527
1550
|
|
|
1528
1551
|
/** Current thinking level */
|
|
1529
|
-
get thinkingLevel(): ThinkingLevel {
|
|
1530
|
-
return this
|
|
1552
|
+
get thinkingLevel(): ThinkingLevel | undefined {
|
|
1553
|
+
return this.#thinkingLevel;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
get serviceTier(): ServiceTier | undefined {
|
|
1557
|
+
return this.agent.serviceTier;
|
|
1531
1558
|
}
|
|
1532
1559
|
|
|
1533
1560
|
/** Whether agent is currently streaming a response */
|
|
@@ -1702,7 +1729,7 @@ export class AgentSession {
|
|
|
1702
1729
|
}
|
|
1703
1730
|
|
|
1704
1731
|
/** Scoped models for cycling (from --models flag) */
|
|
1705
|
-
get scopedModels(): ReadonlyArray<{ model: Model; thinkingLevel
|
|
1732
|
+
get scopedModels(): ReadonlyArray<{ model: Model; thinkingLevel?: ThinkingLevel }> {
|
|
1706
1733
|
return this.#scopedModels;
|
|
1707
1734
|
}
|
|
1708
1735
|
|
|
@@ -1967,7 +1994,9 @@ export class AgentSession {
|
|
|
1967
1994
|
async #promptWithMessage(
|
|
1968
1995
|
message: AgentMessage,
|
|
1969
1996
|
expandedText: string,
|
|
1970
|
-
options?: Pick<PromptOptions, "toolChoice" | "images"> & {
|
|
1997
|
+
options?: Pick<PromptOptions, "toolChoice" | "images" | "skipCompactionCheck"> & {
|
|
1998
|
+
skipPostPromptRecoveryWait?: boolean;
|
|
1999
|
+
},
|
|
1971
2000
|
): Promise<void> {
|
|
1972
2001
|
this.#promptInFlight = true;
|
|
1973
2002
|
const generation = this.#promptGeneration;
|
|
@@ -1999,7 +2028,7 @@ export class AgentSession {
|
|
|
1999
2028
|
|
|
2000
2029
|
// Check if we need to compact before sending (catches aborted responses)
|
|
2001
2030
|
const lastAssistant = this.#findLastAssistantMessage();
|
|
2002
|
-
if (lastAssistant) {
|
|
2031
|
+
if (lastAssistant && !options?.skipCompactionCheck) {
|
|
2003
2032
|
await this.#checkCompaction(lastAssistant, false);
|
|
2004
2033
|
}
|
|
2005
2034
|
|
|
@@ -2520,6 +2549,7 @@ export class AgentSession {
|
|
|
2520
2549
|
this.#pendingNextTurnMessages = [];
|
|
2521
2550
|
|
|
2522
2551
|
this.sessionManager.appendThinkingLevelChange(this.thinkingLevel);
|
|
2552
|
+
this.sessionManager.appendServiceTierChange(this.serviceTier ?? null);
|
|
2523
2553
|
|
|
2524
2554
|
this.#todoReminderCount = 0;
|
|
2525
2555
|
this.#planReferenceSent = false;
|
|
@@ -2629,7 +2659,7 @@ export class AgentSession {
|
|
|
2629
2659
|
this.settings.setModelRole(role, this.#formatRoleModelValue(role, model));
|
|
2630
2660
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
2631
2661
|
|
|
2632
|
-
// Re-
|
|
2662
|
+
// Re-apply the current thinking level for the newly selected model
|
|
2633
2663
|
this.setThinkingLevel(this.thinkingLevel);
|
|
2634
2664
|
}
|
|
2635
2665
|
|
|
@@ -2648,7 +2678,7 @@ export class AgentSession {
|
|
|
2648
2678
|
this.sessionManager.appendModelChange(`${model.provider}/${model.id}`, "temporary");
|
|
2649
2679
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
2650
2680
|
|
|
2651
|
-
// Re-
|
|
2681
|
+
// Re-apply the current thinking level for the newly selected model
|
|
2652
2682
|
this.setThinkingLevel(this.thinkingLevel);
|
|
2653
2683
|
}
|
|
2654
2684
|
|
|
@@ -2733,9 +2763,9 @@ export class AgentSession {
|
|
|
2733
2763
|
return { model: next.model, thinkingLevel: this.thinkingLevel, role: next.role };
|
|
2734
2764
|
}
|
|
2735
2765
|
|
|
2736
|
-
async #getScopedModelsWithApiKey(): Promise<Array<{ model: Model; thinkingLevel
|
|
2766
|
+
async #getScopedModelsWithApiKey(): Promise<Array<{ model: Model; thinkingLevel?: ThinkingLevel }>> {
|
|
2737
2767
|
const apiKeysByProvider = new Map<string, string | undefined>();
|
|
2738
|
-
const result: Array<{ model: Model; thinkingLevel
|
|
2768
|
+
const result: Array<{ model: Model; thinkingLevel?: ThinkingLevel }> = [];
|
|
2739
2769
|
|
|
2740
2770
|
for (const scoped of this.#scopedModels) {
|
|
2741
2771
|
const provider = scoped.model.provider;
|
|
@@ -2773,7 +2803,7 @@ export class AgentSession {
|
|
|
2773
2803
|
this.settings.setModelRole("default", this.#formatRoleModelValue("default", next.model));
|
|
2774
2804
|
this.settings.getStorage()?.recordModelUsage(`${next.model.provider}/${next.model.id}`);
|
|
2775
2805
|
|
|
2776
|
-
// Apply
|
|
2806
|
+
// Apply the scoped model's configured thinking level
|
|
2777
2807
|
this.setThinkingLevel(next.thinkingLevel);
|
|
2778
2808
|
|
|
2779
2809
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
@@ -2801,7 +2831,7 @@ export class AgentSession {
|
|
|
2801
2831
|
this.settings.setModelRole("default", this.#formatRoleModelValue("default", nextModel));
|
|
2802
2832
|
this.settings.getStorage()?.recordModelUsage(`${nextModel.provider}/${nextModel.id}`);
|
|
2803
2833
|
|
|
2804
|
-
// Re-
|
|
2834
|
+
// Re-apply the current thinking level for the newly selected model
|
|
2805
2835
|
this.setThinkingLevel(this.thinkingLevel);
|
|
2806
2836
|
|
|
2807
2837
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
@@ -2820,21 +2850,18 @@ export class AgentSession {
|
|
|
2820
2850
|
|
|
2821
2851
|
/**
|
|
2822
2852
|
* Set thinking level.
|
|
2823
|
-
*
|
|
2824
|
-
* Saves to session and settings only if the level actually changes.
|
|
2853
|
+
* Saves the effective metadata-clamped level to session and settings only if it changes.
|
|
2825
2854
|
*/
|
|
2826
|
-
setThinkingLevel(level: ThinkingLevel, persist: boolean = false): void {
|
|
2827
|
-
const
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
// Only persist if actually changing
|
|
2831
|
-
const isChanging = effectiveLevel !== this.agent.state.thinkingLevel;
|
|
2855
|
+
setThinkingLevel(level: ThinkingLevel | undefined, persist: boolean = false): void {
|
|
2856
|
+
const effectiveLevel = resolveThinkingLevelForModel(this.model, level);
|
|
2857
|
+
const isChanging = effectiveLevel !== this.#thinkingLevel;
|
|
2832
2858
|
|
|
2833
|
-
this
|
|
2859
|
+
this.#thinkingLevel = effectiveLevel;
|
|
2860
|
+
this.agent.setThinkingLevel(toReasoningEffort(effectiveLevel));
|
|
2834
2861
|
|
|
2835
2862
|
if (isChanging) {
|
|
2836
2863
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
2837
|
-
if (persist) {
|
|
2864
|
+
if (persist && effectiveLevel !== undefined && effectiveLevel !== ThinkingLevel.Off) {
|
|
2838
2865
|
this.settings.set("defaultThinkingLevel", effectiveLevel);
|
|
2839
2866
|
}
|
|
2840
2867
|
}
|
|
@@ -2844,57 +2871,48 @@ export class AgentSession {
|
|
|
2844
2871
|
* Cycle to next thinking level.
|
|
2845
2872
|
* @returns New level, or undefined if model doesn't support thinking
|
|
2846
2873
|
*/
|
|
2847
|
-
cycleThinkingLevel():
|
|
2848
|
-
if (!this.
|
|
2874
|
+
cycleThinkingLevel(): Effort | undefined {
|
|
2875
|
+
if (!this.model?.reasoning) return undefined;
|
|
2849
2876
|
|
|
2850
2877
|
const levels = this.getAvailableThinkingLevels();
|
|
2851
|
-
const currentIndex =
|
|
2878
|
+
const currentIndex =
|
|
2879
|
+
this.thinkingLevel && this.thinkingLevel !== ThinkingLevel.Off && this.thinkingLevel !== ThinkingLevel.Inherit
|
|
2880
|
+
? levels.indexOf(this.thinkingLevel)
|
|
2881
|
+
: -1;
|
|
2852
2882
|
const nextIndex = (currentIndex + 1) % levels.length;
|
|
2853
2883
|
const nextLevel = levels[nextIndex];
|
|
2884
|
+
if (!nextLevel) return undefined;
|
|
2854
2885
|
|
|
2855
2886
|
this.setThinkingLevel(nextLevel);
|
|
2856
2887
|
return nextLevel;
|
|
2857
2888
|
}
|
|
2858
2889
|
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
* The provider will clamp to what the specific model supports internally.
|
|
2862
|
-
*/
|
|
2863
|
-
getAvailableThinkingLevels(): ReadonlyArray<ThinkingLevel> {
|
|
2864
|
-
if (!this.supportsThinking()) return ["off"];
|
|
2865
|
-
return getAvailableThinkingLevels(this.supportsXhighThinking());
|
|
2890
|
+
isFastModeEnabled(): boolean {
|
|
2891
|
+
return this.serviceTier === "priority";
|
|
2866
2892
|
}
|
|
2867
2893
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
return this.model ? supportsXhigh(this.model) : false;
|
|
2894
|
+
setServiceTier(serviceTier: ServiceTier | undefined): void {
|
|
2895
|
+
if (this.serviceTier === serviceTier) return;
|
|
2896
|
+
this.agent.serviceTier = serviceTier;
|
|
2897
|
+
this.sessionManager.appendServiceTierChange(serviceTier ?? null);
|
|
2873
2898
|
}
|
|
2874
2899
|
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
*/
|
|
2878
|
-
supportsThinking(): boolean {
|
|
2879
|
-
return !!this.model?.reasoning;
|
|
2900
|
+
setFastMode(enabled: boolean): void {
|
|
2901
|
+
this.setServiceTier(enabled ? "priority" : undefined);
|
|
2880
2902
|
}
|
|
2881
2903
|
|
|
2882
|
-
|
|
2883
|
-
const
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
const candidate = ordered[i];
|
|
2895
|
-
if (available.has(candidate)) return candidate;
|
|
2896
|
-
}
|
|
2897
|
-
return availableLevels[0] ?? "off";
|
|
2904
|
+
toggleFastMode(): boolean {
|
|
2905
|
+
const enabled = !this.isFastModeEnabled();
|
|
2906
|
+
this.setFastMode(enabled);
|
|
2907
|
+
return enabled;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
/**
|
|
2911
|
+
* Get available thinking levels for current model.
|
|
2912
|
+
*/
|
|
2913
|
+
getAvailableThinkingLevels(): ReadonlyArray<Effort> {
|
|
2914
|
+
if (!this.model) return [];
|
|
2915
|
+
return getSupportedEfforts(this.model);
|
|
2898
2916
|
}
|
|
2899
2917
|
|
|
2900
2918
|
// =========================================================================
|
|
@@ -3042,13 +3060,14 @@ export class AgentSession {
|
|
|
3042
3060
|
apiKey,
|
|
3043
3061
|
customInstructions,
|
|
3044
3062
|
this.#compactionAbortController.signal,
|
|
3045
|
-
{ promptOverride: hookPrompt, extraContext: hookContext },
|
|
3063
|
+
{ promptOverride: hookPrompt, extraContext: hookContext, remoteInstructions: this.#baseSystemPrompt },
|
|
3046
3064
|
);
|
|
3047
3065
|
summary = result.summary;
|
|
3048
3066
|
shortSummary = result.shortSummary;
|
|
3049
3067
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
3050
3068
|
tokensBefore = result.tokensBefore;
|
|
3051
3069
|
details = result.details;
|
|
3070
|
+
preserveData = { ...(preserveData ?? {}), ...(result.preserveData ?? {}) };
|
|
3052
3071
|
}
|
|
3053
3072
|
|
|
3054
3073
|
if (this.#compactionAbortController.signal.aborted) {
|
|
@@ -3104,11 +3123,12 @@ export class AgentSession {
|
|
|
3104
3123
|
}
|
|
3105
3124
|
|
|
3106
3125
|
/**
|
|
3107
|
-
* Cancel in-progress
|
|
3126
|
+
* Cancel in-progress context maintenance (manual compaction, auto-compaction, or auto-handoff).
|
|
3108
3127
|
*/
|
|
3109
3128
|
abortCompaction(): void {
|
|
3110
3129
|
this.#compactionAbortController?.abort();
|
|
3111
3130
|
this.#autoCompactionAbortController?.abort();
|
|
3131
|
+
this.#handoffAbortController?.abort();
|
|
3112
3132
|
}
|
|
3113
3133
|
|
|
3114
3134
|
/**
|
|
@@ -3139,9 +3159,10 @@ export class AgentSession {
|
|
|
3139
3159
|
* waits for completion, then starts a fresh session with the handoff as context.
|
|
3140
3160
|
*
|
|
3141
3161
|
* @param customInstructions Optional focus for the handoff document
|
|
3162
|
+
* @param options Handoff execution options
|
|
3142
3163
|
* @returns The handoff document text, or undefined if cancelled/failed
|
|
3143
3164
|
*/
|
|
3144
|
-
async handoff(customInstructions?: string): Promise<HandoffResult | undefined> {
|
|
3165
|
+
async handoff(customInstructions?: string, options?: HandoffOptions): Promise<HandoffResult | undefined> {
|
|
3145
3166
|
const entries = this.sessionManager.getBranch();
|
|
3146
3167
|
const messageCount = entries.filter(e => e.type === "message").length;
|
|
3147
3168
|
|
|
@@ -3152,6 +3173,24 @@ export class AgentSession {
|
|
|
3152
3173
|
this.#skipPostTurnMaintenanceAssistantTimestamp = undefined;
|
|
3153
3174
|
|
|
3154
3175
|
this.#handoffAbortController = new AbortController();
|
|
3176
|
+
const handoffAbortController = this.#handoffAbortController;
|
|
3177
|
+
const handoffSignal = handoffAbortController.signal;
|
|
3178
|
+
const sourceSignal = options?.signal;
|
|
3179
|
+
const onHandoffAbort = () => {
|
|
3180
|
+
this.agent.abort();
|
|
3181
|
+
};
|
|
3182
|
+
handoffSignal.addEventListener("abort", onHandoffAbort, { once: true });
|
|
3183
|
+
const onSourceAbort = () => {
|
|
3184
|
+
if (!handoffSignal.aborted) {
|
|
3185
|
+
handoffAbortController.abort();
|
|
3186
|
+
}
|
|
3187
|
+
};
|
|
3188
|
+
if (sourceSignal) {
|
|
3189
|
+
sourceSignal.addEventListener("abort", onSourceAbort, { once: true });
|
|
3190
|
+
if (sourceSignal.aborted) {
|
|
3191
|
+
onSourceAbort();
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3155
3194
|
|
|
3156
3195
|
// Build the handoff prompt
|
|
3157
3196
|
let handoffPrompt = `Write a comprehensive handoff document that will allow another instance of yourself to seamlessly continue this work. The document should capture everything needed to resume without access to this conversation.
|
|
@@ -3192,42 +3231,58 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3192
3231
|
|
|
3193
3232
|
// Create a promise that resolves when the agent completes
|
|
3194
3233
|
let handoffText: string | undefined;
|
|
3195
|
-
const completionPromise =
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3234
|
+
const { promise: completionPromise, resolve: resolveCompletion } = Promise.withResolvers<void>();
|
|
3235
|
+
let handoffCancelled = false;
|
|
3236
|
+
let unsubscribe: (() => void) | undefined;
|
|
3237
|
+
const onCompletionAbort = () => {
|
|
3238
|
+
unsubscribe?.();
|
|
3239
|
+
handoffCancelled = true;
|
|
3240
|
+
resolveCompletion();
|
|
3241
|
+
};
|
|
3242
|
+
if (handoffSignal.aborted) {
|
|
3243
|
+
onCompletionAbort();
|
|
3244
|
+
} else {
|
|
3245
|
+
handoffSignal.addEventListener("abort", onCompletionAbort, { once: true });
|
|
3246
|
+
}
|
|
3247
|
+
unsubscribe = this.subscribe(event => {
|
|
3248
|
+
if (event.type === "agent_end") {
|
|
3249
|
+
unsubscribe?.();
|
|
3250
|
+
handoffSignal.removeEventListener("abort", onCompletionAbort);
|
|
3251
|
+
// Extract text from the last assistant message
|
|
3252
|
+
const messages = this.agent.state.messages;
|
|
3253
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3254
|
+
const msg = messages[i];
|
|
3255
|
+
if (msg.role === "assistant") {
|
|
3256
|
+
const content = (msg as AssistantMessage).content;
|
|
3257
|
+
const textParts = content
|
|
3258
|
+
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
3259
|
+
.map(c => c.text);
|
|
3260
|
+
if (textParts.length > 0) {
|
|
3261
|
+
handoffText = textParts.join("\n");
|
|
3262
|
+
break;
|
|
3218
3263
|
}
|
|
3219
3264
|
}
|
|
3220
|
-
resolve();
|
|
3221
3265
|
}
|
|
3222
|
-
|
|
3266
|
+
resolveCompletion();
|
|
3267
|
+
}
|
|
3223
3268
|
});
|
|
3224
3269
|
|
|
3225
3270
|
try {
|
|
3226
3271
|
// Send the prompt and wait for completion
|
|
3227
|
-
|
|
3272
|
+
if (handoffSignal.aborted) {
|
|
3273
|
+
throw new Error("Handoff cancelled");
|
|
3274
|
+
}
|
|
3275
|
+
await this.prompt(handoffPrompt, {
|
|
3276
|
+
expandPromptTemplates: false,
|
|
3277
|
+
synthetic: true,
|
|
3278
|
+
skipCompactionCheck: true,
|
|
3279
|
+
});
|
|
3228
3280
|
await completionPromise;
|
|
3229
3281
|
|
|
3230
|
-
if (
|
|
3282
|
+
if (handoffCancelled || handoffSignal.aborted) {
|
|
3283
|
+
throw new Error("Handoff cancelled");
|
|
3284
|
+
}
|
|
3285
|
+
if (!handoffText) {
|
|
3231
3286
|
return undefined;
|
|
3232
3287
|
}
|
|
3233
3288
|
|
|
@@ -3245,26 +3300,49 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3245
3300
|
// Inject the handoff document as a custom message
|
|
3246
3301
|
const handoffContent = `<handoff-context>\n${handoffText}\n</handoff-context>\n\nThe above is a handoff document from a previous session. Use this context to continue the work seamlessly.`;
|
|
3247
3302
|
this.sessionManager.appendCustomMessageEntry("handoff", handoffContent, true, undefined, "agent");
|
|
3303
|
+
let savedPath: string | undefined;
|
|
3304
|
+
if (options?.autoTriggered && this.settings.get("compaction.handoffSaveToDisk")) {
|
|
3305
|
+
const artifactsDir = this.sessionManager.getArtifactsDir();
|
|
3306
|
+
if (artifactsDir) {
|
|
3307
|
+
const fileTimestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
3308
|
+
const handoffFilePath = path.join(artifactsDir, `handoff-${fileTimestamp}.md`);
|
|
3309
|
+
try {
|
|
3310
|
+
await Bun.write(handoffFilePath, `${handoffText}\n`);
|
|
3311
|
+
savedPath = handoffFilePath;
|
|
3312
|
+
} catch (error) {
|
|
3313
|
+
logger.warn("Failed to save handoff document to disk", {
|
|
3314
|
+
path: handoffFilePath,
|
|
3315
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
} else {
|
|
3319
|
+
logger.debug("Skipping handoff document save because session is not persisted");
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3248
3322
|
|
|
3249
3323
|
// Rebuild agent messages from session
|
|
3250
3324
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
3251
3325
|
this.agent.replaceMessages(sessionContext.messages);
|
|
3252
3326
|
this.#syncTodoPhasesFromBranch();
|
|
3253
3327
|
|
|
3254
|
-
return { document: handoffText };
|
|
3328
|
+
return { document: handoffText, savedPath };
|
|
3255
3329
|
} finally {
|
|
3330
|
+
unsubscribe?.();
|
|
3331
|
+
handoffSignal.removeEventListener("abort", onCompletionAbort);
|
|
3332
|
+
handoffSignal.removeEventListener("abort", onHandoffAbort);
|
|
3333
|
+
sourceSignal?.removeEventListener("abort", onSourceAbort);
|
|
3256
3334
|
this.#handoffAbortController = undefined;
|
|
3257
3335
|
}
|
|
3258
3336
|
}
|
|
3259
3337
|
|
|
3260
3338
|
/**
|
|
3261
|
-
* Check if
|
|
3339
|
+
* Check if context maintenance or promotion is needed and run it.
|
|
3262
3340
|
* Called after agent_end and before prompt submission.
|
|
3263
3341
|
*
|
|
3264
3342
|
* Three cases (in order):
|
|
3265
|
-
* 1. Overflow + promotion: promote to larger model, retry without
|
|
3266
|
-
* 2. Overflow + no promotion target:
|
|
3267
|
-
* 3. Threshold: Context over threshold,
|
|
3343
|
+
* 1. Overflow + promotion: promote to larger model, retry without maintenance
|
|
3344
|
+
* 2. Overflow + no promotion target: run context maintenance, auto-retry on same model
|
|
3345
|
+
* 3. Threshold: Context over threshold, run context maintenance (no auto-retry)
|
|
3268
3346
|
*
|
|
3269
3347
|
* @param assistantMessage The assistant message to check
|
|
3270
3348
|
* @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
|
|
@@ -3305,13 +3383,13 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3305
3383
|
|
|
3306
3384
|
// No promotion target available fall through to compaction
|
|
3307
3385
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
3308
|
-
if (compactionSettings.enabled) {
|
|
3386
|
+
if (compactionSettings.enabled && compactionSettings.strategy !== "off") {
|
|
3309
3387
|
await this.#runAutoCompaction("overflow", true);
|
|
3310
3388
|
}
|
|
3311
3389
|
return;
|
|
3312
3390
|
}
|
|
3313
3391
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
3314
|
-
if (!compactionSettings.enabled) return;
|
|
3392
|
+
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return;
|
|
3315
3393
|
|
|
3316
3394
|
// Case 2: Threshold - turn succeeded but context is getting large
|
|
3317
3395
|
// Skip if this was an error (non-overflow errors don't have usage data)
|
|
@@ -3654,8 +3732,11 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3654
3732
|
async #runAutoCompaction(reason: "overflow" | "threshold", willRetry: boolean): Promise<void> {
|
|
3655
3733
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
3656
3734
|
|
|
3735
|
+
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return;
|
|
3657
3736
|
const generation = this.#promptGeneration;
|
|
3658
|
-
|
|
3737
|
+
let action: "context-full" | "handoff" =
|
|
3738
|
+
compactionSettings.strategy === "handoff" && reason !== "overflow" ? "handoff" : "context-full";
|
|
3739
|
+
await this.#emitSessionEvent({ type: "auto_compaction_start", reason, action });
|
|
3659
3740
|
// Properly abort and null existing controller before replacing
|
|
3660
3741
|
if (this.#autoCompactionAbortController) {
|
|
3661
3742
|
this.#autoCompactionAbortController.abort();
|
|
@@ -3663,9 +3744,45 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3663
3744
|
this.#autoCompactionAbortController = new AbortController();
|
|
3664
3745
|
|
|
3665
3746
|
try {
|
|
3747
|
+
if (compactionSettings.strategy === "handoff" && reason !== "overflow") {
|
|
3748
|
+
const handoffFocus = AUTO_HANDOFF_THRESHOLD_FOCUS;
|
|
3749
|
+
const handoffResult = await this.handoff(handoffFocus, {
|
|
3750
|
+
autoTriggered: true,
|
|
3751
|
+
signal: this.#autoCompactionAbortController.signal,
|
|
3752
|
+
});
|
|
3753
|
+
if (!handoffResult) {
|
|
3754
|
+
const aborted = this.#autoCompactionAbortController.signal.aborted;
|
|
3755
|
+
if (aborted) {
|
|
3756
|
+
await this.#emitSessionEvent({
|
|
3757
|
+
type: "auto_compaction_end",
|
|
3758
|
+
action,
|
|
3759
|
+
result: undefined,
|
|
3760
|
+
aborted: true,
|
|
3761
|
+
willRetry: false,
|
|
3762
|
+
});
|
|
3763
|
+
return;
|
|
3764
|
+
}
|
|
3765
|
+
logger.warn("Auto-handoff returned no document; falling back to context-full maintenance", {
|
|
3766
|
+
reason,
|
|
3767
|
+
});
|
|
3768
|
+
action = "context-full";
|
|
3769
|
+
}
|
|
3770
|
+
if (handoffResult) {
|
|
3771
|
+
await this.#emitSessionEvent({
|
|
3772
|
+
type: "auto_compaction_end",
|
|
3773
|
+
action,
|
|
3774
|
+
result: undefined,
|
|
3775
|
+
aborted: false,
|
|
3776
|
+
willRetry: false,
|
|
3777
|
+
});
|
|
3778
|
+
return;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3666
3782
|
if (!this.model) {
|
|
3667
3783
|
await this.#emitSessionEvent({
|
|
3668
3784
|
type: "auto_compaction_end",
|
|
3785
|
+
action,
|
|
3669
3786
|
result: undefined,
|
|
3670
3787
|
aborted: false,
|
|
3671
3788
|
willRetry: false,
|
|
@@ -3677,6 +3794,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3677
3794
|
if (availableModels.length === 0) {
|
|
3678
3795
|
await this.#emitSessionEvent({
|
|
3679
3796
|
type: "auto_compaction_end",
|
|
3797
|
+
action,
|
|
3680
3798
|
result: undefined,
|
|
3681
3799
|
aborted: false,
|
|
3682
3800
|
willRetry: false,
|
|
@@ -3690,10 +3808,18 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3690
3808
|
if (!preparation) {
|
|
3691
3809
|
await this.#emitSessionEvent({
|
|
3692
3810
|
type: "auto_compaction_end",
|
|
3811
|
+
action,
|
|
3693
3812
|
result: undefined,
|
|
3694
3813
|
aborted: false,
|
|
3695
3814
|
willRetry: false,
|
|
3696
3815
|
});
|
|
3816
|
+
if (!willRetry && this.agent.hasQueuedMessages()) {
|
|
3817
|
+
this.#scheduleAgentContinue({
|
|
3818
|
+
delayMs: 100,
|
|
3819
|
+
generation,
|
|
3820
|
+
shouldContinue: () => this.agent.hasQueuedMessages(),
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3697
3823
|
return;
|
|
3698
3824
|
}
|
|
3699
3825
|
|
|
@@ -3715,6 +3841,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3715
3841
|
if (hookResult?.cancel) {
|
|
3716
3842
|
await this.#emitSessionEvent({
|
|
3717
3843
|
type: "auto_compaction_end",
|
|
3844
|
+
action,
|
|
3718
3845
|
result: undefined,
|
|
3719
3846
|
aborted: true,
|
|
3720
3847
|
willRetry: false,
|
|
@@ -3774,7 +3901,11 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3774
3901
|
apiKey,
|
|
3775
3902
|
undefined,
|
|
3776
3903
|
this.#autoCompactionAbortController.signal,
|
|
3777
|
-
{
|
|
3904
|
+
{
|
|
3905
|
+
promptOverride: hookPrompt,
|
|
3906
|
+
extraContext: hookContext,
|
|
3907
|
+
remoteInstructions: this.#baseSystemPrompt,
|
|
3908
|
+
},
|
|
3778
3909
|
);
|
|
3779
3910
|
break;
|
|
3780
3911
|
} catch (error) {
|
|
@@ -3843,11 +3974,13 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3843
3974
|
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
3844
3975
|
tokensBefore = compactResult.tokensBefore;
|
|
3845
3976
|
details = compactResult.details;
|
|
3977
|
+
preserveData = { ...(preserveData ?? {}), ...(compactResult.preserveData ?? {}) };
|
|
3846
3978
|
}
|
|
3847
3979
|
|
|
3848
3980
|
if (this.#autoCompactionAbortController.signal.aborted) {
|
|
3849
3981
|
await this.#emitSessionEvent({
|
|
3850
3982
|
type: "auto_compaction_end",
|
|
3983
|
+
action,
|
|
3851
3984
|
result: undefined,
|
|
3852
3985
|
aborted: true,
|
|
3853
3986
|
willRetry: false,
|
|
@@ -3891,7 +4024,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3891
4024
|
details,
|
|
3892
4025
|
preserveData,
|
|
3893
4026
|
};
|
|
3894
|
-
await this.#emitSessionEvent({ type: "auto_compaction_end", result, aborted: false, willRetry });
|
|
4027
|
+
await this.#emitSessionEvent({ type: "auto_compaction_end", action, result, aborted: false, willRetry });
|
|
3895
4028
|
|
|
3896
4029
|
if (!willRetry && compactionSettings.autoContinue !== false) {
|
|
3897
4030
|
await this.#promptWithMessage(
|
|
@@ -3927,6 +4060,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3927
4060
|
if (this.#autoCompactionAbortController?.signal.aborted) {
|
|
3928
4061
|
await this.#emitSessionEvent({
|
|
3929
4062
|
type: "auto_compaction_end",
|
|
4063
|
+
action,
|
|
3930
4064
|
result: undefined,
|
|
3931
4065
|
aborted: true,
|
|
3932
4066
|
willRetry: false,
|
|
@@ -3936,6 +4070,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3936
4070
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
3937
4071
|
await this.#emitSessionEvent({
|
|
3938
4072
|
type: "auto_compaction_end",
|
|
4073
|
+
action,
|
|
3939
4074
|
result: undefined,
|
|
3940
4075
|
aborted: false,
|
|
3941
4076
|
willRetry: false,
|
|
@@ -3954,11 +4089,14 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3954
4089
|
*/
|
|
3955
4090
|
setAutoCompactionEnabled(enabled: boolean): void {
|
|
3956
4091
|
this.settings.set("compaction.enabled", enabled);
|
|
4092
|
+
if (enabled && this.settings.get("compaction.strategy") === "off") {
|
|
4093
|
+
this.settings.set("compaction.strategy", "context-full");
|
|
4094
|
+
}
|
|
3957
4095
|
}
|
|
3958
4096
|
|
|
3959
4097
|
/** Whether auto-compaction is enabled */
|
|
3960
4098
|
get autoCompactionEnabled(): boolean {
|
|
3961
|
-
return this.settings.get("compaction.enabled");
|
|
4099
|
+
return this.settings.get("compaction.enabled") && this.settings.get("compaction.strategy") !== "off";
|
|
3962
4100
|
}
|
|
3963
4101
|
|
|
3964
4102
|
// =========================================================================
|
|
@@ -4488,18 +4626,22 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
4488
4626
|
}
|
|
4489
4627
|
|
|
4490
4628
|
const hasThinkingEntry = this.sessionManager.getBranch().some(entry => entry.type === "thinking_level_change");
|
|
4491
|
-
const
|
|
4629
|
+
const hasServiceTierEntry = this.sessionManager.getBranch().some(entry => entry.type === "service_tier_change");
|
|
4630
|
+
const defaultThinkingLevel = this.settings.get("defaultThinkingLevel");
|
|
4492
4631
|
|
|
4493
4632
|
if (hasThinkingEntry) {
|
|
4494
|
-
|
|
4495
|
-
this.setThinkingLevel(sessionContext.thinkingLevel as ThinkingLevel);
|
|
4633
|
+
this.setThinkingLevel(sessionContext.thinkingLevel as ThinkingLevel | undefined);
|
|
4496
4634
|
} else {
|
|
4497
|
-
const
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4635
|
+
const effectiveDefaultThinkingLevel = resolveThinkingLevelForModel(this.model, defaultThinkingLevel);
|
|
4636
|
+
this.#thinkingLevel = effectiveDefaultThinkingLevel;
|
|
4637
|
+
this.agent.setThinkingLevel(toReasoningEffort(effectiveDefaultThinkingLevel));
|
|
4638
|
+
this.sessionManager.appendThinkingLevelChange(effectiveDefaultThinkingLevel);
|
|
4639
|
+
}
|
|
4640
|
+
|
|
4641
|
+
if (hasServiceTierEntry) {
|
|
4642
|
+
this.agent.serviceTier = sessionContext.serviceTier;
|
|
4643
|
+
} else {
|
|
4644
|
+
this.sessionManager.appendServiceTierChange(this.serviceTier ?? null);
|
|
4503
4645
|
}
|
|
4504
4646
|
|
|
4505
4647
|
this.#reconnectToAgent();
|
|
@@ -5016,7 +5158,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
5016
5158
|
|
|
5017
5159
|
// Include model and thinking level
|
|
5018
5160
|
const model = this.agent.state.model;
|
|
5019
|
-
const thinkingLevel = this
|
|
5161
|
+
const thinkingLevel = this.#thinkingLevel;
|
|
5020
5162
|
lines.push("## Configuration\n");
|
|
5021
5163
|
lines.push(`Model: ${model.provider}/${model.id}`);
|
|
5022
5164
|
lines.push(`Thinking Level: ${thinkingLevel}`);
|