@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.0
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/README.md +1 -1
- package/dist/cli.js +4659 -4261
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/model-registry.d.ts +3 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +316 -47
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +16 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +7 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +118 -755
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +5 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/types.d.ts +94 -0
- package/package.json +12 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/usage-cli.ts +10 -1
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/model-registry.ts +10 -2
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +209 -51
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +29 -9
- package/src/lsp/index.ts +19 -4
- package/src/main.ts +8 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +1 -1
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +23 -1
- package/src/modes/components/settings-selector.ts +169 -1
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +56 -15
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +10 -1
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +157 -38
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +210 -136
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1892 -12226
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.ts +6 -3
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +0 -46
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +22 -3
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +285 -70
- package/src/task/index.ts +98 -105
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +1 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +9 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +3 -0
|
@@ -55,7 +55,7 @@ import { reset as resetCapabilities } from "../capability";
|
|
|
55
55
|
import type { CollabGuestLink } from "../collab/guest";
|
|
56
56
|
import type { CollabHost } from "../collab/host";
|
|
57
57
|
import { KeybindingsManager } from "../config/keybindings";
|
|
58
|
-
import type
|
|
58
|
+
import { formatModelString, type ResolvedModelRoleValue } from "../config/model-resolver";
|
|
59
59
|
import { applyProviderGlobalsFromSettings } from "../config/provider-globals";
|
|
60
60
|
import {
|
|
61
61
|
isSettingsInitialized,
|
|
@@ -130,8 +130,19 @@ import type { EventBus } from "../utils/event-bus";
|
|
|
130
130
|
import { getEditorCommand, openInEditor } from "../utils/external-editor";
|
|
131
131
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
|
|
132
132
|
import { messageHasDisplayableThinking } from "../utils/thinking-display";
|
|
133
|
-
import {
|
|
134
|
-
|
|
133
|
+
import {
|
|
134
|
+
disposeTerminalTitleState,
|
|
135
|
+
popTerminalTitle,
|
|
136
|
+
pushTerminalTitle,
|
|
137
|
+
setSessionTerminalTitle,
|
|
138
|
+
setTerminalTitleStateEnabled,
|
|
139
|
+
} from "../utils/title-generator";
|
|
140
|
+
import {
|
|
141
|
+
aggregateVibeWorkerTokensPerSecond,
|
|
142
|
+
type VibeOwnerScope,
|
|
143
|
+
type VibeParentSession,
|
|
144
|
+
VibeSessionRegistry,
|
|
145
|
+
} from "../vibe/runtime";
|
|
135
146
|
import type { AssistantMessageComponent } from "./components/assistant-message";
|
|
136
147
|
import type { BashExecutionComponent } from "./components/bash-execution";
|
|
137
148
|
import { ChatBlock, type ChatBlockHost } from "./components/chat-block";
|
|
@@ -142,7 +153,7 @@ import type { EvalExecutionComponent } from "./components/eval-execution";
|
|
|
142
153
|
import type { HookEditorComponent } from "./components/hook-editor";
|
|
143
154
|
import type { HookInputComponent } from "./components/hook-input";
|
|
144
155
|
import type { HookSelectorComponent, HookSelectorSlider } from "./components/hook-selector";
|
|
145
|
-
import { PlanReviewOverlay } from "./components/plan-review-overlay";
|
|
156
|
+
import { type PlanReviewAnnotationState, PlanReviewOverlay } from "./components/plan-review-overlay";
|
|
146
157
|
import { StatusLineComponent } from "./components/status-line";
|
|
147
158
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
148
159
|
import { TranscriptContainer } from "./components/transcript-container";
|
|
@@ -152,6 +163,7 @@ import { CommandController } from "./controllers/command-controller";
|
|
|
152
163
|
import { EventController } from "./controllers/event-controller";
|
|
153
164
|
import { ExtensionUiController } from "./controllers/extension-ui-controller";
|
|
154
165
|
import { InputController } from "./controllers/input-controller";
|
|
166
|
+
import { LiveCommandController } from "./controllers/live-command-controller";
|
|
155
167
|
import { MCPCommandController } from "./controllers/mcp-command-controller";
|
|
156
168
|
import { OmfgController } from "./controllers/omfg-controller";
|
|
157
169
|
import { SelectorController } from "./controllers/selector-controller";
|
|
@@ -543,6 +555,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
543
555
|
#planModePreviousTools: string[] | undefined;
|
|
544
556
|
#goalModePreviousTools: string[] | undefined;
|
|
545
557
|
#vibeModePreviousTools: string[] | undefined;
|
|
558
|
+
#vibeModeOwnerScope: VibeOwnerScope | undefined;
|
|
559
|
+
#vibeScopeSuspendedForSwitch = false;
|
|
546
560
|
#goalContinuationTimer: NodeJS.Timeout | undefined;
|
|
547
561
|
#goalTurnHadToolCalls = false;
|
|
548
562
|
#goalContinuationTurnInFlight = false;
|
|
@@ -555,6 +569,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
555
569
|
#planReviewOverlay: PlanReviewOverlay | undefined;
|
|
556
570
|
#planReviewOverlayHandle: OverlayHandle | undefined;
|
|
557
571
|
#planReviewCancel: (() => void) | undefined;
|
|
572
|
+
/** Serializable review annotations keyed by the resolved plan file path. */
|
|
573
|
+
#planReviewAnnotationState = new Map<string, PlanReviewAnnotationState>();
|
|
574
|
+
/** Annotation state held until the associated queued refinement actually starts. */
|
|
575
|
+
#planReviewAnnotationStateBySubmission = new WeakMap<SubmittedUserInput, string>();
|
|
558
576
|
readonly lspServers: LspStartupServerInfo[] | undefined = undefined;
|
|
559
577
|
mcpManager?: MCPManager;
|
|
560
578
|
readonly #toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
|
|
@@ -564,6 +582,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
564
582
|
readonly #omfgController: OmfgController;
|
|
565
583
|
readonly #commandController: CommandController;
|
|
566
584
|
readonly #todoCommandController: TodoCommandController;
|
|
585
|
+
readonly #liveCommandController: LiveCommandController;
|
|
567
586
|
readonly #eventController: EventController;
|
|
568
587
|
get eventController(): EventController {
|
|
569
588
|
return this.#eventController;
|
|
@@ -773,6 +792,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
773
792
|
this.#eventController = new EventController(this);
|
|
774
793
|
this.#commandController = new CommandController(this);
|
|
775
794
|
this.#todoCommandController = new TodoCommandController(this);
|
|
795
|
+
this.#liveCommandController = new LiveCommandController(this);
|
|
776
796
|
this.#selectorController = new SelectorController(this);
|
|
777
797
|
this.#focusController = new SessionFocusController(this);
|
|
778
798
|
this.#inputController = new InputController(this);
|
|
@@ -976,6 +996,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
976
996
|
// the initial welcome frame does not append over the previous run's scrollback.
|
|
977
997
|
this.ui.start({ clearScrollback: options.clearInitialTerminalHistory === true });
|
|
978
998
|
pushTerminalTitle();
|
|
999
|
+
setTerminalTitleStateEnabled(this.settings.get("tui.titleState"));
|
|
979
1000
|
setSessionTerminalTitle(this.sessionManager.getSessionName(), this.sessionManager.getCwd());
|
|
980
1001
|
this.updateEditorBorderColor();
|
|
981
1002
|
// Single side-effect point for title changes: every setSessionName caller
|
|
@@ -997,6 +1018,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
997
1018
|
await this.initHooksAndCustomTools();
|
|
998
1019
|
|
|
999
1020
|
// Restore mode from session (e.g. plan mode on resume)
|
|
1021
|
+
this.session.setSessionBeforeSwitchReconciler?.(async () => {
|
|
1022
|
+
await this.#liveCommandController.stop();
|
|
1023
|
+
await this.#quiesceVibeForSessionSwitch();
|
|
1024
|
+
});
|
|
1000
1025
|
this.session.setSessionSwitchReconciler?.(() => this.#reconcileModeFromSession({ preserveActiveGoal: true }));
|
|
1001
1026
|
await this.#reconcileModeFromSession();
|
|
1002
1027
|
|
|
@@ -1566,6 +1591,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1566
1591
|
return false;
|
|
1567
1592
|
}
|
|
1568
1593
|
input.started = true;
|
|
1594
|
+
const annotationStateKey = this.#planReviewAnnotationStateBySubmission.get(input);
|
|
1595
|
+
if (annotationStateKey) {
|
|
1596
|
+
this.#planReviewAnnotationStateBySubmission.delete(input);
|
|
1597
|
+
this.#planReviewAnnotationState.delete(annotationStateKey);
|
|
1598
|
+
}
|
|
1569
1599
|
return true;
|
|
1570
1600
|
}
|
|
1571
1601
|
|
|
@@ -1756,6 +1786,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1756
1786
|
return theme.fg("accent", `${prefix}${checkbox.unchecked} ${todo.content}`) + marker;
|
|
1757
1787
|
case "abandoned":
|
|
1758
1788
|
return theme.fg("error", `${prefix}${checkbox.unchecked} ${chalk.strikethrough(todo.content)}`) + marker;
|
|
1789
|
+
case "blocked":
|
|
1790
|
+
return theme.fg("warning", `${prefix}${checkbox.unchecked} ${todo.content} (blocked)`) + marker;
|
|
1759
1791
|
default:
|
|
1760
1792
|
if (matched) return theme.fg("accent", `${prefix}${checkbox.unchecked} ${todo.content}`) + marker;
|
|
1761
1793
|
return theme.fg("dim", `${prefix}${checkbox.unchecked} ${todo.content}`) + marker;
|
|
@@ -1775,12 +1807,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1775
1807
|
}
|
|
1776
1808
|
|
|
1777
1809
|
/**
|
|
1778
|
-
* Auto-complete any pending/in_progress
|
|
1779
|
-
* subagent that has finished successfully. Fires on every observer
|
|
1810
|
+
* Auto-complete any open todo (pending/in_progress/blocked) whose content
|
|
1811
|
+
* matches a subagent that has finished successfully. Fires on every observer
|
|
1780
1812
|
* `onChange` so the visual state stays in sync with subagent lifecycle
|
|
1781
|
-
* without requiring the agent to issue a follow-up `todo`.
|
|
1782
|
-
*
|
|
1783
|
-
*
|
|
1813
|
+
* without requiring the agent to issue a follow-up `todo`. A todo `block`ed
|
|
1814
|
+
* while waiting on a detached subagent is included: that subagent completing
|
|
1815
|
+
* is exactly the unblock signal, and blocked todos are excluded from the stop
|
|
1816
|
+
* reminder, so leaving it blocked would strand it silently. Failed and aborted
|
|
1817
|
+
* subagents are intentionally NOT auto-completed — those stay open so the user
|
|
1818
|
+
* (or the next agent turn) can decide what to do.
|
|
1784
1819
|
*
|
|
1785
1820
|
* Idempotent: only flips open tasks, never re-touches completed ones.
|
|
1786
1821
|
*/
|
|
@@ -1799,10 +1834,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1799
1834
|
const next: TodoPhase[] = this.todoPhases.map(phase => ({
|
|
1800
1835
|
name: phase.name,
|
|
1801
1836
|
tasks: phase.tasks.map(task => {
|
|
1802
|
-
if (task.status !== "pending" && task.status !== "in_progress"
|
|
1837
|
+
if (task.status !== "pending" && task.status !== "in_progress" && task.status !== "blocked") {
|
|
1838
|
+
return task;
|
|
1839
|
+
}
|
|
1803
1840
|
if (!todoMatchesAnyDescription(task.content, completedDescs)) return task;
|
|
1804
1841
|
mutated = true;
|
|
1805
|
-
|
|
1842
|
+
// Drop any blocker note along with the blocked status — the wait the
|
|
1843
|
+
// note described is over.
|
|
1844
|
+
return { content: task.content, status: "completed" as const };
|
|
1806
1845
|
}),
|
|
1807
1846
|
}));
|
|
1808
1847
|
if (!mutated) return;
|
|
@@ -2058,6 +2097,29 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2058
2097
|
this.ui.requestRender();
|
|
2059
2098
|
}
|
|
2060
2099
|
|
|
2100
|
+
#vibeParentSession(): VibeParentSession {
|
|
2101
|
+
return {
|
|
2102
|
+
getAgentId: () => this.session.getAgentId() ?? null,
|
|
2103
|
+
getSessionId: () => this.sessionManager.getSessionId(),
|
|
2104
|
+
getSessionFile: () => this.sessionManager.getSessionFile() ?? null,
|
|
2105
|
+
sessionManager: this.sessionManager,
|
|
2106
|
+
asyncJobManager: this.session.asyncJobManager,
|
|
2107
|
+
settings: this.session.settings,
|
|
2108
|
+
// Resolve restored/switched-to workers against this session's active model
|
|
2109
|
+
// (same as the spawn-path ToolSession), not the settings default. This is
|
|
2110
|
+
// the primary fallback in resolveAgentModelPatterns, so the `good` worker's
|
|
2111
|
+
// pi/task inheritance tracks the reopened session's model.
|
|
2112
|
+
getActiveModelString: () => (this.session.model ? formatModelString(this.session.model) : undefined),
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
async #quiesceVibeForSessionSwitch(): Promise<void> {
|
|
2117
|
+
const ownerScope = this.#vibeModeOwnerScope;
|
|
2118
|
+
if (!this.vibeModeEnabled || !ownerScope) return;
|
|
2119
|
+
await VibeSessionRegistry.global().suspendScope(ownerScope, this.session.asyncJobManager);
|
|
2120
|
+
this.#vibeScopeSuspendedForSwitch = true;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2061
2123
|
#updateGoalModeStatus(): void {
|
|
2062
2124
|
const status =
|
|
2063
2125
|
this.goalModeEnabled || this.goalModePaused
|
|
@@ -2241,7 +2303,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2241
2303
|
}
|
|
2242
2304
|
}
|
|
2243
2305
|
|
|
2244
|
-
async #clearTransientModeState(
|
|
2306
|
+
async #clearTransientModeState(options?: {
|
|
2307
|
+
preserveVibe?: boolean;
|
|
2308
|
+
vibeScopeAlreadySuspended?: boolean;
|
|
2309
|
+
}): Promise<void> {
|
|
2245
2310
|
if (this.planModeEnabled || this.planModePaused) {
|
|
2246
2311
|
this.session.setPlanModeState(undefined);
|
|
2247
2312
|
try {
|
|
@@ -2277,23 +2342,40 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2277
2342
|
this.#updateGoalModeStatus();
|
|
2278
2343
|
}
|
|
2279
2344
|
|
|
2280
|
-
if (this.vibeModeEnabled) {
|
|
2281
|
-
|
|
2345
|
+
if (this.vibeModeEnabled && !options?.preserveVibe) {
|
|
2346
|
+
const ownerScope = this.#vibeModeOwnerScope;
|
|
2347
|
+
// This runs only from #reconcileModeFromSession, i.e. after switchSession
|
|
2348
|
+
// already loaded and restored the target session's active tools. The
|
|
2349
|
+
// #vibeModePreviousTools snapshot belongs to the SOURCE session, so
|
|
2350
|
+
// applying it here would clobber the target's tools — strip only the
|
|
2351
|
+
// transient vibe tools and keep the target's active set intact.
|
|
2352
|
+
await this.session.removeVibeToolsPreservingActive();
|
|
2282
2353
|
this.session.setVibeModeState(undefined);
|
|
2283
2354
|
this.vibeModeEnabled = false;
|
|
2284
2355
|
this.#vibeModePreviousTools = undefined;
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
this.session.asyncJobManager
|
|
2288
|
-
|
|
2356
|
+
this.#vibeModeOwnerScope = undefined;
|
|
2357
|
+
if (ownerScope && !options?.vibeScopeAlreadySuspended) {
|
|
2358
|
+
await VibeSessionRegistry.global().suspendScope(ownerScope, this.session.asyncJobManager);
|
|
2359
|
+
}
|
|
2289
2360
|
this.#updateVibeModeStatus();
|
|
2290
2361
|
}
|
|
2291
2362
|
}
|
|
2292
2363
|
|
|
2293
2364
|
/** Reconcile mode state from session entries on resume/switch. */
|
|
2294
2365
|
async #reconcileModeFromSession(options?: { preserveActiveGoal?: boolean }): Promise<void> {
|
|
2295
|
-
|
|
2366
|
+
const vibeScopeAlreadySuspended = this.#vibeScopeSuspendedForSwitch;
|
|
2367
|
+
this.#vibeScopeSuspendedForSwitch = false;
|
|
2296
2368
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2369
|
+
const vibeSession = this.#vibeParentSession();
|
|
2370
|
+
const targetVibeScope = VibeSessionRegistry.global().ownerScope(vibeSession);
|
|
2371
|
+
const preserveVibe =
|
|
2372
|
+
this.vibeModeEnabled &&
|
|
2373
|
+
sessionContext.mode === "vibe" &&
|
|
2374
|
+
this.#vibeModeOwnerScope?.ownerId === targetVibeScope.ownerId &&
|
|
2375
|
+
this.#vibeModeOwnerScope.parentSessionId === targetVibeScope.parentSessionId &&
|
|
2376
|
+
this.#vibeModeOwnerScope.parentSessionFile === targetVibeScope.parentSessionFile;
|
|
2377
|
+
await this.#clearTransientModeState({ preserveVibe, vibeScopeAlreadySuspended });
|
|
2378
|
+
await VibeSessionRegistry.global().rehydrate(vibeSession);
|
|
2297
2379
|
const goalEnabled = this.session.settings.get("goal.enabled");
|
|
2298
2380
|
if (!goalEnabled && (sessionContext.mode === "goal" || sessionContext.mode === "goal_paused")) {
|
|
2299
2381
|
this.session.goalRuntime.clearAccounting();
|
|
@@ -2328,7 +2410,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2328
2410
|
}
|
|
2329
2411
|
this.session.goalRuntime.clearAccounting();
|
|
2330
2412
|
if (sessionContext.mode === "vibe") {
|
|
2331
|
-
await this.#enterVibeMode();
|
|
2413
|
+
if (!preserveVibe) await this.#enterVibeMode({ persistModeChange: false });
|
|
2332
2414
|
return;
|
|
2333
2415
|
}
|
|
2334
2416
|
if (!this.session.settings.get("plan.enabled")) {
|
|
@@ -2646,6 +2728,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2646
2728
|
onExternalEditor?: () => void;
|
|
2647
2729
|
onPlanEdited?: (content: string) => void;
|
|
2648
2730
|
onFeedbackChange?: (feedback: string) => void;
|
|
2731
|
+
annotationState?: PlanReviewAnnotationState;
|
|
2732
|
+
onAnnotationStateChange?: (state: PlanReviewAnnotationState) => void;
|
|
2649
2733
|
initialIndex?: number;
|
|
2650
2734
|
},
|
|
2651
2735
|
extra?: { slider?: HookSelectorSlider },
|
|
@@ -2669,6 +2753,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2669
2753
|
initialIndex: dialogOptions?.initialIndex,
|
|
2670
2754
|
slider: extra?.slider,
|
|
2671
2755
|
externalEditorLabel: this.keybindings.getDisplayString("app.editor.external") || undefined,
|
|
2756
|
+
annotationState: dialogOptions?.annotationState,
|
|
2672
2757
|
},
|
|
2673
2758
|
{
|
|
2674
2759
|
onPick: choice => finish(choice),
|
|
@@ -2678,6 +2763,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2678
2763
|
onAnnotationExternalEditor: (draft, commit) => void this.#openPlanAnnotationInExternalEditor(draft, commit),
|
|
2679
2764
|
onPlanEdited: dialogOptions?.onPlanEdited,
|
|
2680
2765
|
onFeedbackChange: dialogOptions?.onFeedbackChange,
|
|
2766
|
+
onAnnotationStateChange: dialogOptions?.onAnnotationStateChange,
|
|
2681
2767
|
},
|
|
2682
2768
|
);
|
|
2683
2769
|
this.#planReviewOverlay = overlay;
|
|
@@ -2907,7 +2993,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2907
2993
|
compactBeforeExecute?: boolean;
|
|
2908
2994
|
executionModel?: ResolvedRoleModel;
|
|
2909
2995
|
},
|
|
2910
|
-
): Promise<
|
|
2996
|
+
): Promise<boolean> {
|
|
2911
2997
|
const previousTools = this.#planModePreviousTools ?? this.session.getEnabledToolNames();
|
|
2912
2998
|
|
|
2913
2999
|
// Mark the pending abort caused by the plan-mode → compaction transition as
|
|
@@ -3006,7 +3092,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3006
3092
|
this.showWarning(
|
|
3007
3093
|
"Plan approved, but compaction was cancelled — execution not dispatched. Submit a turn to continue.",
|
|
3008
3094
|
);
|
|
3009
|
-
return;
|
|
3095
|
+
return false;
|
|
3010
3096
|
}
|
|
3011
3097
|
|
|
3012
3098
|
// Approved plans land in a fresh (or compacted) session whose first user-visible
|
|
@@ -3048,14 +3134,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3048
3134
|
// noted below), catch `AgentBusyError` and fall back to the same queue.
|
|
3049
3135
|
if (this.session.isStreaming) {
|
|
3050
3136
|
await this.session.followUp(planModePrompt, undefined, { synthetic: true });
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3137
|
+
} else {
|
|
3138
|
+
try {
|
|
3139
|
+
await this.session.prompt(planModePrompt, { synthetic: true });
|
|
3140
|
+
} catch (error) {
|
|
3141
|
+
if (!(error instanceof AgentBusyError)) throw error;
|
|
3142
|
+
await this.session.followUp(planModePrompt, undefined, { synthetic: true });
|
|
3143
|
+
}
|
|
3058
3144
|
}
|
|
3145
|
+
return true;
|
|
3059
3146
|
}
|
|
3060
3147
|
async #abortPlanApprovalTurnSilently(): Promise<void> {
|
|
3061
3148
|
this.session.markPlanInternalAbortPending();
|
|
@@ -3135,7 +3222,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3135
3222
|
}
|
|
3136
3223
|
}
|
|
3137
3224
|
|
|
3138
|
-
async #enterVibeMode(): Promise<void> {
|
|
3225
|
+
async #enterVibeMode(options?: { persistModeChange?: boolean }): Promise<void> {
|
|
3139
3226
|
if (this.vibeModeEnabled) {
|
|
3140
3227
|
return;
|
|
3141
3228
|
}
|
|
@@ -3148,9 +3235,13 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3148
3235
|
return;
|
|
3149
3236
|
}
|
|
3150
3237
|
|
|
3238
|
+
const vibeRegistry = VibeSessionRegistry.global();
|
|
3239
|
+
const ownerScope = vibeRegistry.ownerScope(this.#vibeParentSession());
|
|
3240
|
+
vibeRegistry.activateScope(ownerScope);
|
|
3151
3241
|
const previousTools = this.session.getEnabledToolNames();
|
|
3152
3242
|
await this.session.activateVibeTools(["read"]);
|
|
3153
3243
|
this.#vibeModePreviousTools = previousTools;
|
|
3244
|
+
this.#vibeModeOwnerScope = ownerScope;
|
|
3154
3245
|
this.vibeModeEnabled = true;
|
|
3155
3246
|
// Suppress cache-miss marker on the next turn: vibe mode changes the
|
|
3156
3247
|
// injected context, which predictably invalidates the cache.
|
|
@@ -3160,7 +3251,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3160
3251
|
await this.session.sendVibeModeContext({ deliverAs: "steer" });
|
|
3161
3252
|
}
|
|
3162
3253
|
this.#updateVibeModeStatus();
|
|
3163
|
-
this.sessionManager.appendModeChange("vibe");
|
|
3254
|
+
if (options?.persistModeChange !== false) this.sessionManager.appendModeChange("vibe");
|
|
3164
3255
|
this.showStatus("Vibe mode enabled. You direct fast/good worker sessions; toolset is read + vibe tools.");
|
|
3165
3256
|
}
|
|
3166
3257
|
|
|
@@ -3168,17 +3259,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3168
3259
|
if (!this.vibeModeEnabled) {
|
|
3169
3260
|
return;
|
|
3170
3261
|
}
|
|
3262
|
+
const ownerScope = this.#vibeModeOwnerScope;
|
|
3263
|
+
const killed = await VibeSessionRegistry.global().killAll(this.#vibeParentSession(), ownerScope);
|
|
3171
3264
|
await this.session.deactivateVibeTools(this.#vibeModePreviousTools ?? []);
|
|
3172
3265
|
this.session.setVibeModeState(undefined);
|
|
3173
3266
|
this.vibeModeEnabled = false;
|
|
3174
3267
|
this.#vibeModePreviousTools = undefined;
|
|
3268
|
+
this.#vibeModeOwnerScope = undefined;
|
|
3175
3269
|
this.lastAssistantUsage = undefined;
|
|
3176
|
-
const killed = await VibeSessionRegistry.global().killAll(
|
|
3177
|
-
this.session.getAgentId() ?? MAIN_AGENT_ID,
|
|
3178
|
-
this.session.asyncJobManager,
|
|
3179
|
-
);
|
|
3180
3270
|
this.#updateVibeModeStatus();
|
|
3181
|
-
this.sessionManager.appendModeChange("none");
|
|
3182
3271
|
this.showStatus(
|
|
3183
3272
|
killed > 0
|
|
3184
3273
|
? `Vibe mode disabled. Killed ${killed} worker session${killed === 1 ? "" : "s"}.`
|
|
@@ -3582,6 +3671,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3582
3671
|
// that the Refine branch re-prompts the model with.
|
|
3583
3672
|
let editedContent: string | undefined;
|
|
3584
3673
|
let feedback = "";
|
|
3674
|
+
const annotationStateKey = this.#resolvePlanFilePath(planFilePath);
|
|
3585
3675
|
|
|
3586
3676
|
const choice = await this.showPlanReview(
|
|
3587
3677
|
planContent,
|
|
@@ -3597,6 +3687,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3597
3687
|
onFeedbackChange: value => {
|
|
3598
3688
|
feedback = value;
|
|
3599
3689
|
},
|
|
3690
|
+
annotationState: this.#planReviewAnnotationState.get(annotationStateKey),
|
|
3691
|
+
onAnnotationStateChange: state => {
|
|
3692
|
+
if (state.annotations.length > 0) this.#planReviewAnnotationState.set(annotationStateKey, state);
|
|
3693
|
+
else this.#planReviewAnnotationState.delete(annotationStateKey);
|
|
3694
|
+
},
|
|
3600
3695
|
disabledIndices: keepContextDisabled ? [PLAN_KEEP_CONTEXT_OPTION_INDEX] : undefined,
|
|
3601
3696
|
},
|
|
3602
3697
|
{ slider },
|
|
@@ -3648,13 +3743,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3648
3743
|
: -1;
|
|
3649
3744
|
const executionModel =
|
|
3650
3745
|
slider && cycle && selectedTierIndex !== restoredIndex ? cycle.models[selectedTierIndex] : undefined;
|
|
3651
|
-
await this.#approvePlan(latestPlanContent, {
|
|
3746
|
+
const executionDispatched = await this.#approvePlan(latestPlanContent, {
|
|
3652
3747
|
planFilePath,
|
|
3653
3748
|
title: details.title,
|
|
3654
3749
|
preserveContext: choice !== "Approve and execute",
|
|
3655
3750
|
compactBeforeExecute: choice === "Approve and compact context",
|
|
3656
3751
|
executionModel,
|
|
3657
3752
|
});
|
|
3753
|
+
if (executionDispatched) this.#planReviewAnnotationState.delete(annotationStateKey);
|
|
3658
3754
|
} catch (error) {
|
|
3659
3755
|
this.showError(
|
|
3660
3756
|
`Failed to finalize approved plan: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3669,9 +3765,12 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3669
3765
|
try {
|
|
3670
3766
|
if (refinement) {
|
|
3671
3767
|
if (this.onInputCallback) {
|
|
3672
|
-
this.
|
|
3768
|
+
const input = this.startPendingSubmission({ text: feedback });
|
|
3769
|
+
this.#planReviewAnnotationStateBySubmission.set(input, annotationStateKey);
|
|
3770
|
+
this.onInputCallback(input);
|
|
3673
3771
|
} else {
|
|
3674
3772
|
await this.session.prompt(feedback);
|
|
3773
|
+
this.#planReviewAnnotationState.delete(annotationStateKey);
|
|
3675
3774
|
}
|
|
3676
3775
|
} else {
|
|
3677
3776
|
this.showStatus("Refine plan: enter a follow-up prompt.");
|
|
@@ -3746,6 +3845,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3746
3845
|
this.#stopLoadingAnimation(false);
|
|
3747
3846
|
}
|
|
3748
3847
|
this.#cleanupMicAnimation();
|
|
3848
|
+
this.#liveCommandController.dispose();
|
|
3749
3849
|
this.#cancelTodoAutoClearTimer();
|
|
3750
3850
|
this.#cancelObserverUiSyncTimer();
|
|
3751
3851
|
this.#cancelGoalContinuation();
|
|
@@ -3788,6 +3888,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3788
3888
|
if (this.#isShuttingDown) return;
|
|
3789
3889
|
this.#isShuttingDown = true;
|
|
3790
3890
|
|
|
3891
|
+
await this.#liveCommandController.stop();
|
|
3892
|
+
|
|
3791
3893
|
this.#btwController.dispose();
|
|
3792
3894
|
this.#omfgController.dispose();
|
|
3793
3895
|
this.#focusController.dispose();
|
|
@@ -3825,6 +3927,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3825
3927
|
// Drain any in-flight Kitty key release events before stopping.
|
|
3826
3928
|
// This prevents escape sequences from leaking to the parent shell over slow SSH.
|
|
3827
3929
|
await this.ui.terminal.drainInput(1000);
|
|
3930
|
+
// Stop the run-state spinner interval BEFORE restoring the shell title, so a
|
|
3931
|
+
// pending tick cannot re-emit an OSC title after `popTerminalTitle` hands the
|
|
3932
|
+
// terminal back (which would leave the parent shell with a `π ⠋ …` tab).
|
|
3933
|
+
disposeTerminalTitleState();
|
|
3828
3934
|
popTerminalTitle();
|
|
3829
3935
|
this.stop();
|
|
3830
3936
|
|
|
@@ -4297,6 +4403,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
4297
4403
|
}
|
|
4298
4404
|
|
|
4299
4405
|
async handleSTTToggle(): Promise<void> {
|
|
4406
|
+
if (this.#liveCommandController.active) {
|
|
4407
|
+
this.showWarning("End live mode before using push-to-talk speech input.");
|
|
4408
|
+
return;
|
|
4409
|
+
}
|
|
4300
4410
|
if (!settings.get("stt.enabled")) {
|
|
4301
4411
|
this.showWarning("Speech-to-text is disabled. Enable it in settings: stt.enabled");
|
|
4302
4412
|
return;
|
|
@@ -4329,6 +4439,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
4329
4439
|
});
|
|
4330
4440
|
}
|
|
4331
4441
|
|
|
4442
|
+
/** Start or stop the Codex-backed realtime voice surface. */
|
|
4443
|
+
async handleLiveCommand(): Promise<void> {
|
|
4444
|
+
if (this.#sttController && this.#sttController.state !== "idle") {
|
|
4445
|
+
this.showWarning("Finish the current speech-to-text capture before starting live mode.");
|
|
4446
|
+
return;
|
|
4447
|
+
}
|
|
4448
|
+
await this.#liveCommandController.handleCommand();
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4332
4451
|
#setMicCursor(color: { r: number; g: number; b: number }): void {
|
|
4333
4452
|
this.editor.cursorOverride = `\x1b[38;2;${color.r};${color.g};${color.b}m${theme.icon.mic}\x1b[0m`;
|
|
4334
4453
|
// Theme symbols can be wide (for example, 🎤), so measure the rendered override.
|
|
@@ -90,6 +90,8 @@ export async function initializeExtensions(session: AgentSession, options: Initi
|
|
|
90
90
|
setModel: model => runExtensionSetModel(session, model),
|
|
91
91
|
getThinkingLevel: () => session.thinkingLevel,
|
|
92
92
|
setThinkingLevel: level => session.setThinkingLevel(level),
|
|
93
|
+
getServiceTiers: () => session.serviceTierByFamily,
|
|
94
|
+
setServiceTier: (family, tier) => session.setServiceTierFamily(family, tier),
|
|
93
95
|
getSessionName: () => session.sessionManager.getSessionName(),
|
|
94
96
|
setSessionName: async name => {
|
|
95
97
|
await session.sessionManager.setSessionName(name, "user");
|
|
@@ -5,16 +5,20 @@ import {
|
|
|
5
5
|
type SgrMouseEvent,
|
|
6
6
|
truncateToWidth,
|
|
7
7
|
} from "@oh-my-pi/pi-tui";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import { getSearchProvider, setSearchProviderOrder } from "../../../web/search/provider";
|
|
9
|
+
import {
|
|
10
|
+
isSearchProviderId,
|
|
11
|
+
SEARCH_PROVIDER_OPTIONS,
|
|
12
|
+
SEARCH_PROVIDER_ORDER,
|
|
13
|
+
type SearchProviderId,
|
|
14
|
+
} from "../../../web/search/types";
|
|
11
15
|
import { getSelectListTheme, theme } from "../../theme/theme";
|
|
12
16
|
import type { SetupSceneHost, SetupTab } from "./types";
|
|
13
17
|
|
|
14
18
|
const MAX_VISIBLE = 8;
|
|
15
19
|
|
|
16
|
-
/** Reuse the
|
|
17
|
-
const WEB_SEARCH_ITEMS: readonly SelectItem[] =
|
|
20
|
+
/** Reuse the shared provider options as the single source of truth for labels/descriptions. */
|
|
21
|
+
const WEB_SEARCH_ITEMS: readonly SelectItem[] = SEARCH_PROVIDER_OPTIONS.map(option => ({
|
|
18
22
|
value: option.value,
|
|
19
23
|
label: option.label,
|
|
20
24
|
description: option.description,
|
|
@@ -42,7 +46,8 @@ export class WebSearchTab implements SetupTab {
|
|
|
42
46
|
|
|
43
47
|
constructor(private readonly host: SetupSceneHost) {
|
|
44
48
|
this.#list = new SelectList(WEB_SEARCH_ITEMS, MAX_VISIBLE, getSelectListTheme());
|
|
45
|
-
const
|
|
49
|
+
const order = host.ctx.settings.get("providers.webSearchOrder");
|
|
50
|
+
const current = Array.isArray(order) && typeof order[0] === "string" ? order[0] : "auto";
|
|
46
51
|
const index = WEB_SEARCH_ITEMS.findIndex(item => item.value === current);
|
|
47
52
|
if (index >= 0) this.#list.setSelectedIndex(index);
|
|
48
53
|
this.#list.onSelectionChange = item => this.#onHighlight(item.value);
|
|
@@ -119,9 +124,12 @@ export class WebSearchTab implements SetupTab {
|
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
#apply(value: string): void {
|
|
122
|
-
if (!
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
if (value !== "auto" && !isSearchProviderId(value)) return;
|
|
128
|
+
// The wizard picks one favorite; persist it as the head of the priority
|
|
129
|
+
// list with the remaining providers in their built-in order (auto = reset).
|
|
130
|
+
const order = value === "auto" ? [] : [value, ...SEARCH_PROVIDER_ORDER.filter(id => id !== value)];
|
|
131
|
+
this.host.ctx.settings.set("providers.webSearchOrder", order);
|
|
132
|
+
setSearchProviderOrder(order);
|
|
125
133
|
const label = WEB_SEARCH_ITEMS.find(item => item.value === value)?.label ?? value;
|
|
126
134
|
this.#status = [theme.fg("success", `${theme.status.success} Web search set to ${label}`)];
|
|
127
135
|
if (value !== "auto" && this.#availability.get(value as SearchProviderId) === false) {
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { defaultThemes } from "./defaults";
|
|
|
20
20
|
import lightThemeJson from "./light.json" with { type: "json" };
|
|
21
21
|
import { resolveMermaidAscii } from "./mermaid-cache";
|
|
22
22
|
|
|
23
|
-
export { getLanguageFromPath } from "../../utils/lang-from-path";
|
|
23
|
+
export { getLanguageFromPath, isMarkdownPath } from "../../utils/lang-from-path";
|
|
24
24
|
|
|
25
25
|
// ============================================================================
|
|
26
26
|
// Symbol Presets
|
package/src/modes/types.ts
CHANGED
|
@@ -71,7 +71,7 @@ export type SubmittedUserInput = {
|
|
|
71
71
|
started: boolean;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
export type TodoStatus = "pending" | "in_progress" | "completed" | "abandoned";
|
|
74
|
+
export type TodoStatus = "pending" | "in_progress" | "completed" | "abandoned" | "blocked";
|
|
75
75
|
|
|
76
76
|
export type TodoItem = {
|
|
77
77
|
content: string;
|
|
@@ -356,6 +356,8 @@ export interface InteractiveModeContext {
|
|
|
356
356
|
handleRenameCommand(title: string): Promise<void>;
|
|
357
357
|
handleMemoryCommand(text: string): Promise<void>;
|
|
358
358
|
handleSTTToggle(): Promise<void>;
|
|
359
|
+
/** Start or stop the Codex-backed realtime voice session. */
|
|
360
|
+
handleLiveCommand(): Promise<void>;
|
|
359
361
|
executeCompaction(
|
|
360
362
|
customInstructionsOrOptions?: string | CompactOptions,
|
|
361
363
|
isAuto?: boolean,
|
|
@@ -41,7 +41,18 @@ export interface ContextBreakdown {
|
|
|
41
41
|
snapcompact?: SnapcompactSavingsEstimate;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
/** Stable inputs used to cache non-message token estimates. */
|
|
45
|
+
export interface NonMessageTokenSource {
|
|
46
|
+
readonly systemPrompt?: string[];
|
|
47
|
+
readonly agent?: {
|
|
48
|
+
readonly state?: {
|
|
49
|
+
readonly tools?: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">>;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
readonly skills?: readonly Skill[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const EMPTY_STRING_PARTS: string[] = [];
|
|
45
56
|
const EMPTY_TOOLS: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">> = [];
|
|
46
57
|
const EMPTY_SKILLS: readonly Skill[] = [];
|
|
47
58
|
|
|
@@ -111,13 +122,18 @@ interface NonMessageTokenCache {
|
|
|
111
122
|
| undefined;
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
const
|
|
125
|
+
const NON_MESSAGE_TOKEN_CACHE = Symbol("non-message-token-cache");
|
|
126
|
+
|
|
127
|
+
interface CachedNonMessageTokenSource extends NonMessageTokenSource {
|
|
128
|
+
[NON_MESSAGE_TOKEN_CACHE]?: NonMessageTokenCache;
|
|
129
|
+
}
|
|
115
130
|
|
|
116
|
-
function nonMessageTokenCacheEntry(session:
|
|
131
|
+
function nonMessageTokenCacheEntry(session: NonMessageTokenSource): NonMessageTokenCache {
|
|
132
|
+
const cachedSession: CachedNonMessageTokenSource = session;
|
|
117
133
|
const systemPromptRef = session.systemPrompt ?? EMPTY_STRING_PARTS;
|
|
118
134
|
const toolsRef = session.agent?.state?.tools ?? EMPTY_TOOLS;
|
|
119
135
|
const skillsRef = session.skills ?? EMPTY_SKILLS;
|
|
120
|
-
let entry =
|
|
136
|
+
let entry = cachedSession[NON_MESSAGE_TOKEN_CACHE];
|
|
121
137
|
if (
|
|
122
138
|
entry &&
|
|
123
139
|
entry.systemPromptRef === systemPromptRef &&
|
|
@@ -127,11 +143,11 @@ function nonMessageTokenCacheEntry(session: AgentSession): NonMessageTokenCache
|
|
|
127
143
|
return entry;
|
|
128
144
|
}
|
|
129
145
|
entry = { systemPromptRef, toolsRef, skillsRef, tokens: undefined, breakdown: undefined };
|
|
130
|
-
|
|
146
|
+
cachedSession[NON_MESSAGE_TOKEN_CACHE] = entry;
|
|
131
147
|
return entry;
|
|
132
148
|
}
|
|
133
149
|
|
|
134
|
-
export function computeNonMessageTokens(session:
|
|
150
|
+
export function computeNonMessageTokens(session: NonMessageTokenSource): number {
|
|
135
151
|
const entry = nonMessageTokenCacheEntry(session);
|
|
136
152
|
if (entry.tokens !== undefined) return entry.tokens;
|
|
137
153
|
const systemPromptParts = session.systemPrompt ?? EMPTY_STRING_PARTS;
|
|
@@ -147,7 +163,7 @@ export function computeNonMessageTokens(session: AgentSession): number {
|
|
|
147
163
|
* the status-line fast path intentionally uses the equivalent collapsed total
|
|
148
164
|
* in `computeNonMessageTokens`.
|
|
149
165
|
*/
|
|
150
|
-
export function computeNonMessageBreakdown(session:
|
|
166
|
+
export function computeNonMessageBreakdown(session: NonMessageTokenSource): {
|
|
151
167
|
skillsTokens: number;
|
|
152
168
|
toolsTokens: number;
|
|
153
169
|
systemContextTokens: number;
|