@oh-my-pi/pi-coding-agent 17.2.3 → 17.2.5
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 +92 -0
- package/dist/{CHANGELOG-c5hpqt9r.md → CHANGELOG-q2w43aw3.md} +92 -0
- package/dist/cli.js +3979 -3548
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/advisor/advise-tool.d.ts +6 -0
- package/dist/types/advisor/runtime.d.ts +4 -4
- package/dist/types/capability/mcp.d.ts +9 -0
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/models-cli.d.ts +1 -1
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +70 -22
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/discovery/helpers.d.ts +6 -0
- package/dist/types/discovery/omp-extension-roots.d.ts +21 -2
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/extensions/loader.d.ts +6 -2
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/extensibility/plugins/marketplace/registry.d.ts +2 -2
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +6 -3
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +3 -2
- package/dist/types/launch/protocol.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/request-id.d.ts +19 -0
- package/dist/types/mcp/types.d.ts +14 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/messages.d.ts +15 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/model-browser.d.ts +7 -5
- package/dist/types/modes/components/model-picker.d.ts +9 -3
- package/dist/types/modes/components/read-tool-group.d.ts +11 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/registry/agent-lifecycle.d.ts +14 -5
- package/dist/types/registry/agent-registry.d.ts +4 -3
- package/dist/types/sdk.d.ts +4 -2
- package/dist/types/secrets/index.d.ts +7 -9
- package/dist/types/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +6 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/model-controls.d.ts +0 -1
- package/dist/types/session/session-advisors.d.ts +8 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-maintenance.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +16 -5
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/system-prompt.d.ts +6 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/default-renderer.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/scripts/legacy-pi-virtual-module.ts +12 -2
- package/src/advisor/advise-tool.ts +18 -0
- package/src/advisor/runtime.ts +6 -6
- package/src/capability/mcp.ts +7 -0
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/models-cli.ts +13 -16
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/collab/host.ts +1 -1
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +19 -9
- package/src/config/mcp-schema.json +5 -0
- package/src/config/model-discovery.ts +30 -13
- package/src/config/model-registry.ts +16 -6
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +73 -21
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/mcp-json.ts +11 -1
- package/src/discovery/omp-extension-roots.ts +79 -20
- package/src/discovery/omp-plugins.ts +10 -1
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/template.js +8 -26
- package/src/extensibility/extensions/loader.ts +66 -28
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/plugins/marketplace/registry.ts +4 -8
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +3 -6
- package/src/launch/protocol.ts +2 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +31 -24
- package/src/mcp/config.ts +1 -0
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mcp/request-id.ts +24 -0
- package/src/mcp/transports/http.ts +4 -2
- package/src/mcp/transports/sse.ts +4 -2
- package/src/mcp/transports/stdio.ts +4 -2
- package/src/mcp/types.ts +15 -0
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/messages.ts +19 -0
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/agent-hub.ts +1 -1
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +10 -1
- package/src/modes/components/model-browser.ts +25 -17
- package/src/modes/components/model-picker.ts +10 -6
- package/src/modes/components/read-tool-group.ts +36 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +85 -20
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/command-controller.ts +2 -2
- package/src/modes/controllers/event-controller.ts +199 -10
- package/src/modes/controllers/input-controller.ts +39 -1
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +47 -6
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/types.ts +6 -1
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/registry/agent-lifecycle.ts +36 -11
- package/src/registry/agent-registry.ts +12 -5
- package/src/sdk.ts +102 -35
- package/src/secrets/index.ts +16 -18
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +23 -2
- package/src/session/agent-storage.ts +6 -4
- package/src/session/history-storage.ts +4 -2
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/messages.ts +31 -7
- package/src/session/model-controls.ts +0 -1
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +21 -3
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +41 -4
- package/src/session/session-manager.ts +181 -94
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/session-tools.ts +25 -23
- package/src/session/stream-guards.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +29 -26
- package/src/system-prompt.ts +14 -4
- package/src/task/executor.ts +8 -1
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tiny/text.ts +12 -0
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +5 -10
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/default-renderer.ts +19 -4
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/index.ts +10 -6
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +7 -2
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
seedAlreadyExists,
|
|
29
29
|
summarizeMentalModel,
|
|
30
30
|
} from "../../hindsight";
|
|
31
|
-
import { resolveMemoryBackend } from "../../memory-backend";
|
|
31
|
+
import { memoryStatsUnavailableMessage, resolveMemoryBackend } from "../../memory-backend";
|
|
32
32
|
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
33
33
|
import { BorderedLoader } from "../../modes/components/bordered-loader";
|
|
34
34
|
import { DynamicBorder } from "../../modes/components/dynamic-border";
|
|
@@ -658,7 +658,7 @@ export class CommandController {
|
|
|
658
658
|
try {
|
|
659
659
|
const payload = await hook?.(agentDir, this.ctx.sessionManager.getCwd(), this.ctx.session);
|
|
660
660
|
if (!payload) {
|
|
661
|
-
this.ctx.showWarning(
|
|
661
|
+
this.ctx.showWarning(memoryStatsUnavailableMessage(backend.id, action));
|
|
662
662
|
return;
|
|
663
663
|
}
|
|
664
664
|
showMarkdownPanel(this.ctx, `Memory ${action === "stats" ? "Stats" : "Diagnostics"}`, payload);
|
|
@@ -91,6 +91,30 @@ export class EventController {
|
|
|
91
91
|
#readToolCallArgs = new Map<string, Record<string, unknown>>();
|
|
92
92
|
#readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
|
|
93
93
|
#toolTimelineComponents = new Map<string, Component>();
|
|
94
|
+
// Stable identity for a streamed tool call while its assistant message is
|
|
95
|
+
// live: maps the tool-call block's position in the streaming message to the
|
|
96
|
+
// id last seen at that position. A streamed id can CHANGE across cumulative
|
|
97
|
+
// `message_update`s — some providers (GitHub Copilot's `call_id|id`
|
|
98
|
+
// transport, any stream that delivers name/args before the id) emit the
|
|
99
|
+
// block with an empty or partial id first and rewrite it in a later delta
|
|
100
|
+
// (openai-completions sets `id: toolCall.id || ""` then overwrites on the
|
|
101
|
+
// next chunk). Keyed only by id, the changed id reads as a brand-new call
|
|
102
|
+
// and a second card is created — the old-id card orphans as a pending
|
|
103
|
+
// preview while the new-id card takes the result (#6879). Reset per assistant
|
|
104
|
+
// message (indices are per-message).
|
|
105
|
+
#streamedToolCallIdByIndex = new Map<number, string>();
|
|
106
|
+
// A TTSR rewind retracts its uncommitted never-run cards at message_end;
|
|
107
|
+
// retain their ids until agent-loop's synthetic tool_execution_start/end for
|
|
108
|
+
// those calls arrive so the normal no-pending path cannot recreate the
|
|
109
|
+
// retracted card below the rewind's fresh blocks (#6879).
|
|
110
|
+
#retractedToolCallIds = new Set<string>();
|
|
111
|
+
// Cards settled by a synthetic aborted/error `tool_execution_end` (agent-loop
|
|
112
|
+
// emits one per never-run call on a terminal error/abort). They stay visible
|
|
113
|
+
// for a genuinely terminal failure, but if an auto-retry then supersedes the
|
|
114
|
+
// turn (`auto_retry_start`, decided AFTER message_end) they must be removed so
|
|
115
|
+
// the retry's fresh cards do not render the same call twice (Codex review on
|
|
116
|
+
// #6881). Keyed by call id; reset per turn.
|
|
117
|
+
#syntheticFailureCards = new Map<string, ToolExecutionHandle>();
|
|
94
118
|
// Completions that arrived before any component existed for their call id.
|
|
95
119
|
// Cursor's server-resolved tools (todo) emit `tool_execution_end` through a
|
|
96
120
|
// synchronous callback fired mid-parse, while the `toolcall_start` for the
|
|
@@ -252,6 +276,7 @@ export class EventController {
|
|
|
252
276
|
showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
|
|
253
277
|
});
|
|
254
278
|
group.setExpanded(this.ctx.toolOutputExpanded);
|
|
279
|
+
group.setToolActivityVisible(!this.ctx.hideToolActivity);
|
|
255
280
|
this.ctx.chatContainer.addChild(group);
|
|
256
281
|
this.#lastReadGroup = group;
|
|
257
282
|
}
|
|
@@ -274,6 +299,71 @@ export class EventController {
|
|
|
274
299
|
this.#readToolCallAssistantComponents.delete(toolCallId);
|
|
275
300
|
}
|
|
276
301
|
|
|
302
|
+
#retractToolCardEntry(toolCallId: string, component: ToolExecutionHandle): void {
|
|
303
|
+
component.seal();
|
|
304
|
+
let removeComponent = true;
|
|
305
|
+
if (component instanceof ReadToolGroupComponent) {
|
|
306
|
+
removeComponent = component.removeEntry(toolCallId);
|
|
307
|
+
if (component === this.#lastReadGroup) this.#resetReadGroup();
|
|
308
|
+
}
|
|
309
|
+
if (removeComponent) this.ctx.chatContainer.removeChild(component);
|
|
310
|
+
this.ctx.pendingTools.delete(toolCallId);
|
|
311
|
+
this.#toolTimelineComponents.delete(toolCallId);
|
|
312
|
+
this.#clearReadToolCall(toolCallId);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Re-key a live streamed tool card whose id changed mid-stream (see
|
|
317
|
+
* {@link #streamedToolCallIdByIndex}). Moves every id-keyed tracker from the
|
|
318
|
+
* old id to the new one so the next cumulative `message_update` reuses the
|
|
319
|
+
* existing card instead of creating a duplicate (#6879). The card component
|
|
320
|
+
* itself is id-agnostic (routing is via `pendingTools`), so only the maps and
|
|
321
|
+
* the shared read group's entry need re-keying.
|
|
322
|
+
*/
|
|
323
|
+
#migrateStreamedToolCallId(oldId: string, newId: string): void {
|
|
324
|
+
// `oldId` may be "" (the block streamed before its id): that empty key still
|
|
325
|
+
// owns a live card and must migrate. Skip only a no-op or an empty target.
|
|
326
|
+
if (oldId === newId || !newId) return;
|
|
327
|
+
const pending = this.ctx.pendingTools.get(oldId);
|
|
328
|
+
if (pending && !this.ctx.pendingTools.has(newId)) {
|
|
329
|
+
this.ctx.pendingTools.delete(oldId);
|
|
330
|
+
this.ctx.pendingTools.set(newId, pending);
|
|
331
|
+
}
|
|
332
|
+
const timeline = this.#toolTimelineComponents.get(oldId);
|
|
333
|
+
if (timeline && !this.#toolTimelineComponents.has(newId)) {
|
|
334
|
+
this.#toolTimelineComponents.delete(oldId);
|
|
335
|
+
this.#toolTimelineComponents.set(newId, timeline);
|
|
336
|
+
}
|
|
337
|
+
// The reveal controller is id-keyed; drop the stale target so the loop's
|
|
338
|
+
// setTarget/bind under the new id owns the paced reveal.
|
|
339
|
+
this.#toolArgsReveal.finish(oldId);
|
|
340
|
+
const readArgs = this.#readToolCallArgs.get(oldId);
|
|
341
|
+
if (readArgs !== undefined) {
|
|
342
|
+
this.#readToolCallArgs.delete(oldId);
|
|
343
|
+
this.#readToolCallArgs.set(newId, readArgs);
|
|
344
|
+
}
|
|
345
|
+
const readAssistant = this.#readToolCallAssistantComponents.get(oldId);
|
|
346
|
+
if (readAssistant !== undefined) {
|
|
347
|
+
this.#readToolCallAssistantComponents.delete(oldId);
|
|
348
|
+
this.#readToolCallAssistantComponents.set(newId, readAssistant);
|
|
349
|
+
}
|
|
350
|
+
// A collapsed read renders into a shared group keyed by id; rename its
|
|
351
|
+
// entry so the row isn't duplicated under the new id.
|
|
352
|
+
if (pending instanceof ReadToolGroupComponent) pending.renameEntry(oldId, newId);
|
|
353
|
+
// A server-resolved completion (Cursor/todo) can land under `newId` while
|
|
354
|
+
// the card was still keyed by `oldId`, so it was parked in
|
|
355
|
+
// `#orphanedToolCompletions` instead of settling. Now that the card owns
|
|
356
|
+
// `newId`, apply the held result — the normal creation path that consumes
|
|
357
|
+
// held completions is skipped on a re-key (Codex review on #6881).
|
|
358
|
+
if (pending) {
|
|
359
|
+
const orphan = this.#orphanedToolCompletions.get(newId);
|
|
360
|
+
if (orphan) {
|
|
361
|
+
this.#orphanedToolCompletions.delete(newId);
|
|
362
|
+
this.#settleHeldCompletion(pending, orphan);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
277
367
|
#inlineReadToolImages(
|
|
278
368
|
toolCallId: string,
|
|
279
369
|
result: { content: Array<{ type: string; data?: string; mimeType?: string }> },
|
|
@@ -351,6 +441,9 @@ export class EventController {
|
|
|
351
441
|
this.#renderedCustomMessages.clear();
|
|
352
442
|
this.#lastIntent = undefined;
|
|
353
443
|
this.#toolTimelineComponents.clear();
|
|
444
|
+
this.#streamedToolCallIdByIndex.clear();
|
|
445
|
+
this.#retractedToolCallIds.clear();
|
|
446
|
+
this.#syntheticFailureCards.clear();
|
|
354
447
|
this.#orphanedToolCompletions.clear();
|
|
355
448
|
this.#postToolAssistantComponents.clear();
|
|
356
449
|
this.#backgroundTaskCallIds.clear();
|
|
@@ -440,6 +533,9 @@ export class EventController {
|
|
|
440
533
|
|
|
441
534
|
async #handleAgentStart(_event: Extract<AgentSessionEvent, { type: "agent_start" }>): Promise<void> {
|
|
442
535
|
this.#toolTimelineComponents.clear();
|
|
536
|
+
this.#streamedToolCallIdByIndex.clear();
|
|
537
|
+
this.#retractedToolCallIds.clear();
|
|
538
|
+
this.#syntheticFailureCards.clear();
|
|
443
539
|
this.#orphanedToolCompletions.clear();
|
|
444
540
|
this.#postToolAssistantComponents.clear();
|
|
445
541
|
this.#lastIntent = undefined;
|
|
@@ -539,6 +635,7 @@ export class EventController {
|
|
|
539
635
|
this.ctx.ui.requestRender();
|
|
540
636
|
} else if (event.message.role === "assistant") {
|
|
541
637
|
this.#lastVisibleBlockCount = 0;
|
|
638
|
+
this.#streamedToolCallIdByIndex.clear();
|
|
542
639
|
this.ctx.streamingComponent = createAssistantMessageComponent(this.ctx);
|
|
543
640
|
this.ctx.streamingMessage = event.message;
|
|
544
641
|
this.ctx.chatContainer.addChild(this.ctx.streamingComponent);
|
|
@@ -750,8 +847,17 @@ export class EventController {
|
|
|
750
847
|
if (this.ctx.streamingMessage.content.some(content => content.type === "toolCall")) {
|
|
751
848
|
this.ctx.streamingComponent.markTranscriptBlockFinalized();
|
|
752
849
|
}
|
|
753
|
-
for (
|
|
850
|
+
for (let contentIndex = 0; contentIndex < this.ctx.streamingMessage.content.length; contentIndex++) {
|
|
851
|
+
const content = this.ctx.streamingMessage.content[contentIndex]!;
|
|
754
852
|
if (content.type !== "toolCall") continue;
|
|
853
|
+
// Re-key the live card when a provider rewrites this block's id
|
|
854
|
+
// across deltas, so the changed id reuses the existing card
|
|
855
|
+
// instead of spawning a duplicate (#6879).
|
|
856
|
+
const priorId = this.#streamedToolCallIdByIndex.get(contentIndex);
|
|
857
|
+
if (priorId !== undefined && priorId !== content.id) {
|
|
858
|
+
this.#migrateStreamedToolCallId(priorId, content.id);
|
|
859
|
+
}
|
|
860
|
+
this.#streamedToolCallIdByIndex.set(contentIndex, content.id);
|
|
755
861
|
if (content.name === "read") {
|
|
756
862
|
if (!readArgsHaveTarget(content.arguments)) {
|
|
757
863
|
// Args still streaming — defer until path is parseable so we can route to the
|
|
@@ -822,6 +928,7 @@ export class EventController {
|
|
|
822
928
|
content.id,
|
|
823
929
|
);
|
|
824
930
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
931
|
+
component.setToolActivityVisible(!this.ctx.hideToolActivity);
|
|
825
932
|
this.ctx.chatContainer.addChild(component);
|
|
826
933
|
this.ctx.pendingTools.set(content.id, component);
|
|
827
934
|
this.#toolTimelineComponents.set(content.id, component);
|
|
@@ -929,17 +1036,40 @@ export class EventController {
|
|
|
929
1036
|
component.setArgsComplete(toolCallId);
|
|
930
1037
|
}
|
|
931
1038
|
} else {
|
|
932
|
-
// The turn ended without running these calls
|
|
933
|
-
//
|
|
934
|
-
//
|
|
935
|
-
// streams fresh
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1039
|
+
// The turn ended without running these calls. What happens next
|
|
1040
|
+
// decides whether their cards should vanish or stay:
|
|
1041
|
+
// • TTSR rewind — known NOW via `isTtsrAbortPending` — re-runs the
|
|
1042
|
+
// turn and re-streams fresh cards, so retract the uncommitted
|
|
1043
|
+
// never-run cards here and swallow the synthetic completions
|
|
1044
|
+
// agent-loop emits for them, else the call renders twice (#6879).
|
|
1045
|
+
// • A plain terminal error/abort, or an auto-retry (whose
|
|
1046
|
+
// supersession is only known later at `auto_retry_start`), must
|
|
1047
|
+
// NOT be retracted here: agent-loop emits a synthetic
|
|
1048
|
+
// `tool_execution_end` right after this that settles each card
|
|
1049
|
+
// into a visible aborted/error result. Leave them so the terminal
|
|
1050
|
+
// failure stays visible; `#handleAutoRetryStart` removes them only
|
|
1051
|
+
// if a retry actually supersedes them (Codex review on #6881).
|
|
1052
|
+
const supersededByRewind =
|
|
1053
|
+
this.ctx.streamingMessage.stopReason === "aborted" && this.ctx.viewSession.isTtsrAbortPending;
|
|
1054
|
+
if (supersededByRewind) {
|
|
1055
|
+
for (const [toolCallId, component] of Array.from(this.ctx.pendingTools.entries())) {
|
|
1056
|
+
if (this.#backgroundTaskCallIds.has(toolCallId)) continue;
|
|
1057
|
+
if (
|
|
1058
|
+
!(component instanceof ToolExecutionComponent) &&
|
|
1059
|
+
!(component instanceof ReadToolGroupComponent)
|
|
1060
|
+
) {
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
if (this.ctx.chatContainer.isBlockUncommitted(component)) {
|
|
1064
|
+
this.#retractToolCardEntry(toolCallId, component);
|
|
1065
|
+
this.#retractedToolCallIds.add(toolCallId);
|
|
1066
|
+
} else {
|
|
1067
|
+
component.seal();
|
|
1068
|
+
}
|
|
939
1069
|
}
|
|
940
1070
|
}
|
|
941
|
-
// These calls will never
|
|
942
|
-
//
|
|
1071
|
+
// These calls will never run this attempt, so the tracked waiting
|
|
1072
|
+
// poll cannot be displaced anymore — freeze it in place.
|
|
943
1073
|
this.#resolveDisplaceablePoll();
|
|
944
1074
|
}
|
|
945
1075
|
// Surface a prompt-cache invalidation: if the previous turn cached a
|
|
@@ -1005,6 +1135,7 @@ export class EventController {
|
|
|
1005
1135
|
}
|
|
1006
1136
|
|
|
1007
1137
|
async #handleToolExecutionStart(event: Extract<AgentSessionEvent, { type: "tool_execution_start" }>): Promise<void> {
|
|
1138
|
+
if (this.#retractedToolCallIds.has(event.toolCallId)) return;
|
|
1008
1139
|
this.#ensureWorkingLoaderWhileStreaming();
|
|
1009
1140
|
this.#updateWorkingMessageFromIntent(event.intent);
|
|
1010
1141
|
if (event.toolName === "ask" || this.#toolWillPromptForApproval(event.toolName, event.args)) {
|
|
@@ -1047,6 +1178,7 @@ export class EventController {
|
|
|
1047
1178
|
event.toolCallId,
|
|
1048
1179
|
);
|
|
1049
1180
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
1181
|
+
component.setToolActivityVisible(!this.ctx.hideToolActivity);
|
|
1050
1182
|
this.ctx.chatContainer.addChild(component);
|
|
1051
1183
|
this.ctx.pendingTools.set(event.toolCallId, component);
|
|
1052
1184
|
this.#toolTimelineComponents.set(event.toolCallId, component);
|
|
@@ -1157,6 +1289,23 @@ export class EventController {
|
|
|
1157
1289
|
}
|
|
1158
1290
|
|
|
1159
1291
|
async #handleToolExecutionEnd(event: Extract<AgentSessionEvent, { type: "tool_execution_end" }>): Promise<void> {
|
|
1292
|
+
// `createAbortedToolResult` emits start/end after an error/aborted
|
|
1293
|
+
// assistant message. The matching card was deliberately retracted at
|
|
1294
|
+
// message_end; consume the completion instead of recreating/updating UI.
|
|
1295
|
+
if (this.#retractedToolCallIds.delete(event.toolCallId)) return;
|
|
1296
|
+
// A synthetic aborted/error completion (agent-loop's placeholder for a
|
|
1297
|
+
// never-run call on a terminal error/abort) settles the card in place so a
|
|
1298
|
+
// terminal failure stays visible. Remember it so `#handleAutoRetryStart`
|
|
1299
|
+
// can remove it if an auto-retry then supersedes the turn (Codex review on
|
|
1300
|
+
// #6881). Capture the live component now — the settle path below drops it
|
|
1301
|
+
// from `pendingTools` but leaves it in the transcript.
|
|
1302
|
+
const syntheticFailureDetails = event.result.details as { __synthetic?: boolean; source?: string } | undefined;
|
|
1303
|
+
const syntheticFailureCard =
|
|
1304
|
+
syntheticFailureDetails?.__synthetic === true &&
|
|
1305
|
+
(syntheticFailureDetails.source === "assistant_stop_error" ||
|
|
1306
|
+
syntheticFailureDetails.source === "assistant_stop_aborted")
|
|
1307
|
+
? this.ctx.pendingTools.get(event.toolCallId)
|
|
1308
|
+
: undefined;
|
|
1160
1309
|
// A transient overlay (auto-compaction / auto-retry / handoff) that ran
|
|
1161
1310
|
// between this tool's start and end could have detached the working
|
|
1162
1311
|
// loader. `tool_execution_update` already reconciles this so the spinner
|
|
@@ -1187,6 +1336,17 @@ export class EventController {
|
|
|
1187
1336
|
} else {
|
|
1188
1337
|
let component = this.ctx.pendingTools.get(event.toolCallId);
|
|
1189
1338
|
if (!component) {
|
|
1339
|
+
// A persisted result can win a mid-stream transcript rebuild
|
|
1340
|
+
// before this live completion handler runs. Rebuild removes the
|
|
1341
|
+
// pending handle and replay owns the completed card, but the
|
|
1342
|
+
// original timeline entry remains as proof that a card already
|
|
1343
|
+
// existed. Do not create a fallback read group beside replay
|
|
1344
|
+
// (#6879); the fallback is only for a completion that genuinely
|
|
1345
|
+
// outran every streamed card.
|
|
1346
|
+
if (this.#toolTimelineComponents.has(event.toolCallId)) {
|
|
1347
|
+
this.#clearReadToolCall(event.toolCallId);
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1190
1350
|
const group = this.#getReadGroup();
|
|
1191
1351
|
const args = this.#readToolCallArgs.get(event.toolCallId);
|
|
1192
1352
|
if (args) {
|
|
@@ -1243,6 +1403,7 @@ export class EventController {
|
|
|
1243
1403
|
this.#orphanedToolCompletions.set(event.toolCallId, event);
|
|
1244
1404
|
}
|
|
1245
1405
|
}
|
|
1406
|
+
if (syntheticFailureCard) this.#syntheticFailureCards.set(event.toolCallId, syntheticFailureCard);
|
|
1246
1407
|
// Update todo display when todo tool completes
|
|
1247
1408
|
if (event.toolName === "todo" && !event.isError) {
|
|
1248
1409
|
const details = event.result.details as { phases?: TodoPhase[] } | undefined;
|
|
@@ -1306,6 +1467,20 @@ export class EventController {
|
|
|
1306
1467
|
// the loader and finalizes it at its own agent_end (isStreaming === false by
|
|
1307
1468
|
// then). Mirrors the collab guest's !isStreaming loader reconciler.
|
|
1308
1469
|
if (this.ctx.session.isStreaming) return;
|
|
1470
|
+
// A non-terminal settle (`isTerminal: false`) is a scheduling pause, not the
|
|
1471
|
+
// end of the run: an unsuppressed async job (a `/vibe` worker turn, a bash
|
|
1472
|
+
// `async` job, etc.) will re-wake the loop when its result is delivered.
|
|
1473
|
+
// `AgentSession` tags this on the deferred event (see `#hasPendingAsyncWake`
|
|
1474
|
+
// in agent-session.ts). Skip the idle title/loader teardown so the tab keeps
|
|
1475
|
+
// reading "working"; the later terminal `agent_end` performs it. Still flush
|
|
1476
|
+
// a deferred model switch — the plan-mode reconciler queues it to apply once
|
|
1477
|
+
// the current stream ends, and `#finishAgentEnd` is otherwise its only flush
|
|
1478
|
+
// site, so the automatic continuation would otherwise run on the old
|
|
1479
|
+
// model/thinking level until the terminal settle.
|
|
1480
|
+
if (event.isTerminal === false) {
|
|
1481
|
+
await this.ctx.flushPendingModelSwitch();
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1309
1484
|
setTerminalTitleState("idle");
|
|
1310
1485
|
|
|
1311
1486
|
await this.#finishAgentEnd(event);
|
|
@@ -1349,6 +1524,9 @@ export class EventController {
|
|
|
1349
1524
|
this.#readToolCallArgs.clear();
|
|
1350
1525
|
this.#readToolCallAssistantComponents.clear();
|
|
1351
1526
|
this.#toolTimelineComponents.clear();
|
|
1527
|
+
this.#streamedToolCallIdByIndex.clear();
|
|
1528
|
+
this.#retractedToolCallIds.clear();
|
|
1529
|
+
this.#syntheticFailureCards.clear();
|
|
1352
1530
|
this.#orphanedToolCompletions.clear();
|
|
1353
1531
|
this.#postToolAssistantComponents.clear();
|
|
1354
1532
|
this.#resetReadGroup();
|
|
@@ -1522,6 +1700,17 @@ export class EventController {
|
|
|
1522
1700
|
async #handleAutoRetryStart(event: Extract<AgentSessionEvent, { type: "auto_retry_start" }>): Promise<void> {
|
|
1523
1701
|
this.#retryPending = true;
|
|
1524
1702
|
this.#trackRetrySupersededAssistantComponent(this.#lastAssistantComponent);
|
|
1703
|
+
// A retry supersedes the just-failed turn: its assistant + tool calls are
|
|
1704
|
+
// pruned from context and re-streamed. Remove the cards that a synthetic
|
|
1705
|
+
// aborted/error completion settled in place at message_end so the retry's
|
|
1706
|
+
// fresh cards don't render the same call twice (#6879). Only uncommitted
|
|
1707
|
+
// cards are removable; one already on the scrollback tape stays as history.
|
|
1708
|
+
for (const [toolCallId, component] of this.#syntheticFailureCards) {
|
|
1709
|
+
if (this.ctx.chatContainer.isBlockUncommitted(component)) {
|
|
1710
|
+
this.#retractToolCardEntry(toolCallId, component);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
this.#syntheticFailureCards.clear();
|
|
1525
1714
|
this.#stopWorkingLoader();
|
|
1526
1715
|
this.ctx.statusContainer.disposeChildren();
|
|
1527
1716
|
if (AIError.is(event.errorId, AIError.Flag.ThinkingLoop)) {
|
|
@@ -8,8 +8,11 @@ import { isSettingsInitialized, settings } from "../../config/settings";
|
|
|
8
8
|
import { resolveLocalRoot } from "../../internal-urls";
|
|
9
9
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
10
10
|
import { extractImagePathFromText } from "../../modes/components/custom-editor";
|
|
11
|
+
import { ReadToolGroupComponent } from "../../modes/components/read-tool-group";
|
|
11
12
|
import { renderSegmentTrack } from "../../modes/components/segment-track";
|
|
13
|
+
import { StrippedToolCallsPlaceholder } from "../../modes/components/stripped-tool-calls-placeholder";
|
|
12
14
|
import { TinyTitleDownloadProgressComponent } from "../../modes/components/tiny-title-download-progress";
|
|
15
|
+
import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
13
16
|
import { expandEmoticons } from "../../modes/emoji-autocomplete";
|
|
14
17
|
import { materializeImageReferenceLinks, shiftImageMarkers } from "../../modes/image-references";
|
|
15
18
|
import { createPromptActionAutocompleteProvider } from "../../modes/prompt-action-autocomplete";
|
|
@@ -327,10 +330,10 @@ export class InputController {
|
|
|
327
330
|
}
|
|
328
331
|
|
|
329
332
|
if (this.ctx.loopModeEnabled) {
|
|
330
|
-
this.ctx.pauseLoop();
|
|
331
333
|
if (this.ctx.session.isStreaming) {
|
|
332
334
|
this.#abortStreamingTurn();
|
|
333
335
|
} else {
|
|
336
|
+
this.ctx.pauseLoop();
|
|
334
337
|
this.ctx.cancelPendingSubmission();
|
|
335
338
|
}
|
|
336
339
|
return;
|
|
@@ -455,6 +458,11 @@ export class InputController {
|
|
|
455
458
|
this.ctx.editor.onCopyPrompt = () => this.handleCopyPrompt();
|
|
456
459
|
this.ctx.editor.setActionKeys("app.tools.expand", this.ctx.keybindings.getKeys("app.tools.expand"));
|
|
457
460
|
this.ctx.editor.onExpandTools = () => this.toggleToolOutputExpansion();
|
|
461
|
+
this.ctx.editor.setActionKeys(
|
|
462
|
+
"app.tools.toggleVisibility",
|
|
463
|
+
this.ctx.keybindings.getKeys("app.tools.toggleVisibility"),
|
|
464
|
+
);
|
|
465
|
+
this.ctx.editor.onToggleToolActivity = () => this.toggleToolActivityVisibility();
|
|
458
466
|
this.ctx.editor.setActionKeys("app.message.dequeue", this.ctx.keybindings.getKeys("app.message.dequeue"));
|
|
459
467
|
this.ctx.editor.onDequeue = () => this.handleDequeue();
|
|
460
468
|
this.ctx.editor.setActionKeys("app.retry", this.ctx.keybindings.getKeys("app.retry"));
|
|
@@ -1845,9 +1853,39 @@ export class InputController {
|
|
|
1845
1853
|
}
|
|
1846
1854
|
|
|
1847
1855
|
toggleToolOutputExpansion(): void {
|
|
1856
|
+
if (this.ctx.hideToolActivity) {
|
|
1857
|
+
const visibilityKey = this.ctx.keybindings.getDisplayString("app.tools.toggleVisibility");
|
|
1858
|
+
const visibilityHint = visibilityKey ? `${visibilityKey} or /settings` : "/settings";
|
|
1859
|
+
this.ctx.showStatus(`Tool activity is hidden — show it with ${visibilityHint} before expanding`);
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1848
1862
|
this.setToolsExpanded(!this.ctx.toolOutputExpanded);
|
|
1849
1863
|
}
|
|
1850
1864
|
|
|
1865
|
+
toggleToolActivityVisibility(): void {
|
|
1866
|
+
this.ctx.hideToolActivity = !this.ctx.hideToolActivity;
|
|
1867
|
+
this.ctx.settings.set("display.hideToolActivity", this.ctx.hideToolActivity);
|
|
1868
|
+
|
|
1869
|
+
if (!this.ctx.hideToolActivity) {
|
|
1870
|
+
this.ctx.toolOutputExpanded = false;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
for (const child of this.ctx.chatContainer.children) {
|
|
1874
|
+
if (child instanceof ToolExecutionComponent || child instanceof ReadToolGroupComponent) {
|
|
1875
|
+
if (!this.ctx.hideToolActivity) child.setExpanded(false);
|
|
1876
|
+
child.setToolActivityVisible(!this.ctx.hideToolActivity);
|
|
1877
|
+
} else if (child instanceof AssistantMessageComponent) {
|
|
1878
|
+
child.setToolResultImagesVisible(!this.ctx.hideToolActivity);
|
|
1879
|
+
} else if (child instanceof StrippedToolCallsPlaceholder) {
|
|
1880
|
+
child.setToolActivityVisible(!this.ctx.hideToolActivity);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
if (this.ctx.hideToolActivity) this.ctx.ui.clearInlineImages();
|
|
1885
|
+
this.ctx.ui.resetDisplay();
|
|
1886
|
+
this.ctx.showStatus(`Tool activity: ${this.ctx.hideToolActivity ? "hidden" : "visible"}`);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1851
1889
|
setToolsExpanded(expanded: boolean): void {
|
|
1852
1890
|
this.ctx.toolOutputExpanded = expanded;
|
|
1853
1891
|
for (const child of this.ctx.chatContainer.children) {
|
|
@@ -1793,16 +1793,22 @@ export class MCPCommandController {
|
|
|
1793
1793
|
const oauth = await this.#resolveOAuthEndpointsFromServer(runtimeBaseConfig, options.authChallenge);
|
|
1794
1794
|
const serverUrl =
|
|
1795
1795
|
runtimeBaseConfig.type === "http" || runtimeBaseConfig.type === "sse" ? runtimeBaseConfig.url : undefined;
|
|
1796
|
-
//
|
|
1797
|
-
//
|
|
1798
|
-
//
|
|
1799
|
-
|
|
1796
|
+
// Client credentials drive the token exchange, so they must come from the
|
|
1797
|
+
// env-expanded runtime config; `found.config`/`currentAuth` may still hold
|
|
1798
|
+
// `${...}` placeholders (the wizard writes the secret to auth.clientSecret).
|
|
1799
|
+
// DCR secrets are embedded in the stored credential and never echoed back
|
|
1800
|
+
// into config files.
|
|
1801
|
+
const runtimeAuth = currentAuth ? expandEnvVarsDeep(currentAuth) : undefined;
|
|
1802
|
+
const configuredClientId = runtimeBaseConfig.oauth?.clientId ?? runtimeAuth?.clientId;
|
|
1800
1803
|
const existingCredential = lookupMcpOAuthCredentialForServer(authStorage, currentAuth, serverUrl)?.credential;
|
|
1801
1804
|
const flowClientId = oauth.clientId ?? configuredClientId ?? existingCredential?.clientId ?? "";
|
|
1802
1805
|
const storedClientSecret =
|
|
1803
1806
|
existingCredential?.clientId === flowClientId ? existingCredential.clientSecret : undefined;
|
|
1807
|
+
const flowClientSecret =
|
|
1808
|
+
runtimeBaseConfig.oauth?.clientSecret ?? runtimeAuth?.clientSecret ?? storedClientSecret ?? "";
|
|
1809
|
+
// Persisted separately below: keep the raw `${...}` placeholder in the file
|
|
1810
|
+
// rather than writing the resolved secret back to (possibly shared) config.
|
|
1804
1811
|
const userClientSecret = found.config.oauth?.clientSecret ?? currentAuth?.clientSecret;
|
|
1805
|
-
const flowClientSecret = userClientSecret ?? storedClientSecret ?? "";
|
|
1806
1812
|
|
|
1807
1813
|
if (!options.silent) {
|
|
1808
1814
|
this.#showMessage(["", theme.fg("muted", `Reauthorizing "${name}"...`), ""].join("\n"));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AgentToolResult, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
2
3
|
import { PASTE_CODE_LOGIN_PROVIDERS } from "@oh-my-pi/pi-ai";
|
|
3
4
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
4
5
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
@@ -91,11 +92,13 @@ import { ModelHubComponent, type ModelRoleSelectionScope } from "../components/m
|
|
|
91
92
|
import { ModelPickerComponent } from "../components/model-picker";
|
|
92
93
|
import { OAuthSelectorComponent } from "../components/oauth-selector";
|
|
93
94
|
import { PluginSelectorComponent } from "../components/plugin-selector";
|
|
95
|
+
import { ReadToolGroupComponent } from "../components/read-tool-group";
|
|
94
96
|
import { ResetUsageSelectorComponent } from "../components/reset-usage-selector";
|
|
95
97
|
import { renderSegmentTrack } from "../components/segment-track";
|
|
96
98
|
import { SessionAccountSelectorComponent } from "../components/session-account-selector";
|
|
97
99
|
import { SessionSelectorComponent, type SessionSelectorOptions } from "../components/session-selector";
|
|
98
100
|
import { SettingsSelectorComponent } from "../components/settings-selector";
|
|
101
|
+
import { StrippedToolCallsPlaceholder } from "../components/stripped-tool-calls-placeholder";
|
|
99
102
|
import { ToolExecutionComponent } from "../components/tool-execution";
|
|
100
103
|
import { TranscriptBlock } from "../components/transcript-container";
|
|
101
104
|
import { TreeSelectorComponent } from "../components/tree-selector";
|
|
@@ -473,6 +476,24 @@ export class SelectorController {
|
|
|
473
476
|
break;
|
|
474
477
|
|
|
475
478
|
// Settings with UI side effects
|
|
479
|
+
case "display.hideToolActivity": {
|
|
480
|
+
const hidden = value as boolean;
|
|
481
|
+
this.ctx.hideToolActivity = hidden;
|
|
482
|
+
if (!hidden) this.ctx.toolOutputExpanded = false;
|
|
483
|
+
for (const child of this.ctx.chatContainer.children) {
|
|
484
|
+
if (child instanceof ToolExecutionComponent || child instanceof ReadToolGroupComponent) {
|
|
485
|
+
if (!hidden) child.setExpanded(false);
|
|
486
|
+
child.setToolActivityVisible(!hidden);
|
|
487
|
+
} else if (child instanceof AssistantMessageComponent) {
|
|
488
|
+
child.setToolResultImagesVisible(!hidden);
|
|
489
|
+
} else if (child instanceof StrippedToolCallsPlaceholder) {
|
|
490
|
+
child.setToolActivityVisible(!hidden);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (hidden) this.ctx.ui.clearInlineImages();
|
|
494
|
+
this.ctx.ui.resetDisplay();
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
476
497
|
case "terminal.showImages":
|
|
477
498
|
case "showImages": {
|
|
478
499
|
const visible = value as boolean;
|
|
@@ -696,15 +717,38 @@ export class SelectorController {
|
|
|
696
717
|
this.ctx.session.modelRegistry,
|
|
697
718
|
this.ctx.session.scopedModels,
|
|
698
719
|
{
|
|
699
|
-
onPick: async (model, selector) => {
|
|
700
|
-
|
|
701
|
-
|
|
720
|
+
onPick: async (model, selector, { overContext }) => {
|
|
721
|
+
// Session-only: update agent state but don't persist the model to settings.
|
|
722
|
+
const applySessionModel = async () => {
|
|
702
723
|
const roleThinkingLevel = this.ctx.session.resolveTemporaryModelThinkingLevel(model);
|
|
703
724
|
await this.ctx.session.setModelTemporary(model, roleThinkingLevel);
|
|
704
725
|
this.ctx.statusLine.invalidate();
|
|
705
726
|
this.ctx.updateEditorBorderColor();
|
|
706
727
|
const roleSelectorHint = this.ctx.keybindings.getKeys("app.model.select")[0] ?? "Alt+M";
|
|
707
728
|
this.ctx.showStatus(`Session-only model: ${selector}. Use ${roleSelectorHint} or /model for roles.`);
|
|
729
|
+
};
|
|
730
|
+
try {
|
|
731
|
+
if (overContext) {
|
|
732
|
+
// Over-context pick: close the picker so the compaction loader is
|
|
733
|
+
// visible, compact with the current model, then switch. The switch
|
|
734
|
+
// runs in the before-flush hook so any prompt queued during
|
|
735
|
+
// compaction executes on the target model, not the old one; the
|
|
736
|
+
// early "nothing to compact" return skips the hook, so the
|
|
737
|
+
// idempotent post-return call covers it. A cancelled or failed
|
|
738
|
+
// compaction keeps the current model — the target still cannot
|
|
739
|
+
// fit the transcript.
|
|
740
|
+
done();
|
|
741
|
+
let switched = false;
|
|
742
|
+
const switchAfterCompaction = async (outcome: CompactionOutcome) => {
|
|
743
|
+
if (switched || outcome !== "ok") return;
|
|
744
|
+
switched = true;
|
|
745
|
+
await applySessionModel();
|
|
746
|
+
};
|
|
747
|
+
const outcome = await this.ctx.handleCompactCommand(undefined, undefined, switchAfterCompaction);
|
|
748
|
+
await switchAfterCompaction(outcome);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
await applySessionModel();
|
|
708
752
|
done();
|
|
709
753
|
} catch (error) {
|
|
710
754
|
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
|
@@ -753,7 +797,6 @@ export class SelectorController {
|
|
|
753
797
|
* entry — used when reopening the hub after a /login round-trip.
|
|
754
798
|
*/
|
|
755
799
|
#showModelHub(hubOptions: { initialProviderId?: string }): void {
|
|
756
|
-
const currentContextTokens = this.ctx.session.getContextUsage()?.tokens ?? 0;
|
|
757
800
|
let overlayHandle: OverlayHandle | undefined;
|
|
758
801
|
let hub: ModelHubComponent | undefined;
|
|
759
802
|
let closed = false;
|
|
@@ -821,7 +864,6 @@ export class SelectorController {
|
|
|
821
864
|
selector,
|
|
822
865
|
thinkingLevel: isAuto ? ThinkingLevel.Inherit : concreteThinking,
|
|
823
866
|
persist: targetScope === "global",
|
|
824
|
-
currentContextTokens,
|
|
825
867
|
});
|
|
826
868
|
if (!switched) return;
|
|
827
869
|
if (targetScope === "project") {
|
|
@@ -922,7 +964,6 @@ export class SelectorController {
|
|
|
922
964
|
thinkingLevel: effectiveIsAuto
|
|
923
965
|
? ThinkingLevel.Inherit
|
|
924
966
|
: (concreteThinking ?? ThinkingLevel.Inherit),
|
|
925
|
-
currentContextTokens,
|
|
926
967
|
});
|
|
927
968
|
if (!switched) return;
|
|
928
969
|
if (effectiveIsAuto) {
|
|
@@ -460,6 +460,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
460
460
|
initialChatRendered = false;
|
|
461
461
|
isBashMode = false;
|
|
462
462
|
toolOutputExpanded = false;
|
|
463
|
+
hideToolActivity = false;
|
|
463
464
|
todoExpanded = false;
|
|
464
465
|
planModeEnabled = false;
|
|
465
466
|
planModePaused = false;
|
|
@@ -778,6 +779,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
778
779
|
// hot path where the render never gets to paint the result.
|
|
779
780
|
this.editor.setTopBorderProvider(availableWidth => this.statusLine.getTopBorder(availableWidth));
|
|
780
781
|
|
|
782
|
+
this.hideToolActivity = settings.get("display.hideToolActivity");
|
|
781
783
|
this.hideThinkingBlock = settings.get("hideThinkingBlock");
|
|
782
784
|
this.proseOnlyThinking = settings.get("proseOnlyThinking");
|
|
783
785
|
|
package/src/modes/types.ts
CHANGED
|
@@ -162,6 +162,7 @@ export interface InteractiveModeContext {
|
|
|
162
162
|
initialChatRendered: boolean;
|
|
163
163
|
isBashMode: boolean;
|
|
164
164
|
toolOutputExpanded: boolean;
|
|
165
|
+
hideToolActivity: boolean;
|
|
165
166
|
todoExpanded: boolean;
|
|
166
167
|
planModeEnabled: boolean;
|
|
167
168
|
vibeModeEnabled: boolean;
|
|
@@ -355,7 +356,11 @@ export interface InteractiveModeContext {
|
|
|
355
356
|
handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
|
|
356
357
|
handleMCPCommand(text: string): Promise<void>;
|
|
357
358
|
handleSSHCommand(text: string): Promise<void>;
|
|
358
|
-
handleCompactCommand(
|
|
359
|
+
handleCompactCommand(
|
|
360
|
+
customInstructions?: string,
|
|
361
|
+
mode?: CompactMode,
|
|
362
|
+
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
363
|
+
): Promise<CompactionOutcome>;
|
|
359
364
|
handleHandoffCommand(customInstructions?: string): Promise<void>;
|
|
360
365
|
handleShakeCommand(mode: ShakeMode): Promise<void>;
|
|
361
366
|
handleMoveCommand(targetPath?: string): Promise<void>;
|
|
@@ -46,6 +46,7 @@ export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string
|
|
|
46
46
|
`| \`${appKey(bindings, "app.plan.toggle")}\` | Toggle plan mode |`,
|
|
47
47
|
`| \`${appKey(bindings, "app.history.search")}\` | Search prompt history |`,
|
|
48
48
|
`| \`${appKey(bindings, "app.tools.expand")}\` | Toggle tool output expansion |`,
|
|
49
|
+
`| \`${appKey(bindings, "app.tools.toggleVisibility")}\` | Toggle tool activity visibility |`,
|
|
49
50
|
`| \`${appKey(bindings, "app.thinking.toggle")}\` | Toggle thinking block visibility |`,
|
|
50
51
|
`| \`${appKey(bindings, "app.editor.external")}\` | Edit message in external editor |`,
|
|
51
52
|
`| \`${appKey(bindings, "app.retry")}\` | Retry last failed assistant turn |`,
|
|
@@ -25,6 +25,7 @@ export function createAssistantMessageComponent(
|
|
|
25
25
|
ctx.proseOnlyThinking,
|
|
26
26
|
);
|
|
27
27
|
component.setImagesVisible(ctx.settings.get("terminal.showImages"));
|
|
28
|
+
component.setToolResultImagesVisible(!ctx.hideToolActivity);
|
|
28
29
|
component.setExpanded(ctx.toolOutputExpanded);
|
|
29
30
|
return component;
|
|
30
31
|
}
|