@oh-my-pi/pi-coding-agent 15.7.1 → 15.7.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 +95 -6
- package/dist/types/auto-thinking/classifier.d.ts +35 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +112 -69
- package/dist/types/edit/hashline/diff.d.ts +9 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/model-selector.d.ts +3 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +7 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +15 -4
- package/dist/types/session/agent-session.d.ts +59 -23
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/thinking.d.ts +39 -1
- package/dist/types/tiny/device.d.ts +3 -3
- package/dist/types/tiny/models.d.ts +34 -1
- package/dist/types/tiny/title-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/src/auto-thinking/classifier.ts +180 -0
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +118 -71
- package/src/config/settings.ts +12 -0
- package/src/edit/hashline/diff.ts +87 -22
- package/src/edit/renderer.ts +16 -12
- package/src/edit/streaming.ts +17 -6
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +53 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/local-module-loader.ts +75 -10
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/main.ts +50 -56
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/acp/acp-agent.ts +13 -3
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +18 -12
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/model-selector.ts +20 -11
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +9 -2
- package/src/modes/components/settings-selector.ts +4 -1
- package/src/modes/components/status-line/segments.ts +13 -5
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +57 -0
- package/src/modes/controllers/event-controller.ts +65 -3
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +21 -6
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/theme/theme.ts +6 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
- package/src/prompts/system/auto-thinking-difficulty.md +12 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +86 -38
- package/src/session/agent-session.ts +558 -80
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +44 -0
- package/src/slash-commands/builtin-registry.ts +41 -16
- package/src/task/executor.ts +3 -3
- package/src/task/index.ts +6 -6
- package/src/thinking.ts +73 -1
- package/src/tiny/device.ts +4 -10
- package/src/tiny/models.ts +54 -2
- package/src/tiny/title-protocol.ts +11 -1
- package/src/tiny/worker.ts +19 -7
- package/src/tools/eval.ts +202 -26
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +480 -76
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
|
@@ -33,26 +33,34 @@ import {
|
|
|
33
33
|
ThinkingLevel,
|
|
34
34
|
} from "@oh-my-pi/pi-agent-core";
|
|
35
35
|
import {
|
|
36
|
+
AGGRESSIVE_SHAKE_CONFIG,
|
|
36
37
|
AUTO_HANDOFF_THRESHOLD_FOCUS,
|
|
38
|
+
applyShakeRegions,
|
|
37
39
|
CompactionCancelledError,
|
|
38
40
|
type CompactionPreparation,
|
|
39
41
|
type CompactionResult,
|
|
40
42
|
calculateContextTokens,
|
|
41
43
|
calculatePromptTokens,
|
|
42
44
|
collectEntriesForBranchSummary,
|
|
45
|
+
collectShakeRegions,
|
|
43
46
|
compact,
|
|
47
|
+
DEFAULT_SHAKE_CONFIG,
|
|
44
48
|
estimateTokens,
|
|
45
49
|
generateBranchSummary,
|
|
46
50
|
generateHandoff,
|
|
47
51
|
prepareCompaction,
|
|
52
|
+
type ShakeConfig,
|
|
53
|
+
type ShakeRegion,
|
|
54
|
+
type ShakeSummaryComplete,
|
|
55
|
+
type ShakeSummaryItem,
|
|
48
56
|
type SummaryOptions,
|
|
49
57
|
shouldCompact,
|
|
58
|
+
summarizeShakeRegions,
|
|
50
59
|
} from "@oh-my-pi/pi-agent-core/compaction";
|
|
51
60
|
import { DEFAULT_PRUNE_CONFIG, pruneToolOutputs } from "@oh-my-pi/pi-agent-core/compaction/pruning";
|
|
52
61
|
import type {
|
|
53
62
|
AssistantMessage,
|
|
54
63
|
Context,
|
|
55
|
-
Effort,
|
|
56
64
|
ImageContent,
|
|
57
65
|
Message,
|
|
58
66
|
MessageAttribution,
|
|
@@ -69,6 +77,7 @@ import type {
|
|
|
69
77
|
import {
|
|
70
78
|
calculateRateLimitBackoffMs,
|
|
71
79
|
clearAnthropicFastModeFallback,
|
|
80
|
+
Effort,
|
|
72
81
|
getSupportedEfforts,
|
|
73
82
|
isContextOverflow,
|
|
74
83
|
isUsageLimitError,
|
|
@@ -77,7 +86,7 @@ import {
|
|
|
77
86
|
resolveServiceTier,
|
|
78
87
|
streamSimple,
|
|
79
88
|
} from "@oh-my-pi/pi-ai";
|
|
80
|
-
import { MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
89
|
+
import { countTokens, MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
81
90
|
import {
|
|
82
91
|
extractRetryHint,
|
|
83
92
|
getAgentDbPath,
|
|
@@ -88,6 +97,7 @@ import {
|
|
|
88
97
|
Snowflake,
|
|
89
98
|
} from "@oh-my-pi/pi-utils";
|
|
90
99
|
import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
|
|
100
|
+
import { classifyDifficulty } from "../auto-thinking/classifier";
|
|
91
101
|
import { reset as resetCapabilities } from "../capability";
|
|
92
102
|
import type { Rule } from "../capability/rule";
|
|
93
103
|
import { MODEL_ROLE_IDS, type ModelRegistry } from "../config/model-registry";
|
|
@@ -147,10 +157,12 @@ import type { Goal, GoalModeState } from "../goals/state";
|
|
|
147
157
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
148
158
|
import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
149
159
|
import { resolveMemoryBackend } from "../memory-backend";
|
|
150
|
-
import {
|
|
160
|
+
import { getMnemopiSessionState, type MnemopiSessionState, setMnemopiSessionState } from "../mnemopi/state";
|
|
151
161
|
import { containsOrchestrate, ORCHESTRATE_NOTICE } from "../modes/orchestrate";
|
|
152
162
|
import { getCurrentThemeName, theme } from "../modes/theme/theme";
|
|
163
|
+
import { parseTurnBudget } from "../modes/turn-budget";
|
|
153
164
|
import { containsUltrathink, ULTRATHINK_NOTICE } from "../modes/ultrathink";
|
|
165
|
+
import { containsWorkflow, WORKFLOW_NOTICE } from "../modes/workflow";
|
|
154
166
|
import type { PlanModeState } from "../plan-mode/state";
|
|
155
167
|
import autoContinuePrompt from "../prompts/system/auto-continue.md" with { type: "text" };
|
|
156
168
|
import eagerTodoPrompt from "../prompts/system/eager-todo.md" with { type: "text" };
|
|
@@ -165,8 +177,16 @@ import ttsrToolReminderTemplate from "../prompts/system/ttsr-tool-reminder.md" w
|
|
|
165
177
|
import { type AgentRegistry, MAIN_AGENT_ID } from "../registry/agent-registry";
|
|
166
178
|
import { deobfuscateSessionContext, type SecretObfuscator } from "../secrets/obfuscator";
|
|
167
179
|
import { invalidateHostMetadata } from "../ssh/connection-manager";
|
|
168
|
-
import {
|
|
169
|
-
|
|
180
|
+
import {
|
|
181
|
+
AUTO_THINKING,
|
|
182
|
+
type ConfiguredThinkingLevel,
|
|
183
|
+
clampAutoThinkingEffort,
|
|
184
|
+
resolveProvisionalAutoLevel,
|
|
185
|
+
resolveThinkingLevelForModel,
|
|
186
|
+
toReasoningEffort,
|
|
187
|
+
} from "../thinking";
|
|
188
|
+
import { isTinyMemoryLocalModelKey } from "../tiny/models";
|
|
189
|
+
import { shutdownTinyTitleClient, tinyModelClient } from "../tiny/title-client";
|
|
170
190
|
import {
|
|
171
191
|
buildDiscoverableToolSearchIndex,
|
|
172
192
|
collectDiscoverableTools,
|
|
@@ -211,6 +231,7 @@ import type {
|
|
|
211
231
|
SessionManager,
|
|
212
232
|
} from "./session-manager";
|
|
213
233
|
import { getLatestCompactionEntry } from "./session-manager";
|
|
234
|
+
import type { ShakeMode, ShakeResult } from "./shake-types";
|
|
214
235
|
import { ToolChoiceQueue } from "./tool-choice-queue";
|
|
215
236
|
import { YieldQueue } from "./yield-queue";
|
|
216
237
|
|
|
@@ -220,11 +241,11 @@ export type AgentSessionEvent =
|
|
|
220
241
|
| {
|
|
221
242
|
type: "auto_compaction_start";
|
|
222
243
|
reason: "threshold" | "overflow" | "idle" | "incomplete";
|
|
223
|
-
action: "context-full" | "handoff";
|
|
244
|
+
action: "context-full" | "handoff" | "shake" | "shake-summary";
|
|
224
245
|
}
|
|
225
246
|
| {
|
|
226
247
|
type: "auto_compaction_end";
|
|
227
|
-
action: "context-full" | "handoff";
|
|
248
|
+
action: "context-full" | "handoff" | "shake" | "shake-summary";
|
|
228
249
|
result: CompactionResult | undefined;
|
|
229
250
|
aborted: boolean;
|
|
230
251
|
willRetry: boolean;
|
|
@@ -241,7 +262,14 @@ export type AgentSessionEvent =
|
|
|
241
262
|
| { type: "todo_auto_clear" }
|
|
242
263
|
| { type: "irc_message"; message: CustomMessage }
|
|
243
264
|
| { type: "notice"; level: "info" | "warning" | "error"; message: string; source?: string }
|
|
244
|
-
| {
|
|
265
|
+
| {
|
|
266
|
+
type: "thinking_level_changed";
|
|
267
|
+
thinkingLevel: ThinkingLevel | undefined;
|
|
268
|
+
/** The user-configured selector when it differs from the effective level (e.g. `auto`). */
|
|
269
|
+
configured?: ConfiguredThinkingLevel;
|
|
270
|
+
/** The level `auto` resolved to this turn, once classified. */
|
|
271
|
+
resolved?: Effort;
|
|
272
|
+
}
|
|
245
273
|
| { type: "goal_updated"; goal: Goal | null; state?: GoalModeState };
|
|
246
274
|
|
|
247
275
|
/** Listener function for agent session events */
|
|
@@ -254,6 +282,8 @@ export interface AsyncJobSnapshot {
|
|
|
254
282
|
delivery: AsyncJobDeliveryState;
|
|
255
283
|
}
|
|
256
284
|
|
|
285
|
+
export type { ShakeMode, ShakeResult };
|
|
286
|
+
|
|
257
287
|
// ============================================================================
|
|
258
288
|
// Types
|
|
259
289
|
// ============================================================================
|
|
@@ -265,7 +295,7 @@ export interface AgentSessionConfig {
|
|
|
265
295
|
/** Models to cycle through with Ctrl+P (from --models flag) */
|
|
266
296
|
scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
267
297
|
/** Initial session thinking selector. */
|
|
268
|
-
thinkingLevel?:
|
|
298
|
+
thinkingLevel?: ConfiguredThinkingLevel;
|
|
269
299
|
/** Prompt templates for expansion */
|
|
270
300
|
promptTemplates?: PromptTemplate[];
|
|
271
301
|
/** File-based slash commands for expansion */
|
|
@@ -445,8 +475,8 @@ interface RetryFallbackSelector {
|
|
|
445
475
|
interface ActiveRetryFallbackState {
|
|
446
476
|
role: string;
|
|
447
477
|
originalSelector: string;
|
|
448
|
-
originalThinkingLevel:
|
|
449
|
-
lastAppliedFallbackThinkingLevel:
|
|
478
|
+
originalThinkingLevel: ConfiguredThinkingLevel | undefined;
|
|
479
|
+
lastAppliedFallbackThinkingLevel: ConfiguredThinkingLevel | undefined;
|
|
450
480
|
}
|
|
451
481
|
|
|
452
482
|
function parseRetryFallbackSelector(selector: string): RetryFallbackSelector | undefined {
|
|
@@ -782,7 +812,12 @@ export class AgentSession {
|
|
|
782
812
|
readonly configWarnings: string[] = [];
|
|
783
813
|
|
|
784
814
|
#scopedModels: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
815
|
+
/** Effective, metadata-clamped thinking level applied to the agent (never `auto`). */
|
|
785
816
|
#thinkingLevel: ThinkingLevel | undefined;
|
|
817
|
+
/** True when the user configured `auto`; the effective level is resolved per turn. */
|
|
818
|
+
#autoThinking: boolean = false;
|
|
819
|
+
/** The level `auto` last resolved to (for UI); undefined until a turn is classified. */
|
|
820
|
+
#autoResolvedLevel: Effort | undefined;
|
|
786
821
|
#promptTemplates: PromptTemplate[];
|
|
787
822
|
#slashCommands: FileSlashCommand[];
|
|
788
823
|
|
|
@@ -1041,7 +1076,15 @@ export class AgentSession {
|
|
|
1041
1076
|
this.#parentEvalSessionId = config.parentEvalSessionId;
|
|
1042
1077
|
this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
|
|
1043
1078
|
this.#scopedModels = config.scopedModels ?? [];
|
|
1044
|
-
|
|
1079
|
+
if (config.thinkingLevel === AUTO_THINKING) {
|
|
1080
|
+
// `auto` is session-level: keep the flag and show a provisional concrete
|
|
1081
|
+
// level (the agent's initial effort was already set by the caller) until
|
|
1082
|
+
// the first user turn is classified.
|
|
1083
|
+
this.#autoThinking = true;
|
|
1084
|
+
this.#thinkingLevel = resolveProvisionalAutoLevel(this.model);
|
|
1085
|
+
} else {
|
|
1086
|
+
this.#thinkingLevel = config.thinkingLevel;
|
|
1087
|
+
}
|
|
1045
1088
|
this.#promptTemplates = config.promptTemplates ?? [];
|
|
1046
1089
|
this.#slashCommands = config.slashCommands ?? [];
|
|
1047
1090
|
this.#extensionRunner = config.extensionRunner;
|
|
@@ -1259,8 +1302,8 @@ export class AgentSession {
|
|
|
1259
1302
|
return previous;
|
|
1260
1303
|
}
|
|
1261
1304
|
|
|
1262
|
-
|
|
1263
|
-
return
|
|
1305
|
+
getMnemopiSessionState(): MnemopiSessionState | undefined {
|
|
1306
|
+
return getMnemopiSessionState(this);
|
|
1264
1307
|
}
|
|
1265
1308
|
|
|
1266
1309
|
/** TTSR manager for time-traveling stream rules */
|
|
@@ -2810,11 +2853,11 @@ export class AgentSession {
|
|
|
2810
2853
|
this.getHindsightSessionState()?.setSessionId(sid);
|
|
2811
2854
|
}
|
|
2812
2855
|
|
|
2813
|
-
#
|
|
2814
|
-
if (resolveMemoryBackend(this.settings).id !== "
|
|
2856
|
+
#rekeyMnemopiMemoryForCurrentSessionId(): void {
|
|
2857
|
+
if (resolveMemoryBackend(this.settings).id !== "mnemopi") return;
|
|
2815
2858
|
const sid = this.agent.sessionId;
|
|
2816
2859
|
if (!sid) return;
|
|
2817
|
-
this.
|
|
2860
|
+
this.getMnemopiSessionState()?.setSessionId(sid);
|
|
2818
2861
|
}
|
|
2819
2862
|
|
|
2820
2863
|
/** New session file: reset auto-recall / retain-threshold counters for the new transcript. */
|
|
@@ -2825,9 +2868,9 @@ export class AgentSession {
|
|
|
2825
2868
|
state.resetConversationTracking();
|
|
2826
2869
|
}
|
|
2827
2870
|
|
|
2828
|
-
#
|
|
2829
|
-
if (resolveMemoryBackend(this.settings).id !== "
|
|
2830
|
-
const state = this.
|
|
2871
|
+
#resetMnemopiConversationTrackingIfMnemopi(): void {
|
|
2872
|
+
if (resolveMemoryBackend(this.settings).id !== "mnemopi") return;
|
|
2873
|
+
const state = this.getMnemopiSessionState();
|
|
2831
2874
|
if (!state || state.aliasOf) return;
|
|
2832
2875
|
state.resetConversationTracking();
|
|
2833
2876
|
}
|
|
@@ -2895,8 +2938,8 @@ export class AgentSession {
|
|
|
2895
2938
|
const hindsightState = this.setHindsightSessionState(undefined);
|
|
2896
2939
|
await hindsightState?.flushRetainQueue();
|
|
2897
2940
|
hindsightState?.dispose();
|
|
2898
|
-
const
|
|
2899
|
-
|
|
2941
|
+
const mnemopiState = setMnemopiSessionState(this, undefined);
|
|
2942
|
+
mnemopiState?.dispose();
|
|
2900
2943
|
this.#disconnectFromAgent();
|
|
2901
2944
|
if (this.#unsubscribeAppendOnly) {
|
|
2902
2945
|
this.#unsubscribeAppendOnly();
|
|
@@ -2935,11 +2978,26 @@ export class AgentSession {
|
|
|
2935
2978
|
return this.agent.state.model;
|
|
2936
2979
|
}
|
|
2937
2980
|
|
|
2938
|
-
/**
|
|
2981
|
+
/** Effective thinking level applied to the agent (the resolved level when `auto`). */
|
|
2939
2982
|
get thinkingLevel(): ThinkingLevel | undefined {
|
|
2940
2983
|
return this.#thinkingLevel;
|
|
2941
2984
|
}
|
|
2942
2985
|
|
|
2986
|
+
/** The selector the user configured: `auto` when auto mode is active, else the effective level. */
|
|
2987
|
+
configuredThinkingLevel(): ConfiguredThinkingLevel | undefined {
|
|
2988
|
+
return this.#autoThinking ? AUTO_THINKING : this.#thinkingLevel;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/** True when `auto` thinking mode is active. */
|
|
2992
|
+
get isAutoThinking(): boolean {
|
|
2993
|
+
return this.#autoThinking;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
/** The level `auto` resolved to for the current turn (undefined until classified). */
|
|
2997
|
+
autoResolvedThinkingLevel(): Effort | undefined {
|
|
2998
|
+
return this.#autoResolvedLevel;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
2943
3001
|
get serviceTier(): ServiceTier | undefined {
|
|
2944
3002
|
return this.agent.serviceTier;
|
|
2945
3003
|
}
|
|
@@ -4058,6 +4116,8 @@ export class AgentSession {
|
|
|
4058
4116
|
const keywordNotices: CustomMessage[] = [];
|
|
4059
4117
|
if (!options?.synthetic) {
|
|
4060
4118
|
const timestamp = Date.now();
|
|
4119
|
+
const turnBudget = parseTurnBudget(expandedText);
|
|
4120
|
+
this.sessionManager.beginTurnBudget(turnBudget?.total ?? null, turnBudget?.hard ?? false);
|
|
4061
4121
|
if (containsUltrathink(expandedText)) {
|
|
4062
4122
|
keywordNotices.push({
|
|
4063
4123
|
role: "custom",
|
|
@@ -4078,6 +4138,16 @@ export class AgentSession {
|
|
|
4078
4138
|
timestamp,
|
|
4079
4139
|
});
|
|
4080
4140
|
}
|
|
4141
|
+
if (containsWorkflow(expandedText)) {
|
|
4142
|
+
keywordNotices.push({
|
|
4143
|
+
role: "custom",
|
|
4144
|
+
customType: "workflow-notice",
|
|
4145
|
+
content: WORKFLOW_NOTICE,
|
|
4146
|
+
display: false,
|
|
4147
|
+
attribution: "user",
|
|
4148
|
+
timestamp,
|
|
4149
|
+
});
|
|
4150
|
+
}
|
|
4081
4151
|
}
|
|
4082
4152
|
|
|
4083
4153
|
// If streaming, queue via steer() or followUp() based on option
|
|
@@ -4304,6 +4374,17 @@ export class AgentSession {
|
|
|
4304
4374
|
return;
|
|
4305
4375
|
}
|
|
4306
4376
|
|
|
4377
|
+
// Auto thinking: classify this real user turn and set the effective level
|
|
4378
|
+
// before the model request. Synthetic/tool-continuation turns (developer/
|
|
4379
|
+
// custom roles) and non-auto sessions are skipped. Never blocks the turn —
|
|
4380
|
+
// failures fall back to a concrete level inside the helper.
|
|
4381
|
+
if (this.#autoThinking && message.role === "user") {
|
|
4382
|
+
await this.#applyAutoThinkingLevel(expandedText, generation);
|
|
4383
|
+
if (this.#promptGeneration !== generation) {
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4307
4388
|
const agentPromptOptions = options?.toolChoice ? { toolChoice: options.toolChoice } : undefined;
|
|
4308
4389
|
await this.#promptAgentWithIdleRetry(messages, agentPromptOptions);
|
|
4309
4390
|
if (!options?.skipPostPromptRecoveryWait) {
|
|
@@ -4919,9 +5000,9 @@ export class AgentSession {
|
|
|
4919
5000
|
this.setTodoPhases([]);
|
|
4920
5001
|
this.#syncAgentSessionId();
|
|
4921
5002
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
4922
|
-
this.#
|
|
5003
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
4923
5004
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
4924
|
-
this.#
|
|
5005
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
4925
5006
|
this.#steeringMessages = [];
|
|
4926
5007
|
this.#followUpMessages = [];
|
|
4927
5008
|
this.#pendingNextTurnMessages = [];
|
|
@@ -5016,8 +5097,8 @@ export class AgentSession {
|
|
|
5016
5097
|
// Update agent session ID
|
|
5017
5098
|
this.#syncAgentSessionId();
|
|
5018
5099
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
5019
|
-
this.#
|
|
5020
|
-
this.#
|
|
5100
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
5101
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
5021
5102
|
|
|
5022
5103
|
// Emit session_switch event with reason "fork" to hooks
|
|
5023
5104
|
if (this.#extensionRunner) {
|
|
@@ -5037,13 +5118,13 @@ export class AgentSession {
|
|
|
5037
5118
|
|
|
5038
5119
|
/**
|
|
5039
5120
|
* Set model directly.
|
|
5040
|
-
* Validates API key
|
|
5121
|
+
* Validates API key and saves to the active session. Persists settings only when requested.
|
|
5041
5122
|
* @throws Error if no API key available for the model
|
|
5042
5123
|
*/
|
|
5043
5124
|
async setModel(
|
|
5044
5125
|
model: Model,
|
|
5045
5126
|
role: string = "default",
|
|
5046
|
-
options?: { selector?: string; thinkingLevel?: ThinkingLevel },
|
|
5127
|
+
options?: { selector?: string; thinkingLevel?: ThinkingLevel; persist?: boolean },
|
|
5047
5128
|
): Promise<void> {
|
|
5048
5129
|
const previousEditMode = this.#resolveActiveEditMode();
|
|
5049
5130
|
const apiKey = await this.#modelRegistry.getApiKey(model, this.sessionId);
|
|
@@ -5054,15 +5135,17 @@ export class AgentSession {
|
|
|
5054
5135
|
this.#clearActiveRetryFallback();
|
|
5055
5136
|
this.#setModelWithProviderSessionReset(model);
|
|
5056
5137
|
this.sessionManager.appendModelChange(`${model.provider}/${model.id}`, role);
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5138
|
+
if (options?.persist) {
|
|
5139
|
+
this.settings.setModelRole(
|
|
5140
|
+
role,
|
|
5141
|
+
this.#formatRoleModelValue(role, model, options.selector, options.thinkingLevel),
|
|
5142
|
+
);
|
|
5143
|
+
}
|
|
5061
5144
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
5062
5145
|
|
|
5063
5146
|
// Re-apply thinking for the newly selected model. Prefer the model's
|
|
5064
|
-
// configured defaultLevel; otherwise preserve the current level.
|
|
5065
|
-
this
|
|
5147
|
+
// configured defaultLevel; otherwise preserve the current level (or auto).
|
|
5148
|
+
this.#reapplyThinkingLevel(model.thinking?.defaultLevel);
|
|
5066
5149
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
5067
5150
|
}
|
|
5068
5151
|
|
|
@@ -5084,8 +5167,12 @@ export class AgentSession {
|
|
|
5084
5167
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
5085
5168
|
|
|
5086
5169
|
// Apply explicit thinking level if given; otherwise prefer the model's
|
|
5087
|
-
// configured defaultLevel; otherwise re-clamp the current level.
|
|
5088
|
-
|
|
5170
|
+
// configured defaultLevel; otherwise re-clamp the current level (or auto).
|
|
5171
|
+
if (thinkingLevel !== undefined) {
|
|
5172
|
+
this.setThinkingLevel(thinkingLevel);
|
|
5173
|
+
} else {
|
|
5174
|
+
this.#reapplyThinkingLevel(model.thinking?.defaultLevel);
|
|
5175
|
+
}
|
|
5089
5176
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
5090
5177
|
}
|
|
5091
5178
|
|
|
@@ -5156,9 +5243,8 @@ export class AgentSession {
|
|
|
5156
5243
|
}
|
|
5157
5244
|
|
|
5158
5245
|
/**
|
|
5159
|
-
* Apply a resolved role model as the active model
|
|
5160
|
-
* settings
|
|
5161
|
-
* {@link cycleRoleModels} and is shared with the plan-approval model slider.
|
|
5246
|
+
* Apply a resolved role model as the active model without changing global
|
|
5247
|
+
* settings. Shared with role cycling and the plan-approval model slider.
|
|
5162
5248
|
*/
|
|
5163
5249
|
async applyRoleModel(entry: ResolvedRoleModel): Promise<void> {
|
|
5164
5250
|
await this.setModel(entry.model, entry.role);
|
|
@@ -5169,24 +5255,21 @@ export class AgentSession {
|
|
|
5169
5255
|
|
|
5170
5256
|
/**
|
|
5171
5257
|
* Cycle through configured role models in a fixed order.
|
|
5172
|
-
* Skips missing roles.
|
|
5258
|
+
* Skips missing roles and changes only the active session model.
|
|
5173
5259
|
* @param roleOrder - Order of roles to cycle through (e.g., ["slow", "default", "smol"])
|
|
5174
|
-
* @param
|
|
5260
|
+
* @param direction - "forward" (default) or "backward"
|
|
5175
5261
|
*/
|
|
5176
5262
|
async cycleRoleModels(
|
|
5177
5263
|
roleOrder: readonly string[],
|
|
5178
|
-
|
|
5264
|
+
direction: "forward" | "backward" = "forward",
|
|
5179
5265
|
): Promise<RoleModelCycleResult | undefined> {
|
|
5180
5266
|
const cycle = this.getRoleModelCycle(roleOrder);
|
|
5181
5267
|
if (!cycle || cycle.models.length <= 1) return undefined;
|
|
5182
5268
|
|
|
5183
|
-
const
|
|
5269
|
+
const step = direction === "backward" ? -1 : 1;
|
|
5270
|
+
const next = cycle.models[(cycle.currentIndex + step + cycle.models.length) % cycle.models.length];
|
|
5184
5271
|
|
|
5185
|
-
|
|
5186
|
-
await this.setModelTemporary(next.model, next.explicitThinkingLevel ? next.thinkingLevel : undefined);
|
|
5187
|
-
} else {
|
|
5188
|
-
await this.applyRoleModel(next);
|
|
5189
|
-
}
|
|
5272
|
+
await this.applyRoleModel(next);
|
|
5190
5273
|
|
|
5191
5274
|
return { model: next.model, thinkingLevel: this.thinkingLevel, role: next.role };
|
|
5192
5275
|
}
|
|
@@ -5230,11 +5313,10 @@ export class AgentSession {
|
|
|
5230
5313
|
this.#clearActiveRetryFallback();
|
|
5231
5314
|
this.#setModelWithProviderSessionReset(next.model);
|
|
5232
5315
|
this.sessionManager.appendModelChange(`${next.model.provider}/${next.model.id}`);
|
|
5233
|
-
this.settings.setModelRole("default", this.#formatRoleModelValue("default", next.model));
|
|
5234
5316
|
this.settings.getStorage()?.recordModelUsage(`${next.model.provider}/${next.model.id}`);
|
|
5235
5317
|
|
|
5236
|
-
// Apply the scoped model's configured thinking level
|
|
5237
|
-
this.setThinkingLevel(next.thinkingLevel);
|
|
5318
|
+
// Apply the scoped model's configured thinking level, preserving auto.
|
|
5319
|
+
this.setThinkingLevel(this.#autoThinking ? AUTO_THINKING : next.thinkingLevel);
|
|
5238
5320
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
5239
5321
|
|
|
5240
5322
|
return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
|
|
@@ -5261,10 +5343,9 @@ export class AgentSession {
|
|
|
5261
5343
|
this.#clearActiveRetryFallback();
|
|
5262
5344
|
this.#setModelWithProviderSessionReset(nextModel);
|
|
5263
5345
|
this.sessionManager.appendModelChange(`${nextModel.provider}/${nextModel.id}`);
|
|
5264
|
-
this.settings.setModelRole("default", this.#formatRoleModelValue("default", nextModel));
|
|
5265
5346
|
this.settings.getStorage()?.recordModelUsage(`${nextModel.provider}/${nextModel.id}`);
|
|
5266
|
-
// Re-apply the current thinking level for the newly selected model
|
|
5267
|
-
this
|
|
5347
|
+
// Re-apply the current thinking level (or auto) for the newly selected model
|
|
5348
|
+
this.#reapplyThinkingLevel();
|
|
5268
5349
|
await this.#syncEditToolModeAfterModelChange(previousEditMode);
|
|
5269
5350
|
|
|
5270
5351
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
@@ -5282,10 +5363,30 @@ export class AgentSession {
|
|
|
5282
5363
|
// =========================================================================
|
|
5283
5364
|
|
|
5284
5365
|
/**
|
|
5285
|
-
* Set thinking level.
|
|
5286
|
-
*
|
|
5366
|
+
* Set the thinking level. `auto` enables per-turn classification; the selector
|
|
5367
|
+
* itself is never written to the session log, but resolved concrete levels are
|
|
5368
|
+
* persisted when real user turns are classified so resumed sessions keep the
|
|
5369
|
+
* last resolved effort instead of reverting to pending auto.
|
|
5287
5370
|
*/
|
|
5288
|
-
setThinkingLevel(level:
|
|
5371
|
+
setThinkingLevel(level: ConfiguredThinkingLevel | undefined, persist: boolean = false): void {
|
|
5372
|
+
if (level === AUTO_THINKING) {
|
|
5373
|
+
const provisional = resolveProvisionalAutoLevel(this.model);
|
|
5374
|
+
const wasAuto = this.#autoThinking;
|
|
5375
|
+
this.#autoThinking = true;
|
|
5376
|
+
this.#autoResolvedLevel = undefined;
|
|
5377
|
+
this.#thinkingLevel = provisional;
|
|
5378
|
+
this.agent.setThinkingLevel(toReasoningEffort(provisional));
|
|
5379
|
+
if (persist) {
|
|
5380
|
+
this.settings.set("defaultThinkingLevel", AUTO_THINKING);
|
|
5381
|
+
}
|
|
5382
|
+
if (!wasAuto || this.#thinkingLevel !== provisional) {
|
|
5383
|
+
this.#emit({ type: "thinking_level_changed", thinkingLevel: provisional, configured: AUTO_THINKING });
|
|
5384
|
+
}
|
|
5385
|
+
return;
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
this.#autoThinking = false;
|
|
5389
|
+
this.#autoResolvedLevel = undefined;
|
|
5289
5390
|
const effectiveLevel = resolveThinkingLevelForModel(this.model, level);
|
|
5290
5391
|
const isChanging = effectiveLevel !== this.#thinkingLevel;
|
|
5291
5392
|
|
|
@@ -5302,14 +5403,28 @@ export class AgentSession {
|
|
|
5302
5403
|
}
|
|
5303
5404
|
|
|
5304
5405
|
/**
|
|
5305
|
-
*
|
|
5306
|
-
*
|
|
5406
|
+
* Re-apply the active thinking selection after a model change. Preserves `auto`
|
|
5407
|
+
* (re-clamping the provisional level to the new model); otherwise re-applies the
|
|
5408
|
+
* preferred default or the current effective level.
|
|
5409
|
+
*/
|
|
5410
|
+
#reapplyThinkingLevel(preferredDefault?: ThinkingLevel): void {
|
|
5411
|
+
this.setThinkingLevel(this.#autoThinking ? AUTO_THINKING : (preferredDefault ?? this.#thinkingLevel));
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
/**
|
|
5415
|
+
* Cycle to next thinking level: off → auto → minimal..xhigh → off.
|
|
5416
|
+
* @returns New selector, or undefined if model doesn't support thinking
|
|
5307
5417
|
*/
|
|
5308
|
-
cycleThinkingLevel():
|
|
5418
|
+
cycleThinkingLevel(): ConfiguredThinkingLevel | undefined {
|
|
5309
5419
|
if (!this.model?.reasoning) return undefined;
|
|
5310
5420
|
|
|
5311
|
-
const levels = [
|
|
5312
|
-
|
|
5421
|
+
const levels: ConfiguredThinkingLevel[] = [
|
|
5422
|
+
ThinkingLevel.Off,
|
|
5423
|
+
AUTO_THINKING,
|
|
5424
|
+
...this.getAvailableThinkingLevels(),
|
|
5425
|
+
];
|
|
5426
|
+
const configured = this.configuredThinkingLevel();
|
|
5427
|
+
const currentLevel = configured === ThinkingLevel.Inherit ? ThinkingLevel.Off : configured;
|
|
5313
5428
|
const currentIndex = currentLevel ? levels.indexOf(currentLevel) : -1;
|
|
5314
5429
|
const nextIndex = (currentIndex + 1) % levels.length;
|
|
5315
5430
|
const nextLevel = levels[nextIndex];
|
|
@@ -5319,6 +5434,65 @@ export class AgentSession {
|
|
|
5319
5434
|
return nextLevel;
|
|
5320
5435
|
}
|
|
5321
5436
|
|
|
5437
|
+
/** Timeout (ms) for per-turn auto-thinking classification before falling back. */
|
|
5438
|
+
static readonly #AUTO_THINKING_TIMEOUT_MS = 4000;
|
|
5439
|
+
|
|
5440
|
+
/**
|
|
5441
|
+
* Classify the current user turn and set the effective thinking level for it.
|
|
5442
|
+
* Bounded by a timeout + abort; on any failure (no smol model, timeout, parse
|
|
5443
|
+
* error) it falls back to the provisional concrete level and continues. Never
|
|
5444
|
+
* throws into the turn, and never clears `#autoThinking` (auto stays active).
|
|
5445
|
+
*/
|
|
5446
|
+
async #applyAutoThinkingLevel(promptText: string, generation: number): Promise<void> {
|
|
5447
|
+
const model = this.model;
|
|
5448
|
+
if (!model?.reasoning) return;
|
|
5449
|
+
|
|
5450
|
+
let resolved: Effort | undefined;
|
|
5451
|
+
if (containsUltrathink(promptText)) {
|
|
5452
|
+
// The user explicitly asked for maximum thinking; bypass the classifier
|
|
5453
|
+
// and jump straight to the highest auto-supported level for this model.
|
|
5454
|
+
resolved = clampAutoThinkingEffort(model, Effort.XHigh);
|
|
5455
|
+
} else {
|
|
5456
|
+
const controller = new AbortController();
|
|
5457
|
+
const timer = setTimeout(() => controller.abort(), AgentSession.#AUTO_THINKING_TIMEOUT_MS);
|
|
5458
|
+
try {
|
|
5459
|
+
resolved = await classifyDifficulty(promptText, {
|
|
5460
|
+
settings: this.settings,
|
|
5461
|
+
registry: this.#modelRegistry,
|
|
5462
|
+
model,
|
|
5463
|
+
sessionId: this.sessionId,
|
|
5464
|
+
signal: controller.signal,
|
|
5465
|
+
metadataResolver: provider => this.agent.metadataForProvider(provider),
|
|
5466
|
+
});
|
|
5467
|
+
} catch (error) {
|
|
5468
|
+
logger.debug("auto-thinking: classification failed; using fallback level", {
|
|
5469
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5470
|
+
});
|
|
5471
|
+
} finally {
|
|
5472
|
+
clearTimeout(timer);
|
|
5473
|
+
}
|
|
5474
|
+
}
|
|
5475
|
+
|
|
5476
|
+
// Drop the result if the turn was aborted/superseded while classifying.
|
|
5477
|
+
if (this.#promptGeneration !== generation || !this.#autoThinking) return;
|
|
5478
|
+
|
|
5479
|
+
const effort = resolved ?? resolveProvisionalAutoLevel(model);
|
|
5480
|
+
if (effort === undefined) return;
|
|
5481
|
+
const shouldPersistResolution = this.#autoResolvedLevel !== effort;
|
|
5482
|
+
this.#autoResolvedLevel = effort;
|
|
5483
|
+
this.#thinkingLevel = effort;
|
|
5484
|
+
this.agent.setThinkingLevel(toReasoningEffort(effort));
|
|
5485
|
+
if (shouldPersistResolution) {
|
|
5486
|
+
this.sessionManager.appendThinkingLevelChange(effort);
|
|
5487
|
+
}
|
|
5488
|
+
this.#emit({
|
|
5489
|
+
type: "thinking_level_changed",
|
|
5490
|
+
thinkingLevel: effort,
|
|
5491
|
+
configured: AUTO_THINKING,
|
|
5492
|
+
resolved: effort,
|
|
5493
|
+
});
|
|
5494
|
+
}
|
|
5495
|
+
|
|
5322
5496
|
/**
|
|
5323
5497
|
* True when *any* fast-mode-granting service tier is configured, regardless
|
|
5324
5498
|
* of whether the active model's provider actually realizes it. Used by the
|
|
@@ -5479,6 +5653,165 @@ export class AgentSession {
|
|
|
5479
5653
|
return { removed };
|
|
5480
5654
|
}
|
|
5481
5655
|
|
|
5656
|
+
/**
|
|
5657
|
+
* Surgically reduce context by dropping heavy content ("shake").
|
|
5658
|
+
*
|
|
5659
|
+
* - `images` delegates to {@link dropImages}.
|
|
5660
|
+
* - `elide` replaces whole tool-call results and large fenced/XML blocks
|
|
5661
|
+
* with short placeholders that embed an `artifact://` recovery link.
|
|
5662
|
+
* - `summary` extractively compresses the same regions with the configured
|
|
5663
|
+
* local on-device model (`providers.shakeSummaryModel`), falling back to
|
|
5664
|
+
* the elide placeholder per region (or wholesale when the local model is
|
|
5665
|
+
* unavailable). Never calls a remote/cloud LLM.
|
|
5666
|
+
*
|
|
5667
|
+
* Mutates the branch in place, persists via `rewriteEntries`, replays the
|
|
5668
|
+
* rebuilt context through the agent, and tears down provider sessions that
|
|
5669
|
+
* cache message identity — same rewrite contract as {@link dropImages}.
|
|
5670
|
+
*
|
|
5671
|
+
* No-op (zero counts) when nothing is eligible.
|
|
5672
|
+
*/
|
|
5673
|
+
async shake(mode: ShakeMode, opts: { config?: ShakeConfig; signal?: AbortSignal } = {}): Promise<ShakeResult> {
|
|
5674
|
+
if (mode === "images") {
|
|
5675
|
+
const { removed } = await this.dropImages();
|
|
5676
|
+
return { mode, toolResultsDropped: 0, blocksDropped: 0, imagesDropped: removed, tokensFreed: 0 };
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
const config = opts.config ?? AGGRESSIVE_SHAKE_CONFIG;
|
|
5680
|
+
const regions = collectShakeRegions(this.sessionManager.getBranch(), config);
|
|
5681
|
+
if (regions.length === 0) {
|
|
5682
|
+
return { mode, toolResultsDropped: 0, blocksDropped: 0, tokensFreed: 0 };
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
const artifactId = await this.#saveShakeArtifact(regions);
|
|
5686
|
+
let replacements: string[];
|
|
5687
|
+
if (mode === "summary") {
|
|
5688
|
+
// Manual `/shake summary` installs the compaction controller so Esc /
|
|
5689
|
+
// `abortCompaction()` can cancel the local-model pass; the auto-shake path
|
|
5690
|
+
// passes its own signal and manages `#autoCompactionAbortController`.
|
|
5691
|
+
let controller: AbortController | undefined;
|
|
5692
|
+
let signal = opts.signal;
|
|
5693
|
+
if (!signal) {
|
|
5694
|
+
if (this.#compactionAbortController) throw new Error("Compaction already in progress");
|
|
5695
|
+
controller = new AbortController();
|
|
5696
|
+
this.#compactionAbortController = controller;
|
|
5697
|
+
signal = controller.signal;
|
|
5698
|
+
}
|
|
5699
|
+
try {
|
|
5700
|
+
replacements = await this.#buildShakeSummaryReplacements(regions, artifactId, signal);
|
|
5701
|
+
} finally {
|
|
5702
|
+
if (controller && this.#compactionAbortController === controller) {
|
|
5703
|
+
this.#compactionAbortController = undefined;
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
} else {
|
|
5707
|
+
replacements = regions.map((region, index) => this.#shakeElidePlaceholder(region, index, artifactId));
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5710
|
+
let toolResultsDropped = 0;
|
|
5711
|
+
let blocksDropped = 0;
|
|
5712
|
+
let originalTokens = 0;
|
|
5713
|
+
let replacementTokens = 0;
|
|
5714
|
+
const items = regions.map((region, index) => {
|
|
5715
|
+
if (region.kind === "toolResult") toolResultsDropped++;
|
|
5716
|
+
else blocksDropped++;
|
|
5717
|
+
originalTokens += region.tokens;
|
|
5718
|
+
const replacement = replacements[index];
|
|
5719
|
+
if (replacement.length > 0) replacementTokens += countTokens(replacement);
|
|
5720
|
+
return { region, replacement };
|
|
5721
|
+
});
|
|
5722
|
+
|
|
5723
|
+
applyShakeRegions(items);
|
|
5724
|
+
|
|
5725
|
+
await this.sessionManager.rewriteEntries();
|
|
5726
|
+
const sessionContext = this.buildDisplaySessionContext();
|
|
5727
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
5728
|
+
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
5729
|
+
|
|
5730
|
+
return {
|
|
5731
|
+
mode,
|
|
5732
|
+
toolResultsDropped,
|
|
5733
|
+
blocksDropped,
|
|
5734
|
+
tokensFreed: Math.max(0, originalTokens - replacementTokens),
|
|
5735
|
+
artifactId,
|
|
5736
|
+
};
|
|
5737
|
+
}
|
|
5738
|
+
|
|
5739
|
+
#shakeElidePlaceholder(region: ShakeRegion, index: number, artifactId: string | undefined): string {
|
|
5740
|
+
if (artifactId) {
|
|
5741
|
+
return `[shaken ~${region.tokens} tokens — recover: artifact://${artifactId} (region ${index + 1})]`;
|
|
5742
|
+
}
|
|
5743
|
+
return `[shaken ~${region.tokens} tokens]`;
|
|
5744
|
+
}
|
|
5745
|
+
|
|
5746
|
+
/**
|
|
5747
|
+
* Concatenate the original region contents into one session artifact so the
|
|
5748
|
+
* agent can read them back via `artifact://<id>`. Returns `undefined` when
|
|
5749
|
+
* the session is not persisted or the write fails — callers degrade to a
|
|
5750
|
+
* bare placeholder.
|
|
5751
|
+
*/
|
|
5752
|
+
async #saveShakeArtifact(regions: ShakeRegion[]): Promise<string | undefined> {
|
|
5753
|
+
const parts: string[] = [];
|
|
5754
|
+
for (let i = 0; i < regions.length; i++) {
|
|
5755
|
+
const region = regions[i];
|
|
5756
|
+
parts.push(`### region ${i + 1} (${region.label}, ~${region.tokens} tok)`, "", region.originalText, "");
|
|
5757
|
+
}
|
|
5758
|
+
try {
|
|
5759
|
+
return await this.sessionManager.saveArtifact(parts.join("\n"), "shake");
|
|
5760
|
+
} catch {
|
|
5761
|
+
return undefined;
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5764
|
+
|
|
5765
|
+
/**
|
|
5766
|
+
* Build per-region replacements for summary mode using the configured local
|
|
5767
|
+
* on-device model (`providers.shakeSummaryModel`) via {@link tinyModelClient}.
|
|
5768
|
+
* Shake summary never calls a remote/cloud LLM. When the configured model is
|
|
5769
|
+
* not a known local key, every region falls back to the elide placeholder.
|
|
5770
|
+
* Otherwise compresses via {@link summarizeShakeRegions}; per region, uses
|
|
5771
|
+
* the parsed summary (with a recovery footer) or the elide placeholder when
|
|
5772
|
+
* the local model omitted it / was unavailable. Any thrown failure degrades
|
|
5773
|
+
* the whole batch to elide so the reduction still happens.
|
|
5774
|
+
*/
|
|
5775
|
+
async #buildShakeSummaryReplacements(
|
|
5776
|
+
regions: ShakeRegion[],
|
|
5777
|
+
artifactId: string | undefined,
|
|
5778
|
+
signal: AbortSignal | undefined,
|
|
5779
|
+
): Promise<string[]> {
|
|
5780
|
+
const elide = (): string[] =>
|
|
5781
|
+
regions.map((region, index) => this.#shakeElidePlaceholder(region, index, artifactId));
|
|
5782
|
+
|
|
5783
|
+
const modelKey = this.settings.get("providers.shakeSummaryModel");
|
|
5784
|
+
if (!isTinyMemoryLocalModelKey(modelKey)) return elide();
|
|
5785
|
+
|
|
5786
|
+
const items: ShakeSummaryItem[] = regions.map((region, index) => ({
|
|
5787
|
+
index,
|
|
5788
|
+
label: region.label,
|
|
5789
|
+
text: region.originalText,
|
|
5790
|
+
}));
|
|
5791
|
+
|
|
5792
|
+
const complete: ShakeSummaryComplete = (promptText, opts) =>
|
|
5793
|
+
tinyModelClient.complete(modelKey, promptText, { maxTokens: opts.maxTokens, signal: opts.signal });
|
|
5794
|
+
|
|
5795
|
+
let summaries: Map<number, string>;
|
|
5796
|
+
try {
|
|
5797
|
+
summaries = await summarizeShakeRegions(items, complete, { signal });
|
|
5798
|
+
} catch (error) {
|
|
5799
|
+
logger.warn("Shake summary compression failed; falling back to elide", {
|
|
5800
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5801
|
+
});
|
|
5802
|
+
return elide();
|
|
5803
|
+
}
|
|
5804
|
+
|
|
5805
|
+
return regions.map((region, index) => {
|
|
5806
|
+
const summary = summaries.get(index);
|
|
5807
|
+
if (!summary) return this.#shakeElidePlaceholder(region, index, artifactId);
|
|
5808
|
+
if (artifactId) {
|
|
5809
|
+
return `${summary}\n\n[recover full: artifact://${artifactId} (region ${index + 1})]`;
|
|
5810
|
+
}
|
|
5811
|
+
return summary;
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5482
5815
|
/**
|
|
5483
5816
|
* Manually compact the session context.
|
|
5484
5817
|
* Aborts current agent operation first.
|
|
@@ -5789,9 +6122,9 @@ export class AgentSession {
|
|
|
5789
6122
|
this.agent.reset();
|
|
5790
6123
|
this.#syncAgentSessionId();
|
|
5791
6124
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
5792
|
-
this.#
|
|
6125
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
5793
6126
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
5794
|
-
this.#
|
|
6127
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
5795
6128
|
this.#steeringMessages = [];
|
|
5796
6129
|
this.#followUpMessages = [];
|
|
5797
6130
|
this.#pendingNextTurnMessages = [];
|
|
@@ -6691,6 +7024,14 @@ export class AgentSession {
|
|
|
6691
7024
|
if (compactionSettings.strategy === "off") return false;
|
|
6692
7025
|
if (reason !== "idle" && !compactionSettings.enabled) return false;
|
|
6693
7026
|
const generation = this.#promptGeneration;
|
|
7027
|
+
|
|
7028
|
+
// Shake strategies run inline (cheap, no remote LLM). On overflow recovery,
|
|
7029
|
+
// if shake reclaims nothing we fall through to the summary-compaction body
|
|
7030
|
+
// below so the oversized input still gets resolved.
|
|
7031
|
+
if (compactionSettings.strategy === "shake" || compactionSettings.strategy === "shake-summary") {
|
|
7032
|
+
const outcome = await this.#runAutoShake(reason, compactionSettings.strategy, willRetry, generation);
|
|
7033
|
+
if (outcome !== "fallback") return false;
|
|
7034
|
+
}
|
|
6694
7035
|
// "overflow" and "incomplete" force inline execution because they are recovery
|
|
6695
7036
|
// paths the caller wants resolved before scheduling the next turn. "idle" is
|
|
6696
7037
|
// triggered by the idle loop and does its own scheduling.
|
|
@@ -7073,6 +7414,119 @@ export class AgentSession {
|
|
|
7073
7414
|
return false;
|
|
7074
7415
|
}
|
|
7075
7416
|
|
|
7417
|
+
/**
|
|
7418
|
+
* Run a shake-strategy auto-maintenance pass. Emits the
|
|
7419
|
+
* `auto_compaction_start`/`auto_compaction_end` pair with a shake `action`,
|
|
7420
|
+
* runs {@link shake} inline against the protect-window config, and schedules
|
|
7421
|
+
* continuation exactly like the context-full tail.
|
|
7422
|
+
*
|
|
7423
|
+
* Returns `"fallback"` only for an overflow recovery where shake reclaimed
|
|
7424
|
+
* nothing (or threw) — the caller then runs the summary-compaction body so
|
|
7425
|
+
* the oversized input still gets resolved. Returns `"handled"` otherwise.
|
|
7426
|
+
*/
|
|
7427
|
+
async #runAutoShake(
|
|
7428
|
+
reason: "overflow" | "threshold" | "idle" | "incomplete",
|
|
7429
|
+
strategy: "shake" | "shake-summary",
|
|
7430
|
+
willRetry: boolean,
|
|
7431
|
+
generation: number,
|
|
7432
|
+
): Promise<"handled" | "fallback"> {
|
|
7433
|
+
const action = strategy === "shake-summary" ? "shake-summary" : "shake";
|
|
7434
|
+
const mode = strategy === "shake-summary" ? "summary" : "elide";
|
|
7435
|
+
await this.#emitSessionEvent({ type: "auto_compaction_start", reason, action });
|
|
7436
|
+
this.#autoCompactionAbortController?.abort();
|
|
7437
|
+
const controller = new AbortController();
|
|
7438
|
+
this.#autoCompactionAbortController = controller;
|
|
7439
|
+
const signal = controller.signal;
|
|
7440
|
+
const compactionSettings = this.settings.getGroup("compaction");
|
|
7441
|
+
try {
|
|
7442
|
+
const result = await this.shake(mode, { config: DEFAULT_SHAKE_CONFIG, signal });
|
|
7443
|
+
if (signal.aborted) {
|
|
7444
|
+
await this.#emitSessionEvent({
|
|
7445
|
+
type: "auto_compaction_end",
|
|
7446
|
+
action,
|
|
7447
|
+
result: undefined,
|
|
7448
|
+
aborted: true,
|
|
7449
|
+
willRetry: false,
|
|
7450
|
+
});
|
|
7451
|
+
return "handled";
|
|
7452
|
+
}
|
|
7453
|
+
const reclaimed = result.toolResultsDropped + result.blocksDropped > 0;
|
|
7454
|
+
// Overflow needs the input to actually shrink before the retry; if shake
|
|
7455
|
+
// reclaimed nothing, summarization is the only remaining recovery.
|
|
7456
|
+
if (reason === "overflow" && !reclaimed) {
|
|
7457
|
+
await this.#emitSessionEvent({
|
|
7458
|
+
type: "auto_compaction_end",
|
|
7459
|
+
action,
|
|
7460
|
+
result: undefined,
|
|
7461
|
+
aborted: false,
|
|
7462
|
+
willRetry: false,
|
|
7463
|
+
skipped: true,
|
|
7464
|
+
});
|
|
7465
|
+
return "fallback";
|
|
7466
|
+
}
|
|
7467
|
+
await this.#emitSessionEvent({
|
|
7468
|
+
type: "auto_compaction_end",
|
|
7469
|
+
action,
|
|
7470
|
+
result: undefined,
|
|
7471
|
+
aborted: false,
|
|
7472
|
+
willRetry,
|
|
7473
|
+
skipped: !reclaimed,
|
|
7474
|
+
});
|
|
7475
|
+
|
|
7476
|
+
if (!willRetry && reason !== "idle" && compactionSettings.autoContinue !== false) {
|
|
7477
|
+
this.#scheduleAutoContinuePrompt(generation);
|
|
7478
|
+
}
|
|
7479
|
+
if (willRetry) {
|
|
7480
|
+
// The shake rebuild replays every entry, so a trailing error/length
|
|
7481
|
+
// assistant from the failed turn re-enters agent state — drop it before
|
|
7482
|
+
// retrying, same as the context-full tail.
|
|
7483
|
+
const messages = this.agent.state.messages;
|
|
7484
|
+
const lastMsg = messages[messages.length - 1];
|
|
7485
|
+
if (lastMsg?.role === "assistant") {
|
|
7486
|
+
const lastAssistant = lastMsg as AssistantMessage;
|
|
7487
|
+
const shouldDrop =
|
|
7488
|
+
lastAssistant.stopReason === "error" ||
|
|
7489
|
+
(reason === "incomplete" && lastAssistant.stopReason === "length");
|
|
7490
|
+
if (shouldDrop) this.agent.replaceMessages(messages.slice(0, -1));
|
|
7491
|
+
}
|
|
7492
|
+
this.#scheduleAgentContinue({ delayMs: 100, generation });
|
|
7493
|
+
} else if (this.agent.hasQueuedMessages()) {
|
|
7494
|
+
this.#scheduleAgentContinue({
|
|
7495
|
+
delayMs: 100,
|
|
7496
|
+
generation,
|
|
7497
|
+
shouldContinue: () => this.agent.hasQueuedMessages(),
|
|
7498
|
+
});
|
|
7499
|
+
}
|
|
7500
|
+
return "handled";
|
|
7501
|
+
} catch (error) {
|
|
7502
|
+
if (signal.aborted) {
|
|
7503
|
+
await this.#emitSessionEvent({
|
|
7504
|
+
type: "auto_compaction_end",
|
|
7505
|
+
action,
|
|
7506
|
+
result: undefined,
|
|
7507
|
+
aborted: true,
|
|
7508
|
+
willRetry: false,
|
|
7509
|
+
});
|
|
7510
|
+
return "handled";
|
|
7511
|
+
}
|
|
7512
|
+
const message = error instanceof Error ? error.message : "shake failed";
|
|
7513
|
+
await this.#emitSessionEvent({
|
|
7514
|
+
type: "auto_compaction_end",
|
|
7515
|
+
action,
|
|
7516
|
+
result: undefined,
|
|
7517
|
+
aborted: false,
|
|
7518
|
+
willRetry: false,
|
|
7519
|
+
errorMessage: `Auto-shake failed: ${message}`,
|
|
7520
|
+
});
|
|
7521
|
+
// Overflow still needs recovery even if shake threw.
|
|
7522
|
+
return reason === "overflow" ? "fallback" : "handled";
|
|
7523
|
+
} finally {
|
|
7524
|
+
if (this.#autoCompactionAbortController === controller) {
|
|
7525
|
+
this.#autoCompactionAbortController = undefined;
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
|
|
7076
7530
|
/**
|
|
7077
7531
|
* Toggle auto-compaction setting.
|
|
7078
7532
|
*/
|
|
@@ -7260,7 +7714,9 @@ export class AgentSession {
|
|
|
7260
7714
|
throw new Error(`No API key for retry fallback ${selector.raw}`);
|
|
7261
7715
|
}
|
|
7262
7716
|
|
|
7263
|
-
|
|
7717
|
+
// Capture the configured selector (auto-aware) so a fallback chain preserves
|
|
7718
|
+
// `auto` instead of collapsing it to the level it resolved to this turn.
|
|
7719
|
+
const currentThinkingLevel = this.configuredThinkingLevel();
|
|
7264
7720
|
const nextThinkingLevel = selector.thinkingLevel ?? currentThinkingLevel;
|
|
7265
7721
|
|
|
7266
7722
|
this.#setModelWithProviderSessionReset(candidate);
|
|
@@ -7333,7 +7789,7 @@ export class AgentSession {
|
|
|
7333
7789
|
const apiKey = await this.#modelRegistry.getApiKey(primaryModel, this.sessionId);
|
|
7334
7790
|
if (!apiKey) return;
|
|
7335
7791
|
|
|
7336
|
-
const currentThinkingLevel = this.
|
|
7792
|
+
const currentThinkingLevel = this.configuredThinkingLevel();
|
|
7337
7793
|
const thinkingToApply =
|
|
7338
7794
|
currentThinkingLevel === lastAppliedFallbackThinkingLevel ? originalThinkingLevel : currentThinkingLevel;
|
|
7339
7795
|
this.#setModelWithProviderSessionReset(primaryModel);
|
|
@@ -8033,6 +8489,7 @@ export class AgentSession {
|
|
|
8033
8489
|
promptText: string;
|
|
8034
8490
|
onTextDelta?: (delta: string) => void;
|
|
8035
8491
|
signal?: AbortSignal;
|
|
8492
|
+
dedupeReply?: boolean;
|
|
8036
8493
|
}): Promise<{ replyText: string; assistantMessage: AssistantMessage }> {
|
|
8037
8494
|
const model = this.model;
|
|
8038
8495
|
if (!model) {
|
|
@@ -8095,7 +8552,10 @@ export class AgentSession {
|
|
|
8095
8552
|
if (!assistantMessage) {
|
|
8096
8553
|
throw new Error("Ephemeral turn ended without a final message");
|
|
8097
8554
|
}
|
|
8098
|
-
return {
|
|
8555
|
+
return {
|
|
8556
|
+
replyText: args.dedupeReply === false ? replyText.trim() : dedupeIrcReply(replyText.trim()),
|
|
8557
|
+
assistantMessage,
|
|
8558
|
+
};
|
|
8099
8559
|
}
|
|
8100
8560
|
|
|
8101
8561
|
/**
|
|
@@ -8244,6 +8704,8 @@ export class AgentSession {
|
|
|
8244
8704
|
const previousScheduledHiddenNextTurnGeneration = this.#scheduledHiddenNextTurnGeneration;
|
|
8245
8705
|
const previousModel = this.model;
|
|
8246
8706
|
const previousThinkingLevel = this.#thinkingLevel;
|
|
8707
|
+
const previousAutoThinking = this.#autoThinking;
|
|
8708
|
+
const previousAutoResolvedLevel = this.#autoResolvedLevel;
|
|
8247
8709
|
const previousServiceTier = this.agent.serviceTier;
|
|
8248
8710
|
const previousSelectedMCPToolNames = new Set(this.#selectedMCPToolNames);
|
|
8249
8711
|
const previousTools = [...this.agent.state.tools];
|
|
@@ -8262,7 +8724,7 @@ export class AgentSession {
|
|
|
8262
8724
|
await this.sessionManager.setSessionFile(sessionPath);
|
|
8263
8725
|
this.#syncAgentSessionId();
|
|
8264
8726
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
8265
|
-
this.#
|
|
8727
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
8266
8728
|
|
|
8267
8729
|
const sessionContext = this.buildDisplaySessionContext();
|
|
8268
8730
|
const didReloadConversationChange =
|
|
@@ -8321,12 +8783,26 @@ export class AgentSession {
|
|
|
8321
8783
|
.some(entry => entry.type === "service_tier_change");
|
|
8322
8784
|
const defaultThinkingLevel = this.settings.get("defaultThinkingLevel");
|
|
8323
8785
|
const configuredServiceTier = this.settings.get("serviceTier");
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8786
|
+
// Session log entries store only concrete levels. When `auto` has resolved
|
|
8787
|
+
// for a turn, the persisted context may already carry that concrete level
|
|
8788
|
+
// even if the branch scan races a just-flushed thinking entry under isolated
|
|
8789
|
+
// parallel test workers. Prefer the concrete context value in that case;
|
|
8790
|
+
// otherwise keep the configured `auto` selector so fresh sessions still
|
|
8791
|
+
// classify their first turn.
|
|
8792
|
+
const restoredThinkingLevel: ConfiguredThinkingLevel | undefined =
|
|
8793
|
+
hasThinkingEntry || (defaultThinkingLevel === AUTO_THINKING && sessionContext.thinkingLevel !== "off")
|
|
8794
|
+
? (sessionContext.thinkingLevel as ThinkingLevel | undefined)
|
|
8795
|
+
: defaultThinkingLevel;
|
|
8796
|
+
if (restoredThinkingLevel === AUTO_THINKING) {
|
|
8797
|
+
this.#autoThinking = true;
|
|
8798
|
+
this.#autoResolvedLevel = undefined;
|
|
8799
|
+
this.#thinkingLevel = resolveProvisionalAutoLevel(this.model);
|
|
8800
|
+
} else {
|
|
8801
|
+
this.#autoThinking = false;
|
|
8802
|
+
this.#autoResolvedLevel = undefined;
|
|
8803
|
+
this.#thinkingLevel = resolveThinkingLevelForModel(this.model, restoredThinkingLevel);
|
|
8804
|
+
}
|
|
8805
|
+
this.agent.setThinkingLevel(toReasoningEffort(this.#thinkingLevel));
|
|
8330
8806
|
this.agent.serviceTier = hasServiceTierEntry
|
|
8331
8807
|
? sessionContext.serviceTier
|
|
8332
8808
|
: configuredServiceTier === "none"
|
|
@@ -8335,7 +8811,7 @@ export class AgentSession {
|
|
|
8335
8811
|
|
|
8336
8812
|
if (switchingToDifferentSession) {
|
|
8337
8813
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
8338
|
-
this.#
|
|
8814
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
8339
8815
|
}
|
|
8340
8816
|
this.#reconnectToAgent();
|
|
8341
8817
|
return true;
|
|
@@ -8343,7 +8819,7 @@ export class AgentSession {
|
|
|
8343
8819
|
this.sessionManager.restoreState(previousSessionState);
|
|
8344
8820
|
this.#syncAgentSessionId(previousSessionState.sessionId);
|
|
8345
8821
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
8346
|
-
this.#
|
|
8822
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
8347
8823
|
let restoreMcpError: unknown;
|
|
8348
8824
|
try {
|
|
8349
8825
|
await this.#restoreMCPSelectionsForSessionContext(previousSessionContext, {
|
|
@@ -8375,6 +8851,8 @@ export class AgentSession {
|
|
|
8375
8851
|
this.#syncToolCallBatchCap(undefined);
|
|
8376
8852
|
}
|
|
8377
8853
|
this.#thinkingLevel = previousThinkingLevel;
|
|
8854
|
+
this.#autoThinking = previousAutoThinking;
|
|
8855
|
+
this.#autoResolvedLevel = previousAutoResolvedLevel;
|
|
8378
8856
|
this.agent.setThinkingLevel(toReasoningEffort(previousThinkingLevel));
|
|
8379
8857
|
this.agent.serviceTier = previousServiceTier;
|
|
8380
8858
|
this.#syncTodoPhasesFromBranch();
|
|
@@ -8439,9 +8917,9 @@ export class AgentSession {
|
|
|
8439
8917
|
this.#syncTodoPhasesFromBranch();
|
|
8440
8918
|
this.#syncAgentSessionId();
|
|
8441
8919
|
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
8442
|
-
this.#
|
|
8920
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
8443
8921
|
this.#resetHindsightConversationTrackingIfHindsight();
|
|
8444
|
-
this.#
|
|
8922
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
8445
8923
|
|
|
8446
8924
|
// Reload messages from entries (works for both file and in-memory mode)
|
|
8447
8925
|
const sessionContext = this.buildDisplaySessionContext();
|
|
@@ -8822,7 +9300,7 @@ export class AgentSession {
|
|
|
8822
9300
|
const msg = messages[i];
|
|
8823
9301
|
if (msg.role === "assistant") {
|
|
8824
9302
|
const assistantMsg = msg as AssistantMessage;
|
|
8825
|
-
if (assistantMsg.usage) {
|
|
9303
|
+
if (assistantMsg.stopReason !== "aborted" && assistantMsg.stopReason !== "error" && assistantMsg.usage) {
|
|
8826
9304
|
lastUsage = assistantMsg.usage;
|
|
8827
9305
|
lastUsageIndex = i;
|
|
8828
9306
|
break;
|