@oh-my-pi/pi-coding-agent 15.12.3 → 15.13.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 +347 -7
- package/dist/cli.js +1615 -1231
- package/dist/types/async/job-manager.d.ts +15 -0
- package/dist/types/autolearn/controller.d.ts +25 -0
- package/dist/types/autolearn/managed-skills.d.ts +45 -0
- package/dist/types/autoresearch/state.d.ts +1 -1
- package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/run-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/update-notes.d.ts +1 -1
- package/dist/types/autoresearch/types.d.ts +1 -1
- package/dist/types/cli/args.d.ts +19 -2
- package/dist/types/cli/models-cli.d.ts +49 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/cli/setup-model-picker.d.ts +14 -0
- package/dist/types/collab/protocol.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +0 -3
- package/dist/types/commands/models.d.ts +33 -0
- package/dist/types/commands/say.d.ts +24 -0
- package/dist/types/commands/token.d.ts +25 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-overview.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +1 -1
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +1 -1
- package/dist/types/commit/agentic/tools/schemas.d.ts +1 -1
- package/dist/types/commit/agentic/tools/split-commit.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +3 -3
- package/dist/types/config/model-registry.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +13 -1
- package/dist/types/config/models-config.d.ts +8 -2
- package/dist/types/config/settings-schema.d.ts +281 -58
- package/dist/types/edit/hashline/params.d.ts +1 -1
- package/dist/types/edit/modes/apply-patch.d.ts +1 -1
- package/dist/types/edit/modes/patch.d.ts +1 -1
- package/dist/types/edit/modes/replace.d.ts +1 -1
- package/dist/types/export/html/index.d.ts +2 -1
- package/dist/types/extensibility/custom-commands/types.d.ts +2 -2
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/model-api.d.ts +17 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -1
- package/dist/types/extensibility/extensions/types.d.ts +49 -3
- package/dist/types/extensibility/hooks/index.d.ts +2 -1
- package/dist/types/extensibility/hooks/types.d.ts +2 -2
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +9 -0
- package/dist/types/extensibility/plugins/loader.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +10 -0
- package/dist/types/goals/guided-setup.d.ts +18 -0
- package/dist/types/goals/state.d.ts +1 -1
- package/dist/types/goals/tools/goal-tool.d.ts +1 -1
- package/dist/types/hindsight/transcript.d.ts +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/internal-urls/local-protocol.d.ts +4 -2
- package/dist/types/lsp/types.d.ts +1 -1
- package/dist/types/main.d.ts +4 -3
- package/dist/types/mcp/manager.d.ts +8 -0
- package/dist/types/mcp/startup-events.d.ts +11 -0
- package/dist/types/memories/index.d.ts +7 -0
- package/dist/types/memory-backend/local-backend.d.ts +4 -3
- package/dist/types/mnemopi/config.d.ts +28 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -2
- package/dist/types/modes/components/agent-hub.d.ts +6 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/compaction-summary-message.d.ts +15 -1
- package/dist/types/modes/components/custom-editor.d.ts +39 -1
- package/dist/types/modes/components/custom-editor.test.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/logout-account-selector.d.ts +8 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +9 -5
- package/dist/types/modes/components/status-line/types.d.ts +2 -1
- package/dist/types/modes/components/tool-execution.d.ts +26 -16
- package/dist/types/modes/components/transcript-container.d.ts +23 -2
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/usage-row.d.ts +3 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/event-controller.d.ts +0 -17
- package/dist/types/modes/controllers/input-controller.d.ts +14 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/gradient-highlight.d.ts +9 -4
- package/dist/types/modes/image-references.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +27 -6
- package/dist/types/modes/magic-keywords.d.ts +13 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +35 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +9 -1
- package/dist/types/modes/runtime-init.d.ts +4 -0
- package/dist/types/modes/theme/theme.d.ts +13 -2
- package/dist/types/modes/types.d.ts +8 -7
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-registry.d.ts +17 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +28 -35
- package/dist/types/session/agent-storage.d.ts +2 -1
- package/dist/types/session/indexed-session-storage.d.ts +3 -3
- package/dist/types/session/messages.d.ts +8 -10
- package/dist/types/session/session-context.d.ts +39 -0
- package/dist/types/session/session-entries.d.ts +159 -0
- package/dist/types/session/session-listing.d.ts +69 -0
- package/dist/types/session/session-loader.d.ts +16 -0
- package/dist/types/session/session-manager.d.ts +85 -462
- package/dist/types/session/session-migrations.d.ts +12 -0
- package/dist/types/session/session-paths.d.ts +25 -0
- package/dist/types/session/session-persistence.d.ts +8 -0
- package/dist/types/session/session-storage.d.ts +11 -7
- package/dist/types/session/snapcompact-inline.d.ts +12 -1
- package/dist/types/session/snapcompact-savings-journal.d.ts +46 -0
- package/dist/types/session/tool-choice-queue.d.ts +6 -6
- package/dist/types/slash-commands/helpers/logout.d.ts +15 -0
- package/dist/types/stt/asr-client.d.ts +90 -0
- package/dist/types/stt/asr-protocol.d.ts +97 -0
- package/dist/types/stt/asr-worker.d.ts +2 -0
- package/dist/types/stt/downloader.d.ts +38 -0
- package/dist/types/stt/endpointer.d.ts +59 -0
- package/dist/types/stt/index.d.ts +5 -1
- package/dist/types/stt/models.d.ts +120 -0
- package/dist/types/stt/recorder.d.ts +17 -0
- package/dist/types/stt/stt-controller.d.ts +6 -0
- package/dist/types/stt/transcriber.d.ts +5 -7
- package/dist/types/stt/wav.d.ts +29 -0
- package/dist/types/system-prompt.d.ts +4 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/index.d.ts +9 -1
- package/dist/types/task/types.d.ts +37 -1
- package/dist/types/tools/ask.d.ts +1 -1
- package/dist/types/tools/ast-edit.d.ts +1 -1
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +202 -0
- package/dist/types/tools/browser/cmux/rpc.d.ts +70 -0
- package/dist/types/tools/browser/cmux/socket-client.d.ts +19 -0
- package/dist/types/tools/browser/registry.d.ts +16 -3
- package/dist/types/tools/browser/render.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +16 -4
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/checkpoint.d.ts +1 -1
- package/dist/types/tools/debug.d.ts +1 -1
- package/dist/types/tools/eval-render.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/gh.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +1 -1
- package/dist/types/tools/index.d.ts +14 -2
- package/dist/types/tools/inspect-image.d.ts +1 -1
- package/dist/types/tools/irc.d.ts +2 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/learn.d.ts +51 -0
- package/dist/types/tools/manage-skill.d.ts +40 -0
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/memory-recall.d.ts +1 -1
- package/dist/types/tools/memory-reflect.d.ts +1 -1
- package/dist/types/tools/memory-retain.d.ts +1 -1
- package/dist/types/tools/plan-mode-guard.d.ts +10 -0
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/render-mermaid.d.ts +1 -1
- package/dist/types/tools/renderers.d.ts +7 -11
- package/dist/types/tools/resolve.d.ts +1 -1
- package/dist/types/tools/review.d.ts +1 -1
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/tools/ssh.d.ts +2 -2
- package/dist/types/tools/todo.d.ts +2 -2
- package/dist/types/tools/tts.d.ts +26 -1
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/tts/downloader.d.ts +20 -0
- package/dist/types/tts/index.d.ts +8 -0
- package/dist/types/tts/models.d.ts +82 -0
- package/dist/types/tts/player.d.ts +32 -0
- package/dist/types/tts/runtime.d.ts +6 -0
- package/dist/types/tts/streaming-player.d.ts +41 -0
- package/dist/types/tts/tts-client.d.ts +93 -0
- package/dist/types/tts/tts-protocol.d.ts +95 -0
- package/dist/types/tts/tts-worker.d.ts +2 -0
- package/dist/types/tts/vocalizer.d.ts +41 -0
- package/dist/types/tts/wav.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +4 -3
- package/dist/types/utils/image-loading.d.ts +18 -1
- package/dist/types/utils/thinking-display.d.ts +17 -0
- package/dist/types/utils/tool-choice.d.ts +8 -0
- package/dist/types/utils/tools-manager.d.ts +2 -1
- package/dist/types/utils/tools-manager.test.d.ts +1 -0
- package/dist/types/web/scrapers/github.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +1 -1
- package/package.json +17 -16
- package/src/async/job-manager.ts +49 -0
- package/src/autolearn/controller.ts +139 -0
- package/src/autolearn/managed-skills.ts +257 -0
- package/src/autoresearch/state.ts +1 -1
- package/src/autoresearch/storage.ts +2 -1
- package/src/autoresearch/tools/init-experiment.ts +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/autoresearch/tools/update-notes.ts +1 -1
- package/src/autoresearch/types.ts +1 -1
- package/src/cli/args.ts +56 -10
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/models-cli.ts +427 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +148 -47
- package/src/cli/setup-model-picker.ts +43 -0
- package/src/cli-commands.ts +3 -0
- package/src/cli.ts +45 -13
- package/src/collab/host.ts +10 -13
- package/src/collab/protocol.ts +1 -1
- package/src/commands/launch.ts +0 -3
- package/src/commands/models.ts +61 -0
- package/src/commands/say.ts +102 -0
- package/src/commands/setup.ts +1 -1
- package/src/commands/token.ts +89 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -1
- package/src/commit/agentic/tools/git-file-diff.ts +1 -1
- package/src/commit/agentic/tools/git-hunk.ts +1 -1
- package/src/commit/agentic/tools/git-overview.ts +1 -1
- package/src/commit/agentic/tools/propose-changelog.ts +1 -1
- package/src/commit/agentic/tools/propose-commit.ts +1 -1
- package/src/commit/agentic/tools/recent-commits.ts +1 -1
- package/src/commit/agentic/tools/schemas.ts +1 -1
- package/src/commit/agentic/tools/split-commit.ts +1 -1
- package/src/commit/analysis/summary.ts +1 -1
- package/src/commit/changelog/generate.ts +1 -1
- package/src/commit/shared-llm.ts +1 -1
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-discovery.ts +11 -5
- package/src/config/model-registry.ts +79 -21
- package/src/config/model-resolver.ts +2 -2
- package/src/config/models-config-schema.ts +5 -2
- package/src/config/models-config.ts +2 -1
- package/src/config/settings-schema.ts +266 -32
- package/src/config/settings.ts +10 -0
- package/src/discovery/builtin.ts +23 -1
- package/src/discovery/claude-plugins.ts +44 -5
- package/src/discovery/helpers.ts +41 -1
- package/src/edit/hashline/params.ts +1 -1
- package/src/edit/modes/apply-patch.ts +1 -1
- package/src/edit/modes/patch.ts +1 -1
- package/src/edit/modes/replace.ts +1 -1
- package/src/eval/__tests__/budget-bridge.test.ts +1 -1
- package/src/eval/agent-bridge.ts +1 -1
- package/src/eval/completion-bridge.ts +1 -1
- package/src/eval/js/shared/prelude.txt +69 -17
- package/src/export/html/index.ts +3 -6
- package/src/export/html/template.js +24 -2
- package/src/export/html/tool-views.generated.js +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -1
- package/src/extensibility/custom-commands/types.ts +2 -2
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +2 -2
- package/src/extensibility/extensions/model-api.ts +41 -0
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +54 -3
- package/src/extensibility/extensions/wrapper.ts +41 -5
- package/src/extensibility/hooks/index.ts +2 -1
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +43 -13
- package/src/extensibility/plugins/loader.ts +30 -19
- package/src/extensibility/plugins/manager.ts +221 -90
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +101 -5
- package/src/goals/guided-setup.ts +133 -0
- package/src/goals/state.ts +1 -1
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/hindsight/transcript.ts +1 -1
- package/src/index.ts +5 -0
- package/src/internal-urls/docs-index.generated.ts +13 -10
- package/src/internal-urls/history-protocol.ts +1 -1
- package/src/internal-urls/local-protocol.ts +29 -7
- package/src/lsp/types.ts +1 -1
- package/src/main.ts +27 -32
- package/src/mcp/config-writer.ts +7 -3
- package/src/mcp/manager.ts +11 -0
- package/src/mcp/startup-events.ts +21 -0
- package/src/mcp/transports/stdio.ts +2 -1
- package/src/memories/index.ts +149 -12
- package/src/memories/storage.ts +2 -1
- package/src/memory-backend/local-backend.ts +11 -5
- package/src/mnemopi/backend.ts +1 -0
- package/src/mnemopi/config.ts +112 -12
- package/src/modes/acp/acp-agent.ts +8 -53
- package/src/modes/acp/acp-event-mapper.ts +5 -1
- package/src/modes/components/agent-hub.ts +51 -5
- package/src/modes/components/assistant-message.ts +12 -44
- package/src/modes/components/compaction-summary-message.ts +125 -26
- package/src/modes/components/custom-editor.test.ts +96 -0
- package/src/modes/components/custom-editor.ts +164 -8
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +7 -0
- package/src/modes/components/status-line/component.ts +54 -157
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +2 -1
- package/src/modes/components/tool-execution.ts +82 -43
- package/src/modes/components/transcript-container.ts +70 -1
- package/src/modes/components/tree-selector.ts +1 -1
- package/src/modes/components/usage-row.ts +18 -0
- package/src/modes/components/user-message.ts +4 -2
- package/src/modes/controllers/command-controller.ts +14 -16
- package/src/modes/controllers/event-controller.ts +101 -73
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +311 -57
- package/src/modes/controllers/mcp-command-controller.ts +44 -3
- package/src/modes/controllers/selector-controller.ts +68 -12
- package/src/modes/controllers/streaming-reveal.ts +4 -3
- package/src/modes/gradient-highlight.ts +21 -9
- package/src/modes/image-references.ts +20 -0
- package/src/modes/interactive-mode.ts +288 -48
- package/src/modes/magic-keywords.ts +27 -5
- package/src/modes/rpc/rpc-mode.ts +146 -14
- package/src/modes/rpc/rpc-subagents.ts +2 -2
- package/src/modes/rpc/rpc-types.ts +8 -2
- package/src/modes/runtime-init.ts +28 -3
- package/src/modes/theme/theme.ts +99 -51
- package/src/modes/types.ts +6 -7
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/utils/ui-helpers.ts +36 -7
- package/src/priority.json +5 -1
- package/src/prompts/agents/task.md +1 -0
- package/src/prompts/goals/guided-goal-interview.md +8 -0
- package/src/prompts/goals/guided-goal-system.md +12 -0
- package/src/prompts/memories/read-path.md +6 -0
- package/src/prompts/system/autolearn-guidance-learn.md +1 -0
- package/src/prompts/system/autolearn-guidance.md +7 -0
- package/src/prompts/system/autolearn-nudge.md +3 -0
- package/src/prompts/system/eager-task.md +7 -0
- package/src/prompts/system/eager-todo.md +11 -6
- package/src/prompts/system/empty-stop-retry.md +4 -6
- package/src/prompts/system/subagent-system-prompt.md +4 -0
- package/src/prompts/system/system-prompt.md +10 -5
- package/src/prompts/system/title-marker-instruction.md +1 -0
- package/src/prompts/system/title-system-marker.md +16 -0
- package/src/prompts/tools/job.md +1 -0
- package/src/prompts/tools/learn.md +7 -0
- package/src/prompts/tools/manage-skill.md +9 -0
- package/src/prompts/tools/task.md +3 -0
- package/src/registry/agent-registry.ts +30 -0
- package/src/sdk.ts +103 -43
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +331 -318
- package/src/session/agent-storage.ts +18 -9
- package/src/session/history-storage.ts +3 -2
- package/src/session/indexed-session-storage.ts +7 -10
- package/src/session/messages.ts +9 -11
- package/src/session/session-context.ts +352 -0
- package/src/session/session-dump-format.ts +4 -2
- package/src/session/session-entries.ts +194 -0
- package/src/session/session-listing.ts +588 -0
- package/src/session/session-loader.ts +106 -0
- package/src/session/session-manager.ts +968 -3064
- package/src/session/session-migrations.ts +78 -0
- package/src/session/session-paths.ts +193 -0
- package/src/session/session-persistence.ts +131 -0
- package/src/session/session-storage.ts +91 -30
- package/src/session/snapcompact-inline.ts +21 -1
- package/src/session/snapcompact-savings-journal.ts +113 -0
- package/src/session/tool-choice-queue.ts +23 -11
- package/src/slash-commands/builtin-registry.ts +40 -4
- package/src/slash-commands/helpers/logout.ts +88 -0
- package/src/stt/asr-client.ts +520 -0
- package/src/stt/asr-protocol.ts +65 -0
- package/src/stt/asr-worker.ts +790 -0
- package/src/stt/downloader.ts +107 -47
- package/src/stt/endpointer.ts +259 -0
- package/src/stt/index.ts +5 -1
- package/src/stt/models.ts +150 -0
- package/src/stt/recorder.ts +247 -60
- package/src/stt/stt-controller.ts +201 -22
- package/src/stt/transcriber.ts +37 -68
- package/src/stt/wav.ts +173 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/agents.ts +1 -2
- package/src/task/executor.ts +49 -15
- package/src/task/index.ts +60 -6
- package/src/task/render.ts +83 -8
- package/src/task/types.ts +54 -1
- package/src/tools/ask.ts +9 -1
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/ast-grep.ts +1 -1
- package/src/tools/bash.ts +5 -4
- package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
- package/src/tools/browser/cmux/rpc.ts +156 -0
- package/src/tools/browser/cmux/socket-client.ts +309 -0
- package/src/tools/browser/registry.ts +37 -3
- package/src/tools/browser/render.ts +6 -1
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +189 -18
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +16 -1
- package/src/tools/checkpoint.ts +1 -1
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +4 -3
- package/src/tools/eval.ts +11 -6
- package/src/tools/fetch.ts +13 -2
- package/src/tools/find.ts +1 -1
- package/src/tools/gh.ts +1 -1
- package/src/tools/github-cache.ts +2 -1
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +43 -5
- package/src/tools/inspect-image.ts +3 -1
- package/src/tools/irc.ts +11 -3
- package/src/tools/job.ts +15 -3
- package/src/tools/learn.ts +144 -0
- package/src/tools/manage-skill.ts +104 -0
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-recall.ts +1 -1
- package/src/tools/memory-reflect.ts +1 -1
- package/src/tools/memory-retain.ts +1 -1
- package/src/tools/plan-mode-guard.ts +53 -19
- package/src/tools/read.ts +8 -2
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/renderers.ts +7 -11
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/search-tool-bm25.ts +1 -1
- package/src/tools/search.ts +1 -1
- package/src/tools/ssh.ts +5 -4
- package/src/tools/todo.ts +2 -2
- package/src/tools/tts.ts +204 -93
- package/src/tools/write.ts +19 -3
- package/src/tts/downloader.ts +64 -0
- package/src/tts/index.ts +8 -0
- package/src/tts/models.ts +137 -0
- package/src/tts/player.ts +137 -0
- package/src/tts/runtime.ts +21 -0
- package/src/tts/streaming-player.ts +266 -0
- package/src/tts/tts-client.ts +647 -0
- package/src/tts/tts-protocol.ts +60 -0
- package/src/tts/tts-worker.ts +497 -0
- package/src/tts/vocalizer.ts +162 -0
- package/src/tts/wav.ts +58 -0
- package/src/utils/clipboard.ts +35 -18
- package/src/utils/image-loading.ts +35 -4
- package/src/utils/thinking-display.ts +37 -0
- package/src/utils/title-generator.ts +48 -5
- package/src/utils/tool-choice.ts +16 -0
- package/src/utils/tools-manager.test.ts +25 -0
- package/src/utils/tools-manager.ts +19 -1
- package/src/web/scrapers/github.ts +96 -0
- package/src/web/search/index.ts +14 -1
- package/src/web/search/providers/searxng.ts +13 -1
- package/dist/types/cli/list-models.d.ts +0 -30
- package/dist/types/stt/setup.d.ts +0 -18
- package/src/cli/list-models.ts +0 -194
- package/src/stt/setup.ts +0 -52
- package/src/stt/transcribe.py +0 -70
|
@@ -64,10 +64,12 @@ import type {
|
|
|
64
64
|
} from "../extensibility/extensions";
|
|
65
65
|
import type { CompactOptions } from "../extensibility/extensions/types";
|
|
66
66
|
import { loadSlashCommands } from "../extensibility/slash-commands";
|
|
67
|
+
import { type GuidedGoalMessage, runGuidedGoalTurn } from "../goals/guided-setup";
|
|
67
68
|
import type { Goal, GoalModeState } from "../goals/state";
|
|
68
69
|
import { resolveLocalUrlToPath } from "../internal-urls";
|
|
69
70
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
|
|
70
71
|
import type { MCPManager } from "../mcp";
|
|
72
|
+
import { formatMCPConnectingMessage, isMcpConnectingEvent, MCP_CONNECTING_EVENT_CHANNEL } from "../mcp/startup-events";
|
|
71
73
|
import {
|
|
72
74
|
humanizePlanTitle,
|
|
73
75
|
type PlanApprovalDetails,
|
|
@@ -80,8 +82,9 @@ import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compa
|
|
|
80
82
|
};
|
|
81
83
|
import type { AgentSession, AgentSessionEvent, ResolvedRoleModel } from "../session/agent-session";
|
|
82
84
|
import { HistoryStorage } from "../session/history-storage";
|
|
83
|
-
import type { SessionContext
|
|
84
|
-
import { getRecentSessions } from "../session/session-
|
|
85
|
+
import type { SessionContext } from "../session/session-context";
|
|
86
|
+
import { getRecentSessions } from "../session/session-listing";
|
|
87
|
+
import type { SessionManager } from "../session/session-manager";
|
|
85
88
|
import type { ShakeMode } from "../session/shake-types";
|
|
86
89
|
import { BUILTIN_SLASH_COMMAND_RESERVED_NAMES, BUILTIN_SLASH_COMMANDS } from "../slash-commands/builtin-registry";
|
|
87
90
|
import { formatDuration } from "../slash-commands/helpers/format";
|
|
@@ -95,6 +98,7 @@ import { setAutoQaConsentHandler } from "../tools/report-tool-issue";
|
|
|
95
98
|
import { type ResolveToolDetails, runResolveInvocation } from "../tools/resolve";
|
|
96
99
|
import { formatPhaseDisplayName, selectStickyTodoWindow, todoMatchesAnyDescription } from "../tools/todo";
|
|
97
100
|
import { ToolError } from "../tools/tool-errors";
|
|
101
|
+
import { vocalizer } from "../tts/vocalizer";
|
|
98
102
|
import type { EventBus } from "../utils/event-bus";
|
|
99
103
|
import { getEditorCommand, openInEditor } from "../utils/external-editor";
|
|
100
104
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
|
|
@@ -210,6 +214,25 @@ const EDITOR_MAX_HEIGHT_MIN = 6;
|
|
|
210
214
|
const EDITOR_MAX_HEIGHT_MAX = 18;
|
|
211
215
|
const EDITOR_RESERVED_ROWS = 12;
|
|
212
216
|
const EDITOR_FALLBACK_ROWS = 24;
|
|
217
|
+
const EDITOR_MIN_CHROME_ROWS = 4; // rows reserved for transcript + status on small terms
|
|
218
|
+
const EDITOR_MIN_RENDERED_ROWS = 3; // bordered editor floor: top+bottom border + 1 content row
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Editor max-height cap for a terminal of `terminalRows` rows.
|
|
222
|
+
*
|
|
223
|
+
* Roomy terminals get the comfortable [6, 18] band. Small terminals shrink the
|
|
224
|
+
* cap so the editor leaves at least EDITOR_MIN_CHROME_ROWS rows for the
|
|
225
|
+
* transcript + status line. The editor is bordered, so it never renders fewer
|
|
226
|
+
* than EDITOR_MIN_RENDERED_ROWS rows; once the terminal is too small for both
|
|
227
|
+
* (terminalRows < EDITOR_MIN_RENDERED_ROWS + EDITOR_MIN_CHROME_ROWS) the cap is
|
|
228
|
+
* pinned to that floor — returning a smaller number would not shrink the editor
|
|
229
|
+
* any further, it would only misreport the rows it actually occupies.
|
|
230
|
+
*/
|
|
231
|
+
export function computeEditorMaxHeight(terminalRows: number): number {
|
|
232
|
+
const rows = Number.isFinite(terminalRows) && terminalRows > 0 ? terminalRows : EDITOR_FALLBACK_ROWS;
|
|
233
|
+
const comfortable = Math.max(EDITOR_MAX_HEIGHT_MIN, Math.min(EDITOR_MAX_HEIGHT_MAX, rows - EDITOR_RESERVED_ROWS));
|
|
234
|
+
return Math.max(EDITOR_MIN_RENDERED_ROWS, Math.min(comfortable, rows - EDITOR_MIN_CHROME_ROWS));
|
|
235
|
+
}
|
|
213
236
|
|
|
214
237
|
const HUD_NOTE_SUP_DIGITS: Record<string, string> = {
|
|
215
238
|
"0": "\u2070",
|
|
@@ -282,6 +305,10 @@ class StatusContainer extends Container implements NativeScrollbackLiveRegion {
|
|
|
282
305
|
}
|
|
283
306
|
}
|
|
284
307
|
|
|
308
|
+
/** How long the ctrl+p model-role cycle chip track lingers above the editor
|
|
309
|
+
* before it auto-clears, mirroring the todo HUD's auto-clear timer. */
|
|
310
|
+
const MODEL_CYCLE_TRACK_CLEAR_MS = 4000;
|
|
311
|
+
|
|
285
312
|
/**
|
|
286
313
|
* Build the anchored subagent HUD block: a bold accent "Subagents" header plus
|
|
287
314
|
* one hooked row per running agent in the same `Id: description` shape the
|
|
@@ -340,6 +367,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
340
367
|
btwContainer: Container;
|
|
341
368
|
omfgContainer: Container;
|
|
342
369
|
errorBannerContainer: Container;
|
|
370
|
+
modelCycleContainer: Container;
|
|
343
371
|
editor: CustomEditor;
|
|
344
372
|
editorContainer: Container;
|
|
345
373
|
hookWidgetContainerAbove: Container;
|
|
@@ -360,6 +388,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
360
388
|
loopLimit: LoopLimitRuntime | undefined = undefined;
|
|
361
389
|
#loopAutoSubmitTimer: NodeJS.Timeout | undefined;
|
|
362
390
|
#todoAutoClearTimer: NodeJS.Timeout | undefined;
|
|
391
|
+
#modelCycleClearTimer: NodeJS.Timeout | undefined;
|
|
363
392
|
todoPhases: TodoPhase[] = [];
|
|
364
393
|
hideThinkingBlock = false;
|
|
365
394
|
pendingImages: ImageContent[] = [];
|
|
@@ -383,8 +412,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
383
412
|
get #defaultWorkingMessage(): string {
|
|
384
413
|
return `Working…${interruptHint()}`;
|
|
385
414
|
}
|
|
386
|
-
autoCompactionEscapeHandler?: () => void;
|
|
387
|
-
retryEscapeHandler?: () => void;
|
|
388
415
|
unsubscribe?: () => void;
|
|
389
416
|
onInputCallback?: (input: SubmittedUserInput) => void;
|
|
390
417
|
optimisticUserMessageSignature: string | undefined = undefined;
|
|
@@ -464,6 +491,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
464
491
|
}
|
|
465
492
|
this.statusContainer.clear();
|
|
466
493
|
this.pendingMessagesContainer.clear();
|
|
494
|
+
this.#cancelModelCycleClearTimer();
|
|
495
|
+
this.modelCycleContainer.clear();
|
|
467
496
|
this.compactionQueuedMessages = [];
|
|
468
497
|
this.streamingComponent = undefined;
|
|
469
498
|
this.streamingMessage = undefined;
|
|
@@ -510,6 +539,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
510
539
|
this.#handleLspStartupEvent(data as LspStartupEvent);
|
|
511
540
|
}),
|
|
512
541
|
);
|
|
542
|
+
this.#eventBusUnsubscribers.push(
|
|
543
|
+
eventBus.on(MCP_CONNECTING_EVENT_CHANNEL, data => {
|
|
544
|
+
if (!isMcpConnectingEvent(data)) {
|
|
545
|
+
logger.warn("Ignoring malformed mcp:connecting event", { data });
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
this.showStatus(formatMCPConnectingMessage(data.serverNames));
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
513
551
|
}
|
|
514
552
|
|
|
515
553
|
this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"));
|
|
@@ -526,6 +564,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
526
564
|
this.btwContainer = new Container();
|
|
527
565
|
this.omfgContainer = new Container();
|
|
528
566
|
this.errorBannerContainer = new Container();
|
|
567
|
+
this.modelCycleContainer = new Container();
|
|
529
568
|
this.editor = new CustomEditor(getEditorTheme());
|
|
530
569
|
this.editor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
|
|
531
570
|
this.editor.setAutocompleteMaxVisible(settings.get("autocompleteMaxVisible"));
|
|
@@ -535,6 +574,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
535
574
|
this.editor.onAutocompleteUpdate = () => {
|
|
536
575
|
this.ui.requestRender();
|
|
537
576
|
};
|
|
577
|
+
this.editor.setShimmerRepaintHandler(() => this.ui.requestComponentRender(this.editor));
|
|
538
578
|
this.#syncEditorMaxHeight();
|
|
539
579
|
this.#resizeHandler = () => {
|
|
540
580
|
this.#syncEditorMaxHeight();
|
|
@@ -694,6 +734,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
694
734
|
this.ui.addChild(this.btwContainer);
|
|
695
735
|
this.ui.addChild(this.omfgContainer);
|
|
696
736
|
this.ui.addChild(this.errorBannerContainer);
|
|
737
|
+
this.ui.addChild(this.modelCycleContainer);
|
|
697
738
|
this.ui.addChild(this.statusLine); // Only renders hook statuses (main status in editor border)
|
|
698
739
|
this.ui.addChild(this.hookWidgetContainerAbove);
|
|
699
740
|
this.ui.addChild(this.editorContainer);
|
|
@@ -812,8 +853,30 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
812
853
|
name: cmd.name,
|
|
813
854
|
description: cmd.description,
|
|
814
855
|
}));
|
|
856
|
+
// Surface discovered prompt templates in the picker. AgentSession.prompt() expands
|
|
857
|
+
// `expandSlashCommand` before `expandPromptTemplate`, and builtin command
|
|
858
|
+
// execution resolves aliases before template expansion. Mirror that command
|
|
859
|
+
// resolution order by skipping templates whose names already appear in any
|
|
860
|
+
// builtin/hook/custom/skill/file command token.
|
|
861
|
+
const reservedNames = new Set<string>();
|
|
862
|
+
for (const command of this.#pendingSlashCommands) {
|
|
863
|
+
reservedNames.add(command.name);
|
|
864
|
+
for (const alias of command.aliases ?? []) reservedNames.add(alias);
|
|
865
|
+
}
|
|
866
|
+
for (const command of fileSlashCommands) {
|
|
867
|
+
reservedNames.add(command.name);
|
|
868
|
+
for (const alias of command.aliases ?? []) reservedNames.add(alias);
|
|
869
|
+
}
|
|
870
|
+
const promptTemplateCommands: SlashCommand[] = this.session.promptTemplates
|
|
871
|
+
.filter(template => !reservedNames.has(template.name))
|
|
872
|
+
.map(template => ({
|
|
873
|
+
name: template.name,
|
|
874
|
+
// `PromptTemplate.description` from `loadTemplatesFromDir` already includes the
|
|
875
|
+
// source suffix (e.g. "Review code (project)"), so pass it through verbatim.
|
|
876
|
+
description: template.description,
|
|
877
|
+
}));
|
|
815
878
|
const autocompleteProvider = this.#inputController.createAutocompleteProvider(
|
|
816
|
-
[...this.#pendingSlashCommands, ...fileSlashCommands],
|
|
879
|
+
[...this.#pendingSlashCommands, ...fileSlashCommands, ...promptTemplateCommands],
|
|
817
880
|
basePath,
|
|
818
881
|
);
|
|
819
882
|
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
@@ -907,6 +970,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
907
970
|
this.#goalContinuationTimer = undefined;
|
|
908
971
|
if (!this.onInputCallback) return;
|
|
909
972
|
if (!this.goalModeEnabled || this.goalModePaused) return;
|
|
973
|
+
// The 800ms timer can outlive the idle window that scheduled it: a
|
|
974
|
+
// `/goal set` taken via the streaming branch (or any extension/hook
|
|
975
|
+
// path that starts a turn while we wait) leaves the agent busy. Firing
|
|
976
|
+
// the continuation now would route through `submitInteractiveInput` →
|
|
977
|
+
// `promptCustomMessage` with no `streamingBehavior` and resurface
|
|
978
|
+
// `AgentBusyError`. Drop this tick; `#handleGoalSessionEvent` reschedules
|
|
979
|
+
// on the next `agent_end`.
|
|
980
|
+
if (this.#isAutoSubmitBlocked()) return;
|
|
910
981
|
if (this.#pendingSubmittedInput) return;
|
|
911
982
|
if (this.editor.getText().trim().length > 0) return;
|
|
912
983
|
if ((this.pendingImages?.length ?? 0) > 0) return;
|
|
@@ -930,7 +1001,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
930
1001
|
}
|
|
931
1002
|
}
|
|
932
1003
|
|
|
933
|
-
#
|
|
1004
|
+
#isAutoSubmitBlocked(): boolean {
|
|
934
1005
|
return this.session.isStreaming || this.session.isCompacting || this.session.hasPostPromptWork;
|
|
935
1006
|
}
|
|
936
1007
|
|
|
@@ -940,7 +1011,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
940
1011
|
this.disableLoopMode("Loop time limit reached. Loop mode disabled.");
|
|
941
1012
|
return;
|
|
942
1013
|
}
|
|
943
|
-
if (this.#
|
|
1014
|
+
if (this.#isAutoSubmitBlocked()) {
|
|
944
1015
|
this.#deferLoopAutoSubmit(() => this.#submitLoopPromptWhenReady(prompt));
|
|
945
1016
|
return;
|
|
946
1017
|
}
|
|
@@ -949,7 +1020,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
949
1020
|
|
|
950
1021
|
async #runLoopIteration(action: "prompt" | "compact" | "reset", prompt: string): Promise<void> {
|
|
951
1022
|
if (!this.loopModeEnabled || this.loopPrompt !== prompt || !this.onInputCallback) return;
|
|
952
|
-
if (this.#
|
|
1023
|
+
if (this.#isAutoSubmitBlocked()) {
|
|
953
1024
|
this.#deferLoopAutoSubmit(() => {
|
|
954
1025
|
void this.#runLoopIteration(action, prompt);
|
|
955
1026
|
});
|
|
@@ -1142,10 +1213,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1142
1213
|
}
|
|
1143
1214
|
|
|
1144
1215
|
#computeEditorMaxHeight(): number {
|
|
1145
|
-
|
|
1146
|
-
const terminalRows = Number.isFinite(rows) && rows > 0 ? rows : EDITOR_FALLBACK_ROWS;
|
|
1147
|
-
const maxHeight = terminalRows - EDITOR_RESERVED_ROWS;
|
|
1148
|
-
return Math.max(EDITOR_MAX_HEIGHT_MIN, Math.min(EDITOR_MAX_HEIGHT_MAX, maxHeight));
|
|
1216
|
+
return computeEditorMaxHeight(this.ui.terminal.rows);
|
|
1149
1217
|
}
|
|
1150
1218
|
|
|
1151
1219
|
#syncEditorMaxHeight(): void {
|
|
@@ -1345,6 +1413,41 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1345
1413
|
this.#todoAutoClearTimer.unref?.();
|
|
1346
1414
|
}
|
|
1347
1415
|
|
|
1416
|
+
/**
|
|
1417
|
+
* Render the ctrl+p model-role cycle chip track into its own anchored
|
|
1418
|
+
* container (just above the editor), mirroring the todo HUD: the container is
|
|
1419
|
+
* cleared and rebuilt in place on every cycle, so rapid presses or concurrent
|
|
1420
|
+
* chat activity can never stack duplicate tracks into the scrollback.
|
|
1421
|
+
*/
|
|
1422
|
+
showModelCycleTrack(track: string): void {
|
|
1423
|
+
this.#renderModelCycleTrack(track);
|
|
1424
|
+
this.#syncModelCycleClearTimer();
|
|
1425
|
+
this.ui.requestRender();
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
#renderModelCycleTrack(track: string | null): void {
|
|
1429
|
+
this.modelCycleContainer.clear();
|
|
1430
|
+
if (!track) return;
|
|
1431
|
+
this.modelCycleContainer.addChild(new Spacer(1));
|
|
1432
|
+
this.modelCycleContainer.addChild(new Text(track, 1, 0));
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
#cancelModelCycleClearTimer(): void {
|
|
1436
|
+
if (!this.#modelCycleClearTimer) return;
|
|
1437
|
+
clearTimeout(this.#modelCycleClearTimer);
|
|
1438
|
+
this.#modelCycleClearTimer = undefined;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
#syncModelCycleClearTimer(): void {
|
|
1442
|
+
this.#cancelModelCycleClearTimer();
|
|
1443
|
+
this.#modelCycleClearTimer = setTimeout(() => {
|
|
1444
|
+
this.#modelCycleClearTimer = undefined;
|
|
1445
|
+
this.#renderModelCycleTrack(null);
|
|
1446
|
+
this.ui.requestRender();
|
|
1447
|
+
}, MODEL_CYCLE_TRACK_CLEAR_MS);
|
|
1448
|
+
this.#modelCycleClearTimer.unref?.();
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1348
1451
|
#getActivePhase(phases: TodoPhase[]): TodoPhase | undefined {
|
|
1349
1452
|
const nonEmpty = phases.filter(phase => phase.tasks.length > 0);
|
|
1350
1453
|
const active = nonEmpty.find(phase =>
|
|
@@ -1738,7 +1841,40 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1738
1841
|
});
|
|
1739
1842
|
}
|
|
1740
1843
|
|
|
1741
|
-
async #
|
|
1844
|
+
async #restorePlanPreviousModel(prev: { model: Model; thinkingLevel?: ThinkingLevel }): Promise<void> {
|
|
1845
|
+
if (modelsAreEqual(this.session.model, prev.model)) {
|
|
1846
|
+
// Same model — only thinking level may differ. Avoid setModelTemporary()
|
|
1847
|
+
// which would reset provider-side sessions and break continuity.
|
|
1848
|
+
this.session.setThinkingLevel(prev.thinkingLevel);
|
|
1849
|
+
} else if (this.session.isStreaming) {
|
|
1850
|
+
this.#pendingModelSwitch = { model: prev.model, thinkingLevel: prev.thinkingLevel };
|
|
1851
|
+
} else {
|
|
1852
|
+
await this.session.setModelTemporary(prev.model, prev.thinkingLevel);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
/**
|
|
1857
|
+
* Idempotent post-compaction model transition for the plan-approval compact
|
|
1858
|
+
* path. The deferred pre-plan state is consumed on first application, so a
|
|
1859
|
+
* second call (the before-flush hook vs. the short-circuit fallback) is a
|
|
1860
|
+
* no-op. "failed" intentionally stays on the plan model — the context is
|
|
1861
|
+
* intact and we dispatch best-effort.
|
|
1862
|
+
*/
|
|
1863
|
+
async #applyDeferredPlanModelTransition(
|
|
1864
|
+
outcome: CompactionOutcome | undefined,
|
|
1865
|
+
executionModel: ResolvedRoleModel | undefined,
|
|
1866
|
+
): Promise<void> {
|
|
1867
|
+
const deferredPrev = this.#planModePreviousModelState;
|
|
1868
|
+
if (deferredPrev === undefined || outcome === "failed") return;
|
|
1869
|
+
this.#planModePreviousModelState = undefined;
|
|
1870
|
+
if (executionModel) {
|
|
1871
|
+
await this.#applyPlanExecutionModel(executionModel);
|
|
1872
|
+
} else {
|
|
1873
|
+
await this.#restorePlanPreviousModel(deferredPrev);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
async #exitPlanMode(options?: { silent?: boolean; paused?: boolean; deferModelRestore?: boolean }): Promise<void> {
|
|
1742
1878
|
if (!this.planModeEnabled) {
|
|
1743
1879
|
return;
|
|
1744
1880
|
}
|
|
@@ -1748,23 +1884,18 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1748
1884
|
await this.session.setActiveToolsByName(previousTools);
|
|
1749
1885
|
}
|
|
1750
1886
|
if (this.#planModePreviousModelState) {
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
// Same model — only thinking level may differ. Avoid setModelTemporary()
|
|
1754
|
-
// which would reset provider-side sessions (openai-responses/Codex) and
|
|
1755
|
-
// break conversation continuity.
|
|
1756
|
-
this.session.setThinkingLevel(prev.thinkingLevel);
|
|
1757
|
-
} else if (this.session.isStreaming) {
|
|
1758
|
-
this.#pendingModelSwitch = { model: prev.model, thinkingLevel: prev.thinkingLevel };
|
|
1759
|
-
} else {
|
|
1760
|
-
await this.session.setModelTemporary(prev.model, prev.thinkingLevel);
|
|
1887
|
+
if (!options?.deferModelRestore) {
|
|
1888
|
+
await this.#restorePlanPreviousModel(this.#planModePreviousModelState);
|
|
1761
1889
|
}
|
|
1762
1890
|
// If #applyPlanModeModel queued a deferred switch to the plan-role model
|
|
1763
1891
|
// (because the session was streaming on entry), drop it now: we are
|
|
1764
1892
|
// leaving plan mode, so flushing it on the next agent_end would land the
|
|
1765
1893
|
// session on the plan-role model after the user has exited plan mode
|
|
1766
|
-
// (issue #816).
|
|
1767
|
-
//
|
|
1894
|
+
// (issue #816). This runs even when deferModelRestore is set
|
|
1895
|
+
// (compact-approval path): otherwise the stale plan switch survives and
|
|
1896
|
+
// flushPendingModelSwitch() later clobbers the restored/execution model.
|
|
1897
|
+
// Only clear when the pending target matches the plan-role model — leave
|
|
1898
|
+
// any unrelated user-queued switch intact.
|
|
1768
1899
|
const pending = this.#pendingModelSwitch;
|
|
1769
1900
|
if (pending) {
|
|
1770
1901
|
const planResolution = this.session.resolveRoleModelWithThinking("plan");
|
|
@@ -1779,7 +1910,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1779
1910
|
this.planModePaused = options?.paused ?? false;
|
|
1780
1911
|
this.planModePlanFilePath = undefined;
|
|
1781
1912
|
this.#planModePreviousTools = undefined;
|
|
1782
|
-
this.#planModePreviousModelState = undefined;
|
|
1913
|
+
if (!options?.deferModelRestore) this.#planModePreviousModelState = undefined;
|
|
1783
1914
|
this.#updatePlanModeStatus();
|
|
1784
1915
|
const paused = options?.paused ?? false;
|
|
1785
1916
|
this.sessionManager.appendModeChange(paused ? "plan_paused" : "none");
|
|
@@ -2119,7 +2250,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2119
2250
|
}
|
|
2120
2251
|
let compactOutcome: CompactionOutcome | undefined;
|
|
2121
2252
|
try {
|
|
2122
|
-
await this.#exitPlanMode({
|
|
2253
|
+
await this.#exitPlanMode({
|
|
2254
|
+
silent: true,
|
|
2255
|
+
paused: false,
|
|
2256
|
+
deferModelRestore: options.compactBeforeExecute === true,
|
|
2257
|
+
});
|
|
2123
2258
|
|
|
2124
2259
|
if (!options.preserveContext) {
|
|
2125
2260
|
await this.handleClearCommand();
|
|
@@ -2148,7 +2283,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2148
2283
|
// the try/finally is idempotent and kept for the !compactBeforeExecute
|
|
2149
2284
|
// branch.
|
|
2150
2285
|
this.session.setPlanReferencePath(options.planFilePath);
|
|
2151
|
-
compactOutcome = await this.handleCompactCommand(compactionPrompt
|
|
2286
|
+
compactOutcome = await this.handleCompactCommand(compactionPrompt, outcome =>
|
|
2287
|
+
this.#applyDeferredPlanModelTransition(outcome, options.executionModel),
|
|
2288
|
+
);
|
|
2152
2289
|
}
|
|
2153
2290
|
} finally {
|
|
2154
2291
|
// Unconditional clear. Idempotent: a no-op when the flag was never set
|
|
@@ -2165,22 +2302,33 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2165
2302
|
}
|
|
2166
2303
|
this.session.setPlanReferencePath(options.planFilePath);
|
|
2167
2304
|
|
|
2305
|
+
// Resolve the deferred plan-approval model transition. On the compact path
|
|
2306
|
+
// the before-flush hook passed to handleCompactCommand already ran this (so
|
|
2307
|
+
// any input queued during compaction executed on the post-compaction
|
|
2308
|
+
// model); the re-run here is idempotent and covers the short-circuit where
|
|
2309
|
+
// compaction never executed. It runs for "cancelled" too — the operator
|
|
2310
|
+
// aborted only the compaction, not the approval — so the next turn no longer
|
|
2311
|
+
// lands on the plan model. "failed" stays on the plan model (context
|
|
2312
|
+
// intact) and dispatches best-effort.
|
|
2313
|
+
if (options.compactBeforeExecute) {
|
|
2314
|
+
await this.#applyDeferredPlanModelTransition(compactOutcome, options.executionModel);
|
|
2315
|
+
} else {
|
|
2316
|
+
await this.#applyPlanExecutionModel(options.executionModel);
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2168
2319
|
if (compactOutcome === "cancelled") {
|
|
2169
2320
|
// Explicit abort: honor it. `executeCompaction` already surfaced
|
|
2170
|
-
// `showError("Compaction cancelled")
|
|
2171
|
-
//
|
|
2172
|
-
//
|
|
2173
|
-
// `AgentSession.#buildPlanReferenceMessage`
|
|
2174
|
-
//
|
|
2175
|
-
// sent here, the executor's first turn would have no plan context.
|
|
2321
|
+
// `showError("Compaction cancelled")`; we add the deferred-dispatch
|
|
2322
|
+
// warning and exit without dispatching the synthetic plan-approved
|
|
2323
|
+
// prompt. `markPlanReferenceSent` stays unset so
|
|
2324
|
+
// `AgentSession.#buildPlanReferenceMessage` injects the plan reference
|
|
2325
|
+
// on the operator's next `prompt()` call.
|
|
2176
2326
|
this.showWarning(
|
|
2177
2327
|
"Plan approved, but compaction was cancelled — execution not dispatched. Submit a turn to continue.",
|
|
2178
2328
|
);
|
|
2179
2329
|
return;
|
|
2180
2330
|
}
|
|
2181
2331
|
|
|
2182
|
-
await this.#applyPlanExecutionModel(options.executionModel);
|
|
2183
|
-
|
|
2184
2332
|
// Approved plans land in a fresh (or compacted) session whose first user-visible
|
|
2185
2333
|
// turn is the synthetic plan-approved prompt — that path bypasses the
|
|
2186
2334
|
// input-controller's title generation. Seed an auto-name from the plan title
|
|
@@ -2203,6 +2351,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2203
2351
|
planFilePath: options.planFilePath,
|
|
2204
2352
|
contextPreserved: options.preserveContext === true,
|
|
2205
2353
|
});
|
|
2354
|
+
// The executor's first turn must start on an idle session. The agent may still
|
|
2355
|
+
// be streaming the post-`resolve` continuation (Agent.#emit is fire-and-forget)
|
|
2356
|
+
// or a turn kicked off by the compaction/clear above; prompt() would then throw
|
|
2357
|
+
// AgentBusyError ("Failed to finalize approved plan"). Abort the now-irrelevant
|
|
2358
|
+
// in-flight turn first — abort() bumps the prompt generation and cancels pending
|
|
2359
|
+
// continuations, so nothing re-streams in the synchronous gap before prompt().
|
|
2360
|
+
if (this.session.isStreaming) {
|
|
2361
|
+
await this.session.abort();
|
|
2362
|
+
}
|
|
2206
2363
|
await this.session.prompt(planModePrompt, { synthetic: true });
|
|
2207
2364
|
}
|
|
2208
2365
|
|
|
@@ -2223,6 +2380,19 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2223
2380
|
await this.#exitPlanMode({ paused: true });
|
|
2224
2381
|
return;
|
|
2225
2382
|
}
|
|
2383
|
+
if (this.planModePaused && !initialPrompt) {
|
|
2384
|
+
// No-arg third toggle: paused → off. Tools, model, and plan state were
|
|
2385
|
+
// already restored by the prior #exitPlanMode({ paused: true }); only the
|
|
2386
|
+
// paused flag, the reentry marker, and the session mode entry remain.
|
|
2387
|
+
// Prompted /plan invocations fall through to #enterPlanMode below so the
|
|
2388
|
+
// supplied prompt is still submitted as the first plan-mode turn.
|
|
2389
|
+
this.planModePaused = false;
|
|
2390
|
+
this.#planModeHasEntered = false;
|
|
2391
|
+
this.#updatePlanModeStatus();
|
|
2392
|
+
this.sessionManager.appendModeChange("none");
|
|
2393
|
+
this.showStatus("Plan mode disabled.");
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2226
2396
|
if (!this.session.settings.get("plan.enabled")) {
|
|
2227
2397
|
this.showWarning("Plan mode is disabled. Enable it in settings (plan.enabled).");
|
|
2228
2398
|
return;
|
|
@@ -2304,6 +2474,70 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2304
2474
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
2305
2475
|
}
|
|
2306
2476
|
}
|
|
2477
|
+
async handleGuidedGoalCommand(rest?: string): Promise<void> {
|
|
2478
|
+
try {
|
|
2479
|
+
if (this.planModeEnabled || this.planModePaused) {
|
|
2480
|
+
this.showWarning("Exit plan mode first.");
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
if (!this.session.settings.get("goal.enabled")) {
|
|
2484
|
+
this.showWarning("Goal mode is disabled. Enable it in settings (goal.enabled).");
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2487
|
+
if (this.goalModeEnabled) {
|
|
2488
|
+
this.showStatus("Goal mode is already active. Use /goal to manage it, or /goal drop to start over.");
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
if (this.#getPausedGoalState()) {
|
|
2492
|
+
this.showWarning("Resume the current goal first, or drop it before setting a new objective.");
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
const initial = rest?.trim()
|
|
2497
|
+
? rest.trim()
|
|
2498
|
+
: (await this.showHookEditor("Guided goal", undefined, undefined, { promptStyle: true }))?.trim();
|
|
2499
|
+
if (!initial) return;
|
|
2500
|
+
|
|
2501
|
+
const messages: GuidedGoalMessage[] = [{ role: "user", content: initial }];
|
|
2502
|
+
let latestDraftObjective: string | undefined;
|
|
2503
|
+
for (let turn = 0; turn < 6; turn++) {
|
|
2504
|
+
const result = await runGuidedGoalTurn(this.session, { messages });
|
|
2505
|
+
if (result.objective?.trim()) latestDraftObjective = result.objective.trim();
|
|
2506
|
+
if (result.kind === "question") {
|
|
2507
|
+
messages.push({ role: "assistant", content: result.question });
|
|
2508
|
+
const answer = (
|
|
2509
|
+
await this.showHookEditor(result.question, undefined, undefined, { promptStyle: true })
|
|
2510
|
+
)?.trim();
|
|
2511
|
+
if (!answer) return;
|
|
2512
|
+
messages.push({ role: "user", content: answer });
|
|
2513
|
+
continue;
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
const finalObjective = (
|
|
2517
|
+
await this.showHookEditor("Review guided goal", result.objective, undefined, { promptStyle: true })
|
|
2518
|
+
)?.trim();
|
|
2519
|
+
if (!finalObjective) return;
|
|
2520
|
+
await this.#startGoalFromObjective(finalObjective);
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
// Hit the turn cap without an explicit `ready`. Rather than discard the whole interview,
|
|
2525
|
+
// salvage the latest non-empty model objective draft seen on any earlier turn. A final
|
|
2526
|
+
// question turn may omit `objective`; that must not erase a usable draft.
|
|
2527
|
+
if (latestDraftObjective) {
|
|
2528
|
+
const finalObjective = (
|
|
2529
|
+
await this.showHookEditor("Review guided goal", latestDraftObjective, undefined, { promptStyle: true })
|
|
2530
|
+
)?.trim();
|
|
2531
|
+
if (finalObjective) {
|
|
2532
|
+
await this.#startGoalFromObjective(finalObjective);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
this.showWarning("Guided goal setup needs more detail. Run /guided-goal again with a narrower objective.");
|
|
2537
|
+
} catch (error) {
|
|
2538
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2307
2541
|
|
|
2308
2542
|
async #dispatchGoalSubcommand(sub: GoalSubcommand, rest: string): Promise<void> {
|
|
2309
2543
|
switch (sub) {
|
|
@@ -2437,7 +2671,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2437
2671
|
async #startGoalFromObjective(objective: string): Promise<void> {
|
|
2438
2672
|
await this.#enterGoalMode({ objective, silent: true });
|
|
2439
2673
|
this.#resetGoalContinuationSuppression();
|
|
2440
|
-
if (this.onInputCallback) {
|
|
2674
|
+
if (!this.session.isStreaming && this.onInputCallback) {
|
|
2441
2675
|
this.onInputCallback(this.startPendingSubmission({ text: objective }));
|
|
2442
2676
|
}
|
|
2443
2677
|
}
|
|
@@ -2452,7 +2686,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2452
2686
|
if (this.session.isStreaming) {
|
|
2453
2687
|
await this.session.sendGoalModeContext({ deliverAs: "steer" });
|
|
2454
2688
|
}
|
|
2455
|
-
if (this.onInputCallback) {
|
|
2689
|
+
if (!this.session.isStreaming && this.onInputCallback) {
|
|
2456
2690
|
this.onInputCallback(this.startPendingSubmission({ text: objective }));
|
|
2457
2691
|
}
|
|
2458
2692
|
}
|
|
@@ -2587,11 +2821,13 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2587
2821
|
return;
|
|
2588
2822
|
}
|
|
2589
2823
|
// Capture the operator's tier choice and hand it to #approvePlan, which
|
|
2590
|
-
// applies it AFTER #exitPlanMode. #exitPlanMode restores
|
|
2824
|
+
// applies it AFTER #exitPlanMode. #exitPlanMode normally restores
|
|
2591
2825
|
// #planModePreviousModelState (the model from before plan mode), so
|
|
2592
2826
|
// applying the slider choice any earlier would be silently reverted —
|
|
2593
2827
|
// the bug that made "continue with slow" keep executing on the default
|
|
2594
|
-
// model.
|
|
2828
|
+
// model. For compact-context approval, the plan model is kept through
|
|
2829
|
+
// compaction, then a successful compaction transitions to the slider model
|
|
2830
|
+
// (or restores the pre-plan model when no slider choice was made).
|
|
2595
2831
|
// `cycle.currentIndex` is exactly that restored model, so any chosen tier
|
|
2596
2832
|
// differing from it needs an explicit executionModel — this also covers
|
|
2597
2833
|
// leaving the slider on its `default` anchor while planning ran elsewhere.
|
|
@@ -2792,6 +3028,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2792
3028
|
nextEditor.onAutocompleteUpdate = () => {
|
|
2793
3029
|
this.ui.requestRender();
|
|
2794
3030
|
};
|
|
3031
|
+
nextEditor.setShimmerRepaintHandler(() => this.ui.requestComponentRender(this.editor));
|
|
2795
3032
|
nextEditor.setMaxHeight(this.#computeEditorMaxHeight());
|
|
2796
3033
|
if (this.historyStorage) {
|
|
2797
3034
|
nextEditor.setHistoryStorage(this.historyStorage);
|
|
@@ -3025,10 +3262,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3025
3262
|
this.setWorkingMessage(message);
|
|
3026
3263
|
}
|
|
3027
3264
|
|
|
3028
|
-
notifyInterrupting(): void {
|
|
3029
|
-
this.#eventController.notifyInterrupting();
|
|
3030
|
-
}
|
|
3031
|
-
|
|
3032
3265
|
showNewVersionNotification(newVersion: string): void {
|
|
3033
3266
|
this.#uiHelpers.showNewVersionNotification(newVersion);
|
|
3034
3267
|
}
|
|
@@ -3187,7 +3420,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3187
3420
|
await this.#sttController.toggle(this.editor, {
|
|
3188
3421
|
showWarning: (msg: string) => this.showWarning(msg),
|
|
3189
3422
|
showStatus: (msg: string) => this.showStatus(msg),
|
|
3423
|
+
requestRender: () => this.ui.requestRender(),
|
|
3190
3424
|
onStateChange: (state: SttState) => {
|
|
3425
|
+
// Duck assistant speech while the user is talking (push-to-talk); restore after.
|
|
3426
|
+
if (state === "recording") vocalizer.duck();
|
|
3427
|
+
else vocalizer.unduck();
|
|
3191
3428
|
if (state === "recording") {
|
|
3192
3429
|
this.#voicePreviousShowHardwareCursor = this.ui.getShowHardwareCursor();
|
|
3193
3430
|
this.#voicePreviousUseTerminalCursor = this.editor.getUseTerminalCursor();
|
|
@@ -3258,8 +3495,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3258
3495
|
await this.#selectorController.showDebugSelector();
|
|
3259
3496
|
}
|
|
3260
3497
|
|
|
3261
|
-
showAgentHub(): void {
|
|
3262
|
-
this.#selectorController.showAgentHub(this.#observerRegistry);
|
|
3498
|
+
showAgentHub(options?: { requireContent?: boolean }): void {
|
|
3499
|
+
this.#selectorController.showAgentHub(this.#observerRegistry, options);
|
|
3263
3500
|
}
|
|
3264
3501
|
|
|
3265
3502
|
resetObserverRegistry(): void {
|
|
@@ -3285,8 +3522,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3285
3522
|
await controller.handle(text);
|
|
3286
3523
|
}
|
|
3287
3524
|
|
|
3288
|
-
handleCompactCommand(
|
|
3289
|
-
|
|
3525
|
+
handleCompactCommand(
|
|
3526
|
+
customInstructions?: string,
|
|
3527
|
+
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
3528
|
+
): Promise<CompactionOutcome> {
|
|
3529
|
+
return this.#commandController.handleCompactCommand(customInstructions, beforeFlush);
|
|
3290
3530
|
}
|
|
3291
3531
|
|
|
3292
3532
|
handleHandoffCommand(customInstructions?: string): Promise<void> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { highlightOrchestrate } from "./orchestrate";
|
|
2
|
-
import { highlightUltrathink } from "./ultrathink";
|
|
3
|
-
import { highlightWorkflow } from "./workflow";
|
|
1
|
+
import { containsOrchestrate, highlightOrchestrate } from "./orchestrate";
|
|
2
|
+
import { containsUltrathink, highlightUltrathink } from "./ultrathink";
|
|
3
|
+
import { containsWorkflow, highlightWorkflow } from "./workflow";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Gradient-highlight every magic keyword ("ultrathink", "orchestrate",
|
|
@@ -14,7 +14,29 @@ import { highlightWorkflow } from "./workflow";
|
|
|
14
14
|
* pass the surrounding text color when decorating already-colored content (e.g.
|
|
15
15
|
* a themed message bubble) so the gradient does not bleed into the rest of the
|
|
16
16
|
* line. Defaults to a plain foreground reset for default-colored editor text.
|
|
17
|
+
*
|
|
18
|
+
* `phase` ∈ [0, 1) cyclically rotates each gradient — the editor passes a
|
|
19
|
+
* `Date.now()`-derived value to animate a Claude-Code-style shimmer while a
|
|
20
|
+
* keyword is on screen and the prompt is focused; sent message bubbles omit it
|
|
21
|
+
* to keep the static gradient.
|
|
22
|
+
*/
|
|
23
|
+
export function highlightMagicKeywords(text: string, resetTo?: string, phase?: number): string {
|
|
24
|
+
return highlightWorkflow(
|
|
25
|
+
highlightOrchestrate(highlightUltrathink(text, resetTo, phase), resetTo, phase),
|
|
26
|
+
resetTo,
|
|
27
|
+
phase,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cheap test for "does this text contain any magic keyword as standalone prose?".
|
|
33
|
+
* Short-circuits on a substring probe before paying for the markdown-aware
|
|
34
|
+
* prose check, so the common "no keyword in buffer" path is just three
|
|
35
|
+
* `String#indexOf`s. Used by the live editor to gate the shimmer timer.
|
|
17
36
|
*/
|
|
18
|
-
export function
|
|
19
|
-
|
|
37
|
+
export function hasMagicKeyword(text: string): boolean {
|
|
38
|
+
if (!text.includes("ultrathink") && !text.includes("orchestrate") && !text.includes("workflowz")) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return containsUltrathink(text) || containsOrchestrate(text) || containsWorkflow(text);
|
|
20
42
|
}
|