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