@oh-my-pi/pi-coding-agent 17.1.5 → 17.1.7
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 +54 -0
- package/dist/{CHANGELOG-qs3vd6xf.md → CHANGELOG-5k4dq4g6.md} +54 -0
- package/dist/cli.js +3342 -3274
- package/dist/types/capability/index.d.ts +1 -1
- package/dist/types/capability/types.d.ts +23 -1
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/cursor.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +4 -0
- package/dist/types/extensibility/shared-events.d.ts +18 -1
- package/dist/types/internal-urls/mcp-protocol.d.ts +3 -2
- package/dist/types/internal-urls/parse.d.ts +12 -0
- package/dist/types/internal-urls/router.d.ts +6 -0
- package/dist/types/internal-urls/types.d.ts +6 -0
- package/dist/types/lsp/config.d.ts +1 -0
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +5 -0
- package/dist/types/mcp/tool-bridge.d.ts +13 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -0
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -0
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +8 -6
- package/dist/types/session/agent-session.d.ts +28 -1
- package/dist/types/session/model-controls.d.ts +4 -1
- package/dist/types/session/session-advisors.d.ts +7 -1
- package/dist/types/session/session-tools.d.ts +44 -6
- package/dist/types/session/streaming-output.d.ts +7 -2
- package/dist/types/session/tool-choice-queue.d.ts +6 -4
- package/dist/types/session/turn-recovery.d.ts +5 -3
- package/dist/types/task/index.d.ts +1 -1
- package/dist/types/task/types.d.ts +3 -11
- package/dist/types/thinking.d.ts +21 -2
- package/dist/types/tools/index.d.ts +8 -3
- package/dist/types/tools/output-meta.d.ts +5 -0
- package/dist/types/tools/read.d.ts +9 -1
- package/dist/types/tools/xdev.d.ts +53 -67
- package/dist/types/utils/cpuprofile.d.ts +51 -0
- package/dist/types/utils/inspect-image-mode.d.ts +29 -0
- package/dist/types/utils/profile-tree.d.ts +47 -0
- package/dist/types/utils/sample-profile.d.ts +67 -0
- package/dist/types/utils/title-generator.d.ts +17 -16
- package/package.json +12 -12
- package/src/capability/index.ts +43 -12
- package/src/capability/mcp.ts +21 -0
- package/src/capability/types.ts +20 -1
- package/src/cli/read-cli.ts +44 -2
- package/src/config/settings-schema.ts +42 -2
- package/src/config/settings.ts +35 -0
- package/src/cursor.ts +4 -3
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/eval/py/runner.py +16 -2
- package/src/extensibility/extensions/runner.ts +117 -5
- package/src/extensibility/extensions/types.ts +0 -1
- package/src/extensibility/extensions/wrapper.ts +74 -42
- package/src/extensibility/hooks/tool-wrapper.ts +11 -4
- package/src/extensibility/hooks/types.ts +0 -1
- package/src/extensibility/shared-events.ts +18 -1
- package/src/internal-urls/mcp-protocol.ts +17 -3
- package/src/internal-urls/parse.ts +31 -0
- package/src/internal-urls/router.ts +24 -4
- package/src/internal-urls/types.ts +6 -0
- package/src/live/transport.ts +2 -2
- package/src/lsp/client.ts +2 -2
- package/src/lsp/config.ts +4 -0
- package/src/lsp/types.ts +2 -0
- package/src/mcp/config.ts +26 -14
- package/src/mcp/manager.ts +26 -9
- package/src/mcp/tool-bridge.ts +52 -1
- package/src/memories/index.ts +25 -6
- package/src/modes/components/custom-editor.ts +39 -16
- package/src/modes/components/status-line/segments.ts +3 -1
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +8 -7
- package/src/modes/controllers/command-controller.ts +10 -10
- package/src/modes/controllers/extension-ui-controller.ts +7 -7
- package/src/modes/controllers/selector-controller.ts +7 -2
- package/src/modes/rpc/rpc-mode.ts +60 -47
- package/src/prompts/steering/user-interjection.md +2 -5
- package/src/prompts/tools/task.md +4 -2
- package/src/sdk.ts +64 -58
- package/src/session/agent-session-types.ts +8 -5
- package/src/session/agent-session.ts +144 -11
- package/src/session/model-controls.ts +48 -12
- package/src/session/session-advisors.ts +30 -14
- package/src/session/session-listing.ts +66 -4
- package/src/session/session-tools.ts +162 -54
- package/src/session/streaming-output.ts +18 -6
- package/src/session/tool-choice-queue.ts +19 -4
- package/src/session/turn-recovery.ts +25 -6
- package/src/slash-commands/builtin-registry.ts +69 -0
- package/src/task/executor.ts +11 -3
- package/src/task/index.ts +43 -32
- package/src/task/types.ts +12 -17
- package/src/thinking.ts +68 -5
- package/src/tools/bash.ts +16 -9
- package/src/tools/index.ts +36 -16
- package/src/tools/output-meta.ts +20 -0
- package/src/tools/read.ts +89 -15
- package/src/tools/write.ts +16 -7
- package/src/tools/xdev.ts +198 -210
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/sample-profile.ts +437 -0
- package/src/utils/title-generator.ts +88 -34
- package/dist/types/advisor/__tests__/advisor.test.d.ts +0 -1
- package/dist/types/advisor/__tests__/config.test.d.ts +0 -1
- package/dist/types/advisor/__tests__/emission-guard.test.d.ts +0 -1
- package/dist/types/cli/__tests__/auth-gateway-catalog.test.d.ts +0 -1
- package/dist/types/cli/update-cli.test.d.ts +0 -1
- package/dist/types/config/__tests__/model-registry.test.d.ts +0 -1
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/bridge-timeout.test.d.ts +0 -1
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/completion-bridge.test.d.ts +0 -1
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +0 -1
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +0 -1
- package/dist/types/eval/__tests__/js-context-manager.test.d.ts +0 -1
- package/dist/types/eval/__tests__/julia-prelude.test.d.ts +0 -1
- package/dist/types/eval/__tests__/kernel-spawn.test.d.ts +0 -1
- package/dist/types/eval/__tests__/prelude-agent.test.d.ts +0 -1
- package/dist/types/eval/__tests__/process-entry-import.test.d.ts +0 -1
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +0 -1
- package/dist/types/eval/py/__tests__/runner-shell-output.test.d.ts +0 -1
- package/dist/types/hindsight/client.test.d.ts +0 -1
- package/dist/types/internal-urls/__tests__/agent-protocol-nested.test.d.ts +0 -1
- package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +0 -1
- package/dist/types/launch/broker-list-order.test.d.ts +0 -1
- package/dist/types/launch/broker-output-snapshot.test.d.ts +0 -1
- package/dist/types/launch/protocol.test.d.ts +0 -1
- package/dist/types/launch/spawn-options.test.d.ts +0 -1
- package/dist/types/launch/terminal-output.test.d.ts +0 -1
- package/dist/types/live/protocol.test.d.ts +0 -1
- package/dist/types/mcp/config-writer.test.d.ts +0 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +0 -1
- package/dist/types/mcp/smithery-registry.test.d.ts +0 -1
- package/dist/types/mcp/transports/stdio.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/dynamic-border.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/pause-screen.test.d.ts +0 -1
- package/dist/types/modes/components/__tests__/skill-message.test.d.ts +0 -1
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +0 -1
- package/dist/types/modes/components/custom-editor.test.d.ts +0 -1
- package/dist/types/modes/components/login-dialog.test.d.ts +0 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +0 -1
- package/dist/types/modes/components/status-line/component.test.d.ts +0 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.test.d.ts +0 -1
- package/dist/types/modes/noninteractive-dispose.test.d.ts +0 -1
- package/dist/types/modes/print-mode.test.d.ts +0 -1
- package/dist/types/modes/session-teardown.test.d.ts +0 -1
- package/dist/types/modes/theme/mermaid-rendering.test.d.ts +0 -1
- package/dist/types/modes/utils/transcript-render-helpers.test.d.ts +0 -1
- package/dist/types/modes/warp-events.test.d.ts +0 -1
- package/dist/types/plan-mode/approved-plan-prompt.test.d.ts +0 -1
- package/dist/types/plan-mode/model-transition.test.d.ts +0 -1
- package/dist/types/plan-mode/reentry-prompt.test.d.ts +0 -1
- package/dist/types/session/agent-session-error-log.test.d.ts +0 -1
- package/dist/types/session/blob-store.test.d.ts +0 -1
- package/dist/types/session/messages.test.d.ts +0 -1
- package/dist/types/session/session-context.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/connection-manager-timeout.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +0 -1
- package/dist/types/ssh/__tests__/sshfs-mount.test.d.ts +0 -1
- package/dist/types/system-prompt.test.d.ts +0 -1
- package/dist/types/task/render.test.d.ts +0 -1
- package/dist/types/task/spawn-policy.test.d.ts +0 -1
- package/dist/types/tools/__tests__/eval-description.test.d.ts +0 -1
- package/dist/types/tools/__tests__/glob.test.d.ts +0 -1
- package/dist/types/tools/__tests__/json-tree.test.d.ts +0 -1
- package/dist/types/tools/__tests__/vibe-render.test.d.ts +0 -1
- package/dist/types/tools/hub/launch-compat.test.d.ts +0 -1
- package/dist/types/vibe/__tests__/token-rate.test.d.ts +0 -1
- package/src/advisor/__tests__/advisor.test.ts +0 -4889
- package/src/advisor/__tests__/config.test.ts +0 -349
- package/src/advisor/__tests__/emission-guard.test.ts +0 -147
- package/src/cli/__tests__/auth-gateway-catalog.test.ts +0 -111
- package/src/cli/update-cli.test.ts +0 -28
- package/src/config/__tests__/model-registry.test.ts +0 -182
- package/src/eval/__tests__/agent-bridge.test.ts +0 -1509
- package/src/eval/__tests__/bridge-timeout.test.ts +0 -170
- package/src/eval/__tests__/budget-bridge.test.ts +0 -80
- package/src/eval/__tests__/completion-bridge.test.ts +0 -412
- package/src/eval/__tests__/helpers-local-roots.test.ts +0 -55
- package/src/eval/__tests__/idle-timeout.test.ts +0 -80
- package/src/eval/__tests__/js-context-manager.test.ts +0 -456
- package/src/eval/__tests__/julia-prelude.test.ts +0 -66
- package/src/eval/__tests__/kernel-spawn.test.ts +0 -115
- package/src/eval/__tests__/prelude-agent.test.ts +0 -156
- package/src/eval/__tests__/process-entry-import.test.ts +0 -137
- package/src/eval/py/__tests__/prelude.test.ts +0 -104
- package/src/eval/py/__tests__/runner-shell-output.test.ts +0 -157
- package/src/hindsight/client.test.ts +0 -75
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +0 -141
- package/src/internal-urls/__tests__/ssh-protocol.test.ts +0 -331
- package/src/launch/broker-list-order.test.ts +0 -89
- package/src/launch/broker-output-snapshot.test.ts +0 -126
- package/src/launch/protocol.test.ts +0 -59
- package/src/launch/spawn-options.test.ts +0 -31
- package/src/launch/terminal-output.test.ts +0 -107
- package/src/live/protocol.test.ts +0 -140
- package/src/mcp/config-writer.test.ts +0 -43
- package/src/mcp/smithery-auth.test.ts +0 -29
- package/src/mcp/smithery-registry.test.ts +0 -51
- package/src/mcp/transports/stdio.test.ts +0 -427
- package/src/modes/components/__tests__/dynamic-border.test.ts +0 -55
- package/src/modes/components/__tests__/move-overlay.test.ts +0 -252
- package/src/modes/components/__tests__/pause-screen.test.ts +0 -143
- package/src/modes/components/__tests__/skill-message.test.ts +0 -94
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +0 -36
- package/src/modes/components/custom-editor.test.ts +0 -510
- package/src/modes/components/login-dialog.test.ts +0 -56
- package/src/modes/components/status-line/component.jj-cache.test.ts +0 -229
- package/src/modes/components/status-line/component.test.ts +0 -84
- package/src/modes/components/tool-execution.test.ts +0 -162
- package/src/modes/controllers/extension-ui-controller.test.ts +0 -250
- package/src/modes/noninteractive-dispose.test.ts +0 -73
- package/src/modes/print-mode.test.ts +0 -71
- package/src/modes/session-teardown.test.ts +0 -219
- package/src/modes/theme/mermaid-rendering.test.ts +0 -53
- package/src/modes/utils/transcript-render-helpers.test.ts +0 -38
- package/src/modes/warp-events.test.ts +0 -794
- package/src/plan-mode/approved-plan-prompt.test.ts +0 -36
- package/src/plan-mode/model-transition.test.ts +0 -60
- package/src/plan-mode/reentry-prompt.test.ts +0 -41
- package/src/session/agent-session-error-log.test.ts +0 -59
- package/src/session/blob-store.test.ts +0 -56
- package/src/session/messages.test.ts +0 -282
- package/src/session/session-context.test.ts +0 -384
- package/src/ssh/__tests__/connection-manager-args.test.ts +0 -191
- package/src/ssh/__tests__/connection-manager-timeout.test.ts +0 -61
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +0 -105
- package/src/ssh/__tests__/sshfs-mount.test.ts +0 -13
- package/src/system-prompt.test.ts +0 -236
- package/src/task/render.test.ts +0 -290
- package/src/task/spawn-policy.test.ts +0 -62
- package/src/tools/__tests__/eval-description.test.ts +0 -18
- package/src/tools/__tests__/glob.test.ts +0 -37
- package/src/tools/__tests__/json-tree.test.ts +0 -35
- package/src/tools/__tests__/vibe-render.test.ts +0 -210
- package/src/tools/hub/launch-compat.test.ts +0 -40
- package/src/vibe/__tests__/token-rate.test.ts +0 -96
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
type AgentTurnEndContext,
|
|
36
36
|
AppendOnlyContextManager,
|
|
37
37
|
type AsideMessage,
|
|
38
|
+
type BeforeToolCallContext,
|
|
39
|
+
type BeforeToolCallResult,
|
|
38
40
|
resolveTelemetry,
|
|
39
41
|
type StreamFn,
|
|
40
42
|
TERMINAL_TOOL_RESULT_ABORT_REASON,
|
|
@@ -70,6 +72,7 @@ import type {
|
|
|
70
72
|
ToolChoice,
|
|
71
73
|
ToolResultMessage,
|
|
72
74
|
UsageReport,
|
|
75
|
+
UserMessage,
|
|
73
76
|
} from "@oh-my-pi/pi-ai";
|
|
74
77
|
import { type Effort, streamSimple } from "@oh-my-pi/pi-ai";
|
|
75
78
|
import * as AIError from "@oh-my-pi/pi-ai/error";
|
|
@@ -133,6 +136,7 @@ import type { CompactOptions, ContextUsage } from "../extensibility/extensions/t
|
|
|
133
136
|
import type { HookCommandContext } from "../extensibility/hooks/types";
|
|
134
137
|
import type { Skill, SkillWarning } from "../extensibility/skills";
|
|
135
138
|
import { expandSlashCommand, type FileSlashCommand } from "../extensibility/slash-commands";
|
|
139
|
+
import { normalizeToolEventInput, resolveToolEventInput } from "../extensibility/tool-event-input";
|
|
136
140
|
import { GoalRuntime } from "../goals/runtime";
|
|
137
141
|
import type { GoalModeState } from "../goals/state";
|
|
138
142
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
@@ -176,6 +180,7 @@ import {
|
|
|
176
180
|
toReasoningEffort,
|
|
177
181
|
} from "../thinking";
|
|
178
182
|
import { shutdownTinyTitleClient } from "../tiny/title-client";
|
|
183
|
+
import { resolveApproval } from "../tools/approval";
|
|
179
184
|
import { type AskToolDetails, type AskToolInput, recoverAskQuestions } from "../tools/ask";
|
|
180
185
|
import { releaseTabsForOwner } from "../tools/browser/tab-supervisor";
|
|
181
186
|
import type { CheckpointState, CompletedRewindState } from "../tools/checkpoint";
|
|
@@ -196,6 +201,7 @@ import type { EditMode } from "../utils/edit-mode";
|
|
|
196
201
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
197
202
|
import { extractFileMentions, generateFileMentionMessages } from "../utils/file-mentions";
|
|
198
203
|
import { normalizeModelContextImages } from "../utils/image-loading";
|
|
204
|
+
import type { InspectImageMode } from "../utils/inspect-image-mode";
|
|
199
205
|
import { generateSessionTitle } from "../utils/title-generator";
|
|
200
206
|
import { buildNamedToolChoice, isToolChoiceActive } from "../utils/tool-choice";
|
|
201
207
|
import type { VibeModeState } from "../vibe/state";
|
|
@@ -429,6 +435,8 @@ export class AgentSession {
|
|
|
429
435
|
#scheduledHiddenNextTurnGeneration: number | undefined = undefined;
|
|
430
436
|
#queuedMessageDrainScheduled = false;
|
|
431
437
|
#planModeState: PlanModeState | undefined;
|
|
438
|
+
/** Session-scoped `/vision` override; undefined = follow persisted `inspect_image.mode`. */
|
|
439
|
+
#inspectImageModeOverride: InspectImageMode | undefined;
|
|
432
440
|
#vibeModeState: VibeModeState | undefined;
|
|
433
441
|
#goalModeState: GoalModeState | undefined;
|
|
434
442
|
#goalRuntime: GoalRuntime;
|
|
@@ -911,6 +919,7 @@ export class AgentSession {
|
|
|
911
919
|
this.#models = new ModelControls(modelControlsHost, {
|
|
912
920
|
scopedModels: config.scopedModels,
|
|
913
921
|
thinkingLevel: config.thinkingLevel,
|
|
922
|
+
thinkingLevelCeiling: config.thinkingLevelCeiling,
|
|
914
923
|
serviceTierByFamily: config.serviceTierByFamily,
|
|
915
924
|
});
|
|
916
925
|
|
|
@@ -928,6 +937,7 @@ export class AgentSession {
|
|
|
928
937
|
thinkingLevel: () => this.thinkingLevel,
|
|
929
938
|
configuredThinkingLevel: () => this.configuredThinkingLevel(),
|
|
930
939
|
setThinkingLevel: level => this.setThinkingLevel(level),
|
|
940
|
+
thinkingLevelCeiling: () => this.#models.thinkingLevelCeiling,
|
|
931
941
|
isDisposed: () => this.#isDisposed,
|
|
932
942
|
isStreaming: () => this.isStreaming,
|
|
933
943
|
isCompacting: () => this.isCompacting,
|
|
@@ -1079,20 +1089,24 @@ export class AgentSession {
|
|
|
1079
1089
|
emitNotice: (level, message, source) => this.emitNotice(level, message, source),
|
|
1080
1090
|
notifyCommandMetadataChanged: () => this.#notifyCommandMetadataChanged(),
|
|
1081
1091
|
localProtocolOptions: () => this.#localProtocolOptions(),
|
|
1092
|
+
getInspectImageModeOverride: () => this.#inspectImageModeOverride,
|
|
1093
|
+
setInspectImageModeOverride: mode => {
|
|
1094
|
+
this.#inspectImageModeOverride = mode;
|
|
1095
|
+
},
|
|
1082
1096
|
};
|
|
1083
1097
|
this.#tools = new SessionTools(sessionToolsHost, {
|
|
1084
1098
|
autoApprove: config.autoApprove,
|
|
1085
1099
|
toolRegistry: config.toolRegistry,
|
|
1086
1100
|
createVibeTools: config.createVibeTools,
|
|
1087
1101
|
createComputerTool: config.createComputerTool,
|
|
1102
|
+
createInspectImageTool: config.createInspectImageTool,
|
|
1088
1103
|
builtInToolNames: config.builtInToolNames,
|
|
1089
1104
|
presentationPinnedToolNames: config.presentationPinnedToolNames,
|
|
1090
1105
|
ensureWriteRegistered: config.ensureWriteRegistered,
|
|
1091
1106
|
rebuildSystemPrompt: config.rebuildSystemPrompt,
|
|
1092
1107
|
getLocalCalendarDate: config.getLocalCalendarDate,
|
|
1093
1108
|
getMcpServerInstructions: config.getMcpServerInstructions,
|
|
1094
|
-
|
|
1095
|
-
initialMountedXdevToolNames: config.initialMountedXdevToolNames,
|
|
1109
|
+
xdev: config.xdev,
|
|
1096
1110
|
setActiveToolNames: config.setActiveToolNames,
|
|
1097
1111
|
baseSystemPrompt: this.agent.state.systemPrompt,
|
|
1098
1112
|
skills: config.skills,
|
|
@@ -1175,6 +1189,10 @@ export class AgentSession {
|
|
|
1175
1189
|
});
|
|
1176
1190
|
// Tool-result hook owns synchronous post-tool actions that must affect the current loop.
|
|
1177
1191
|
this.agent.afterToolCall = ctx => this.#afterToolCall(ctx);
|
|
1192
|
+
// Pre-scheduling tool_call wiring: extension handlers run at arg-prep
|
|
1193
|
+
// time so a block/revision lands before concurrency resolution,
|
|
1194
|
+
// tool_execution_start, and the wrapper's approval gate.
|
|
1195
|
+
this.agent.beforeToolCall = ctx => this.#beforeToolCall(ctx);
|
|
1178
1196
|
this.agent.providerSessionState = this.#providerSessionState;
|
|
1179
1197
|
this.#syncAgentSessionId();
|
|
1180
1198
|
this.#todo.syncFromBranch();
|
|
@@ -1471,7 +1489,7 @@ export class AgentSession {
|
|
|
1471
1489
|
|
|
1472
1490
|
this.#toolChoiceQueue.pushSequence([forced, "none"], {
|
|
1473
1491
|
label: "user-force",
|
|
1474
|
-
onRejected:
|
|
1492
|
+
onRejected: info => (info.reason === "unavailable" ? "drop_sequence" : "requeue"),
|
|
1475
1493
|
});
|
|
1476
1494
|
}
|
|
1477
1495
|
|
|
@@ -2959,6 +2977,50 @@ export class AgentSession {
|
|
|
2959
2977
|
}
|
|
2960
2978
|
return this.#ttsr.afterToolCall(ctx);
|
|
2961
2979
|
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Emits the extension `tool_call` event for a loop-dispatched call at
|
|
2982
|
+
* arg-prep time — before concurrency scheduling, `tool_execution_start`,
|
|
2983
|
+
* and the wrapper's approval gate. A handler block becomes a blocked tool
|
|
2984
|
+
* result; a handler `input` revision becomes the arguments the loop
|
|
2985
|
+
* schedules, displays, persists, and executes, so approval resolves against
|
|
2986
|
+
* what actually runs. Marks the dispatch so `ExtensionToolWrapper` does not
|
|
2987
|
+
* emit a second event (nested xd:// device dispatches and direct non-loop
|
|
2988
|
+
* execution still emit there).
|
|
2989
|
+
*/
|
|
2990
|
+
async #beforeToolCall(ctx: BeforeToolCallContext): Promise<BeforeToolCallResult | undefined> {
|
|
2991
|
+
const runner = this.#extensionRunner;
|
|
2992
|
+
if (!runner?.hasHandlers("tool_call")) return undefined;
|
|
2993
|
+
const metadata = ctx.toolCall.providerMetadata;
|
|
2994
|
+
const computer = metadata?.type === "computer" ? metadata : undefined;
|
|
2995
|
+
// Parity with the wrapper's pre-emit short-circuit: an already-denied
|
|
2996
|
+
// call never reaches extensions. Deny is mode-independent (tool decision
|
|
2997
|
+
// or user policy), so resolving under the most permissive mode is exact;
|
|
2998
|
+
// the wrapper still enforces the mode-accurate gate before execution.
|
|
2999
|
+
const userPolicies = (this.settings.get("tools.approval") ?? {}) as Record<string, unknown>;
|
|
3000
|
+
const approvalArgs = computer ? { actions: computer.actions } : ctx.args;
|
|
3001
|
+
if (resolveApproval(ctx.tool, approvalArgs, "yolo", userPolicies).policy === "deny") {
|
|
3002
|
+
return undefined;
|
|
3003
|
+
}
|
|
3004
|
+
const eventArgs = computer
|
|
3005
|
+
? { actions: computer.actions, pendingSafetyChecks: computer.pendingSafetyChecks }
|
|
3006
|
+
: ctx.args;
|
|
3007
|
+
runner.markToolCallEmitted(ctx.toolCall.id, ctx.tool.name);
|
|
3008
|
+
const callResult = await runner.emitToolCall({
|
|
3009
|
+
type: "tool_call",
|
|
3010
|
+
toolName: ctx.tool.name,
|
|
3011
|
+
toolCallId: ctx.toolCall.id,
|
|
3012
|
+
input: normalizeToolEventInput(ctx.tool.name, resolveToolEventInput(ctx.tool, eventArgs)),
|
|
3013
|
+
});
|
|
3014
|
+
if (callResult?.block) {
|
|
3015
|
+
return { block: true, reason: callResult.reason || "Tool execution was blocked by an extension" };
|
|
3016
|
+
}
|
|
3017
|
+
// A computer call's event input is a synthetic {actions, pendingSafetyChecks}
|
|
3018
|
+
// view, not the execution params — a revision cannot map back onto them.
|
|
3019
|
+
if (callResult?.input !== undefined && !computer) {
|
|
3020
|
+
return { args: callResult.input };
|
|
3021
|
+
}
|
|
3022
|
+
return undefined;
|
|
3023
|
+
}
|
|
2962
3024
|
|
|
2963
3025
|
/** Find the last assistant message in agent state (including aborted ones) */
|
|
2964
3026
|
#findLastAssistantMessage(): AssistantMessage | undefined {
|
|
@@ -3948,6 +4010,34 @@ export class AgentSession {
|
|
|
3948
4010
|
return this.#tools.setComputerToolEnabled(enabled);
|
|
3949
4011
|
}
|
|
3950
4012
|
|
|
4013
|
+
/**
|
|
4014
|
+
* Session-scoped inspect_image mode (`/vision`). `auto` clears the override
|
|
4015
|
+
* and returns to the persisted `inspect_image.mode` setting; `on`/`off`
|
|
4016
|
+
* force the tool for this session only. See {@link SessionTools.setInspectImageMode}.
|
|
4017
|
+
*/
|
|
4018
|
+
setInspectImageMode(mode: InspectImageMode): Promise<boolean> {
|
|
4019
|
+
return this.#tools.setInspectImageMode(mode);
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
/** Effective inspect_image state for `/vision status`. */
|
|
4023
|
+
inspectImageState(): { mode: InspectImageMode; active: boolean; model: string | undefined } {
|
|
4024
|
+
return this.#tools.inspectImageState();
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
/** Session-scoped `/vision` override; undefined means "follow the persisted setting". */
|
|
4028
|
+
getInspectImageModeOverride(): InspectImageMode | undefined {
|
|
4029
|
+
return this.#inspectImageModeOverride;
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
/**
|
|
4033
|
+
* Reconciles the inspect_image tool set after the persisted
|
|
4034
|
+
* `inspect_image.mode` setting changed (e.g. via the settings selector), so
|
|
4035
|
+
* the new value takes effect immediately instead of on the next model switch.
|
|
4036
|
+
*/
|
|
4037
|
+
applyInspectImageModeChange(): Promise<boolean> {
|
|
4038
|
+
return this.#tools.reconcileInspectImageTool();
|
|
4039
|
+
}
|
|
4040
|
+
|
|
3951
4041
|
/** Cancels the local rollout-memory startup owned by this session. */
|
|
3952
4042
|
cancelLocalMemoryStartup(): void {
|
|
3953
4043
|
this.#memory.cancelLocalMemoryStartup();
|
|
@@ -4222,6 +4312,7 @@ export class AgentSession {
|
|
|
4222
4312
|
|
|
4223
4313
|
/** Drop mutable tool decisions and directives owned by the previous logical session. */
|
|
4224
4314
|
#clearSessionScopedToolState(): void {
|
|
4315
|
+
this.agent.clearDeferredToolDirectives();
|
|
4225
4316
|
this.#toolChoiceQueue.clear();
|
|
4226
4317
|
this.#tools.clearAcpPermissionDecisions();
|
|
4227
4318
|
}
|
|
@@ -5901,6 +5992,10 @@ export class AgentSession {
|
|
|
5901
5992
|
additionalDirectories: this.settings.get("workspace.additionalDirectories"),
|
|
5902
5993
|
});
|
|
5903
5994
|
this.#bash.markSessionTransition(bashTransition);
|
|
5995
|
+
// The new session owns the transcript from here, so the previous
|
|
5996
|
+
// conversation's advisor spend is retired with it. Clearing at the commit
|
|
5997
|
+
// point keeps the status line honest even if a later step below throws.
|
|
5998
|
+
this.#advisors.clearCost();
|
|
5904
5999
|
sessionTransitioned = true;
|
|
5905
6000
|
} finally {
|
|
5906
6001
|
this.#bash.finishSessionTransition(bashTransition, sessionTransitioned);
|
|
@@ -6323,7 +6418,7 @@ export class AgentSession {
|
|
|
6323
6418
|
activeMessages.splice(0, activeMessages.length, ...sessionContext.messages);
|
|
6324
6419
|
}
|
|
6325
6420
|
this.agent.replaceMessages(activeMessages ?? sessionContext.messages);
|
|
6326
|
-
this.#advisors.resetSessionState();
|
|
6421
|
+
this.#advisors.resetSessionState({ preserveCost: true });
|
|
6327
6422
|
this.#todo.syncFromBranch();
|
|
6328
6423
|
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
6329
6424
|
this.#checkpointState = undefined;
|
|
@@ -6398,7 +6493,7 @@ export class AgentSession {
|
|
|
6398
6493
|
return true;
|
|
6399
6494
|
}
|
|
6400
6495
|
|
|
6401
|
-
#setModelWithProviderSessionReset(model: Model): void {
|
|
6496
|
+
async #setModelWithProviderSessionReset(model: Model): Promise<void> {
|
|
6402
6497
|
const currentModel = this.model;
|
|
6403
6498
|
if (currentModel) {
|
|
6404
6499
|
this.#closeProviderSessionsForModelSwitch(currentModel, model);
|
|
@@ -6410,6 +6505,16 @@ export class AgentSession {
|
|
|
6410
6505
|
|
|
6411
6506
|
// Re-evaluate append-only context mode — provider or setting may have changed
|
|
6412
6507
|
this.#syncAppendOnlyContext(model);
|
|
6508
|
+
|
|
6509
|
+
// inspect_image auto mode keys off model image capability. Reconcile
|
|
6510
|
+
// centrally here so retry-fallback model changes (turn-recovery.ts),
|
|
6511
|
+
// which bypass syncAfterModelChange, cannot leave the tool set stale —
|
|
6512
|
+
// callers await, so a scheduled retry never races the reconciled slate.
|
|
6513
|
+
try {
|
|
6514
|
+
await this.#tools.reconcileInspectImageAfterModelChange();
|
|
6515
|
+
} catch (error) {
|
|
6516
|
+
logger.warn("inspect_image reconcile after model change failed", { error: String(error) });
|
|
6517
|
+
}
|
|
6413
6518
|
}
|
|
6414
6519
|
|
|
6415
6520
|
#closeCodexProviderSessionsForHistoryRewrite(): void {
|
|
@@ -6941,7 +7046,7 @@ export class AgentSession {
|
|
|
6941
7046
|
}
|
|
6942
7047
|
|
|
6943
7048
|
this.agent.replaceMessages(sessionContext.messages);
|
|
6944
|
-
this.#advisors.resetSessionState();
|
|
7049
|
+
this.#advisors.resetSessionState({ preserveCost: true });
|
|
6945
7050
|
this.#todo.syncFromBranch();
|
|
6946
7051
|
if (switchingToDifferentSession) {
|
|
6947
7052
|
this.#closeAllProviderSessions("session switch");
|
|
@@ -6974,7 +7079,7 @@ export class AgentSession {
|
|
|
6974
7079
|
currentModel.id !== match.id ||
|
|
6975
7080
|
currentModel.api !== match.api));
|
|
6976
7081
|
if (shouldResetProviderState) {
|
|
6977
|
-
this.#setModelWithProviderSessionReset(match);
|
|
7082
|
+
await this.#setModelWithProviderSessionReset(match);
|
|
6978
7083
|
} else {
|
|
6979
7084
|
this.agent.setModel(match);
|
|
6980
7085
|
}
|
|
@@ -7050,6 +7155,9 @@ export class AgentSession {
|
|
|
7050
7155
|
error: String(refreshErr),
|
|
7051
7156
|
});
|
|
7052
7157
|
}
|
|
7158
|
+
// Only a committed switch retires the previous conversation's advisor spend:
|
|
7159
|
+
// an earlier clear would be lost work if any step above rolled the switch back.
|
|
7160
|
+
if (switchingToDifferentSession) this.#advisors.clearCost();
|
|
7053
7161
|
this.#bash.finishSessionTransition(bashTransition, true);
|
|
7054
7162
|
return true;
|
|
7055
7163
|
} catch (error) {
|
|
@@ -7098,10 +7206,12 @@ export class AgentSession {
|
|
|
7098
7206
|
* @param entryId ID of the entry to branch from
|
|
7099
7207
|
* @returns Object with:
|
|
7100
7208
|
* - selectedText: The text of the selected user message (for editor pre-fill)
|
|
7209
|
+
* - selectedImages: Image attachments of the selected user message (for editor draft restore)
|
|
7101
7210
|
* - cancelled: True if a hook cancelled the branch
|
|
7102
7211
|
*/
|
|
7103
7212
|
async branch(entryId: string): Promise<{
|
|
7104
7213
|
selectedText: string;
|
|
7214
|
+
selectedImages: ImageContent[];
|
|
7105
7215
|
cancelled: boolean;
|
|
7106
7216
|
}> {
|
|
7107
7217
|
const previousSessionFile = this.sessionFile;
|
|
@@ -7112,6 +7222,7 @@ export class AgentSession {
|
|
|
7112
7222
|
}
|
|
7113
7223
|
|
|
7114
7224
|
const selectedText = this.#extractUserMessageText(selectedEntry.message.content);
|
|
7225
|
+
const selectedImages = this.#extractUserMessageImages(selectedEntry.message.content);
|
|
7115
7226
|
|
|
7116
7227
|
let skipConversationRestore = false;
|
|
7117
7228
|
|
|
@@ -7123,7 +7234,7 @@ export class AgentSession {
|
|
|
7123
7234
|
})) as SessionBeforeBranchResult | undefined;
|
|
7124
7235
|
|
|
7125
7236
|
if (result?.cancel) {
|
|
7126
|
-
return { selectedText, cancelled: true };
|
|
7237
|
+
return { selectedText, selectedImages, cancelled: true };
|
|
7127
7238
|
}
|
|
7128
7239
|
skipConversationRestore = result?.skipConversationRestore ?? false;
|
|
7129
7240
|
}
|
|
@@ -7148,6 +7259,7 @@ export class AgentSession {
|
|
|
7148
7259
|
this.sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
7149
7260
|
}
|
|
7150
7261
|
this.#bash.markSessionTransition(bashTransition);
|
|
7262
|
+
this.#advisors.clearCost();
|
|
7151
7263
|
sessionTransitioned = true;
|
|
7152
7264
|
} finally {
|
|
7153
7265
|
this.#bash.finishSessionTransition(bashTransition, sessionTransitioned);
|
|
@@ -7178,7 +7290,7 @@ export class AgentSession {
|
|
|
7178
7290
|
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
7179
7291
|
}
|
|
7180
7292
|
|
|
7181
|
-
return { selectedText, cancelled: false };
|
|
7293
|
+
return { selectedText, selectedImages, cancelled: false };
|
|
7182
7294
|
}
|
|
7183
7295
|
|
|
7184
7296
|
async branchFromBtw(
|
|
@@ -7245,6 +7357,7 @@ export class AgentSession {
|
|
|
7245
7357
|
try {
|
|
7246
7358
|
this.sessionManager.createBranchedSession(leafId);
|
|
7247
7359
|
this.#bash.markSessionTransition(bashTransition);
|
|
7360
|
+
this.#advisors.clearCost();
|
|
7248
7361
|
sessionTransitioned = true;
|
|
7249
7362
|
} finally {
|
|
7250
7363
|
this.#bash.finishSessionTransition(bashTransition, sessionTransitioned);
|
|
@@ -7292,7 +7405,7 @@ export class AgentSession {
|
|
|
7292
7405
|
* @param targetId The entry ID to navigate to
|
|
7293
7406
|
* @param options.summarize Whether user wants to summarize abandoned branch
|
|
7294
7407
|
* @param options.customInstructions Custom instructions for summarizer
|
|
7295
|
-
* @returns Result with editorText (if user message) and cancelled status
|
|
7408
|
+
* @returns Result with editorText/editorImages (if user message) and cancelled status
|
|
7296
7409
|
*/
|
|
7297
7410
|
async navigateTree(
|
|
7298
7411
|
targetId: string,
|
|
@@ -7322,6 +7435,8 @@ export class AgentSession {
|
|
|
7322
7435
|
} = {},
|
|
7323
7436
|
): Promise<{
|
|
7324
7437
|
editorText?: string;
|
|
7438
|
+
/** Image attachments of the target user message, parallel to the positional `[Image #N]` markers in {@link editorText}. */
|
|
7439
|
+
editorImages?: ImageContent[];
|
|
7325
7440
|
cancelled: boolean;
|
|
7326
7441
|
aborted?: boolean;
|
|
7327
7442
|
summaryEntry?: BranchSummaryEntry;
|
|
@@ -7494,6 +7609,7 @@ export class AgentSession {
|
|
|
7494
7609
|
// Determine the new leaf position based on target type
|
|
7495
7610
|
let newLeafId: string | null;
|
|
7496
7611
|
let editorText: string | undefined;
|
|
7612
|
+
let editorImages: ImageContent[] | undefined;
|
|
7497
7613
|
// Set when the second-pass `ask` re-answer branch below actually commits a
|
|
7498
7614
|
// new sibling answer — the trigger for resuming the agent afterwards so the
|
|
7499
7615
|
// model consumes it, mirroring a live `ask` completion (issue #6483).
|
|
@@ -7503,6 +7619,8 @@ export class AgentSession {
|
|
|
7503
7619
|
// User message: leaf = parent (null if root), text goes to editor
|
|
7504
7620
|
newLeafId = targetEntry.parentId;
|
|
7505
7621
|
editorText = this.#extractUserMessageText(targetEntry.message.content);
|
|
7622
|
+
const targetImages = this.#extractUserMessageImages(targetEntry.message.content);
|
|
7623
|
+
if (targetImages.length > 0) editorImages = targetImages;
|
|
7506
7624
|
} else if (targetEntry.type === "custom_message" && targetEntry.customType !== SKILL_PROMPT_MESSAGE_TYPE) {
|
|
7507
7625
|
// Custom message: leaf = parent (null if root), text goes to editor
|
|
7508
7626
|
newLeafId = targetEntry.parentId;
|
|
@@ -7574,7 +7692,7 @@ export class AgentSession {
|
|
|
7574
7692
|
const displayContext = deobfuscateSessionContext(stateContext, this.#obfuscator);
|
|
7575
7693
|
this.agent.replaceMessages(displayContext.messages);
|
|
7576
7694
|
this.#rehydrateCheckpointRewindState();
|
|
7577
|
-
this.#advisors.resetSessionState();
|
|
7695
|
+
this.#advisors.resetSessionState({ preserveCost: true });
|
|
7578
7696
|
this.#todo.syncFromBranch();
|
|
7579
7697
|
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
7580
7698
|
|
|
@@ -7602,6 +7720,7 @@ export class AgentSession {
|
|
|
7602
7720
|
const rawContext = this.sessionManager.buildSessionContext();
|
|
7603
7721
|
return {
|
|
7604
7722
|
editorText,
|
|
7723
|
+
editorImages,
|
|
7605
7724
|
cancelled: false,
|
|
7606
7725
|
summaryEntry,
|
|
7607
7726
|
sessionContext: rawContext,
|
|
@@ -7610,6 +7729,7 @@ export class AgentSession {
|
|
|
7610
7729
|
}
|
|
7611
7730
|
return {
|
|
7612
7731
|
editorText,
|
|
7732
|
+
editorImages,
|
|
7613
7733
|
cancelled: false,
|
|
7614
7734
|
summaryEntry,
|
|
7615
7735
|
sessionContext: stateContext,
|
|
@@ -7728,6 +7848,14 @@ export class AgentSession {
|
|
|
7728
7848
|
return "";
|
|
7729
7849
|
}
|
|
7730
7850
|
|
|
7851
|
+
/** Image parts of a stored user message, in submission order — index N-1 backs the
|
|
7852
|
+
* `[Image #N]` marker in the message text, so restoring them alongside the text keeps
|
|
7853
|
+
* positional markers resolvable on resubmit. */
|
|
7854
|
+
#extractUserMessageImages(content: UserMessage["content"]): ImageContent[] {
|
|
7855
|
+
if (!Array.isArray(content)) return [];
|
|
7856
|
+
return content.filter((c): c is ImageContent => c.type === "image");
|
|
7857
|
+
}
|
|
7858
|
+
|
|
7731
7859
|
/**
|
|
7732
7860
|
* Get session statistics.
|
|
7733
7861
|
*/
|
|
@@ -8205,6 +8333,11 @@ export class AgentSession {
|
|
|
8205
8333
|
getAdvisorStatusOverview(): { configured: boolean; advisors: { name: string; status: AdvisorRuntimeStatus }[] } {
|
|
8206
8334
|
return this.#advisors.getAdvisorStatusOverview();
|
|
8207
8335
|
}
|
|
8336
|
+
|
|
8337
|
+
/** Return cumulative cost recorded for the current session's advisor activity. */
|
|
8338
|
+
getAdvisorCost(): number {
|
|
8339
|
+
return this.#advisors.getAdvisorCost();
|
|
8340
|
+
}
|
|
8208
8341
|
/**
|
|
8209
8342
|
* Return structured advisor stats for the status command and TUI panel.
|
|
8210
8343
|
*/
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
AUTO_THINKING,
|
|
28
28
|
type ConfiguredThinkingLevel,
|
|
29
29
|
clampAutoThinkingEffort,
|
|
30
|
+
clampThinkingLevelToCeiling,
|
|
30
31
|
resolveProvisionalAutoLevel,
|
|
31
32
|
resolveThinkingLevelForModel,
|
|
32
33
|
shouldDisableReasoning,
|
|
@@ -51,7 +52,7 @@ export interface ModelControlsHost {
|
|
|
51
52
|
promptGeneration(): number;
|
|
52
53
|
resolveActiveEditMode(): EditMode;
|
|
53
54
|
syncAfterModelChange(previousEditMode: EditMode): Promise<void>;
|
|
54
|
-
setModelWithProviderSessionReset(model: Model): void
|
|
55
|
+
setModelWithProviderSessionReset(model: Model): Promise<void>;
|
|
55
56
|
clearActiveRetryFallback(): void;
|
|
56
57
|
clearInheritedProviderPromptCacheKey(): void;
|
|
57
58
|
magicKeywordEnabled(keyword: "orchestrate" | "ultrathink" | "workflow"): boolean;
|
|
@@ -65,6 +66,8 @@ export class ModelControls {
|
|
|
65
66
|
readonly #host: ModelControlsHost;
|
|
66
67
|
#scopedModels: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
67
68
|
#thinkingLevel: ThinkingLevel | undefined;
|
|
69
|
+
/** Hard per-session effort ceiling (e.g. a task spawn's `task.maxEffort` cap); recovery paths re-clamp to it. */
|
|
70
|
+
readonly #thinkingLevelCeiling: Effort | undefined;
|
|
68
71
|
#autoThinking = false;
|
|
69
72
|
#autoResolvedLevel: Effort | undefined;
|
|
70
73
|
#serviceTierByFamily: ServiceTierByFamily;
|
|
@@ -74,18 +77,28 @@ export class ModelControls {
|
|
|
74
77
|
options: {
|
|
75
78
|
scopedModels?: Array<{ model: Model; thinkingLevel?: ThinkingLevel }>;
|
|
76
79
|
thinkingLevel?: ConfiguredThinkingLevel;
|
|
80
|
+
thinkingLevelCeiling?: Effort;
|
|
77
81
|
serviceTierByFamily?: ServiceTierByFamily;
|
|
78
82
|
},
|
|
79
83
|
) {
|
|
80
84
|
this.#host = host;
|
|
81
85
|
this.#scopedModels = options.scopedModels ?? [];
|
|
82
86
|
this.#serviceTierByFamily = options.serviceTierByFamily ?? {};
|
|
87
|
+
this.#thinkingLevelCeiling = options.thinkingLevelCeiling;
|
|
83
88
|
if (options.thinkingLevel === AUTO_THINKING) {
|
|
84
89
|
// Keep auto pending until the first turn while exposing a valid wire effort.
|
|
85
90
|
this.#autoThinking = true;
|
|
86
|
-
this.#thinkingLevel =
|
|
91
|
+
this.#thinkingLevel = clampThinkingLevelToCeiling(
|
|
92
|
+
this.#model,
|
|
93
|
+
resolveProvisionalAutoLevel(this.#model),
|
|
94
|
+
this.#thinkingLevelCeiling,
|
|
95
|
+
);
|
|
87
96
|
} else {
|
|
88
|
-
this.#thinkingLevel =
|
|
97
|
+
this.#thinkingLevel = clampThinkingLevelToCeiling(
|
|
98
|
+
this.#model,
|
|
99
|
+
options.thinkingLevel,
|
|
100
|
+
this.#thinkingLevelCeiling,
|
|
101
|
+
);
|
|
89
102
|
}
|
|
90
103
|
this.#applyThinkingLevelToAgent(this.#thinkingLevel);
|
|
91
104
|
}
|
|
@@ -99,6 +112,11 @@ export class ModelControls {
|
|
|
99
112
|
return this.#thinkingLevel;
|
|
100
113
|
}
|
|
101
114
|
|
|
115
|
+
/** Hard per-session effort ceiling every thinking-level change is clamped to. */
|
|
116
|
+
get thinkingLevelCeiling(): Effort | undefined {
|
|
117
|
+
return this.#thinkingLevelCeiling;
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
/** Configured selector, preserving `auto` while classification is active. */
|
|
103
121
|
configuredThinkingLevel(): ConfiguredThinkingLevel | undefined {
|
|
104
122
|
return this.#autoThinking ? AUTO_THINKING : this.#thinkingLevel;
|
|
@@ -130,8 +148,15 @@ export class ModelControls {
|
|
|
130
148
|
this.#autoResolvedLevel = undefined;
|
|
131
149
|
this.#thinkingLevel =
|
|
132
150
|
level === AUTO_THINKING
|
|
133
|
-
?
|
|
134
|
-
|
|
151
|
+
? clampThinkingLevelToCeiling(
|
|
152
|
+
this.#model,
|
|
153
|
+
resolveProvisionalAutoLevel(this.#model),
|
|
154
|
+
this.#thinkingLevelCeiling,
|
|
155
|
+
)
|
|
156
|
+
: resolveThinkingLevelForModel(
|
|
157
|
+
this.#model,
|
|
158
|
+
clampThinkingLevelToCeiling(this.#model, level, this.#thinkingLevelCeiling),
|
|
159
|
+
);
|
|
135
160
|
this.#applyThinkingLevelToAgent(this.#thinkingLevel);
|
|
136
161
|
}
|
|
137
162
|
|
|
@@ -195,7 +220,7 @@ export class ModelControls {
|
|
|
195
220
|
|
|
196
221
|
this.#host.modelRegistry.clearSuppressedSelector(formatModelStringWithRouting(targetModel));
|
|
197
222
|
this.#host.clearActiveRetryFallback();
|
|
198
|
-
this.#host.setModelWithProviderSessionReset(targetModel);
|
|
223
|
+
await this.#host.setModelWithProviderSessionReset(targetModel);
|
|
199
224
|
this.#host.sessionManager.appendModelChange(`${targetModel.provider}/${targetModel.id}`, role);
|
|
200
225
|
if (options?.persist) {
|
|
201
226
|
this.#host.settings.setModelRole(
|
|
@@ -240,7 +265,7 @@ export class ModelControls {
|
|
|
240
265
|
|
|
241
266
|
this.#host.modelRegistry.clearSuppressedSelector(formatModelStringWithRouting(targetModel));
|
|
242
267
|
this.#host.clearActiveRetryFallback();
|
|
243
|
-
this.#host.setModelWithProviderSessionReset(targetModel);
|
|
268
|
+
await this.#host.setModelWithProviderSessionReset(targetModel);
|
|
244
269
|
this.#host.sessionManager.appendModelChange(
|
|
245
270
|
`${targetModel.provider}/${targetModel.id}`,
|
|
246
271
|
options?.ephemeral ? EPHEMERAL_MODEL_CHANGE_ROLE : "temporary",
|
|
@@ -400,7 +425,7 @@ export class ModelControls {
|
|
|
400
425
|
// Apply model
|
|
401
426
|
this.#host.modelRegistry.clearSuppressedSelector(formatModelStringWithRouting(next.model));
|
|
402
427
|
this.#host.clearActiveRetryFallback();
|
|
403
|
-
this.#host.setModelWithProviderSessionReset(next.model);
|
|
428
|
+
await this.#host.setModelWithProviderSessionReset(next.model);
|
|
404
429
|
this.#host.sessionManager.appendModelChange(`${next.model.provider}/${next.model.id}`);
|
|
405
430
|
this.#host.settings.getStorage()?.recordModelUsage(`${next.model.provider}/${next.model.id}`);
|
|
406
431
|
|
|
@@ -431,7 +456,7 @@ export class ModelControls {
|
|
|
431
456
|
|
|
432
457
|
this.#host.modelRegistry.clearSuppressedSelector(formatModelStringWithRouting(nextModel));
|
|
433
458
|
this.#host.clearActiveRetryFallback();
|
|
434
|
-
this.#host.setModelWithProviderSessionReset(nextModel);
|
|
459
|
+
await this.#host.setModelWithProviderSessionReset(nextModel);
|
|
435
460
|
this.#host.sessionManager.appendModelChange(`${nextModel.provider}/${nextModel.id}`);
|
|
436
461
|
this.#host.settings.getStorage()?.recordModelUsage(`${nextModel.provider}/${nextModel.id}`);
|
|
437
462
|
// Re-apply the current thinking level (or auto) for the newly selected model
|
|
@@ -469,7 +494,11 @@ export class ModelControls {
|
|
|
469
494
|
*/
|
|
470
495
|
setThinkingLevel(level: ConfiguredThinkingLevel | undefined, persist: boolean = false): void {
|
|
471
496
|
if (level === AUTO_THINKING) {
|
|
472
|
-
const provisional =
|
|
497
|
+
const provisional = clampThinkingLevelToCeiling(
|
|
498
|
+
this.#model,
|
|
499
|
+
resolveProvisionalAutoLevel(this.#model),
|
|
500
|
+
this.#thinkingLevelCeiling,
|
|
501
|
+
);
|
|
473
502
|
const wasAuto = this.#autoThinking;
|
|
474
503
|
const previousLevel = this.#thinkingLevel;
|
|
475
504
|
this.#autoThinking = true;
|
|
@@ -493,7 +522,10 @@ export class ModelControls {
|
|
|
493
522
|
const wasAuto = this.#autoThinking;
|
|
494
523
|
this.#autoThinking = false;
|
|
495
524
|
this.#autoResolvedLevel = undefined;
|
|
496
|
-
const effectiveLevel = resolveThinkingLevelForModel(
|
|
525
|
+
const effectiveLevel = resolveThinkingLevelForModel(
|
|
526
|
+
this.#model,
|
|
527
|
+
clampThinkingLevelToCeiling(this.#model, level, this.#thinkingLevelCeiling),
|
|
528
|
+
);
|
|
497
529
|
// Leaving auto must persist even when the resolved effort is unchanged (e.g.
|
|
498
530
|
// auto resolved to medium, then the user pins medium): otherwise the latest
|
|
499
531
|
// session entry keeps `configured: "auto"` and resume re-enables auto.
|
|
@@ -591,7 +623,11 @@ export class ModelControls {
|
|
|
591
623
|
// Drop the result if the turn was aborted/superseded while classifying.
|
|
592
624
|
if (this.#host.promptGeneration() !== generation || !this.#autoThinking) return;
|
|
593
625
|
|
|
594
|
-
const effort =
|
|
626
|
+
const effort = clampThinkingLevelToCeiling(
|
|
627
|
+
model,
|
|
628
|
+
resolved ?? resolveProvisionalAutoLevel(model),
|
|
629
|
+
this.#thinkingLevelCeiling,
|
|
630
|
+
);
|
|
595
631
|
if (effort === undefined) return;
|
|
596
632
|
const shouldPersistResolution = this.#thinkingLevel !== effort;
|
|
597
633
|
this.#autoResolvedLevel = effort;
|