@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11
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 +83 -0
- package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
- package/dist/cli.js +10208 -12596
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +34 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
- package/dist/types/extensibility/extensions/loader.d.ts +9 -2
- package/dist/types/extensibility/extensions/types.d.ts +45 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/mcp/transports/header-policy.d.ts +45 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +14 -5
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +23 -1
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +18 -1
- package/dist/types/session/turn-recovery.d.ts +7 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +4 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +2 -1
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +1 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +16 -0
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +2 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +54 -0
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/command-help.ts +4 -0
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/say.ts +1 -1
- package/src/commands/share.ts +71 -0
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/cli.ts +1 -1
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-registry.ts +21 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +46 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents.ts +4 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/eval/py/runner.py +38 -1
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +3 -3
- package/src/extensibility/extensions/loader.ts +85 -20
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +51 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +5 -1
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/launch/terminal-output.ts +1 -1
- package/src/main.ts +88 -23
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +20 -9
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +674 -207
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +10 -7
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/controllers/event-controller.ts +44 -20
- package/src/modes/controllers/extension-ui-controller.ts +2 -2
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +41 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +14 -7
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +52 -1
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session.ts +54 -16
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-context.ts +3 -5
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-handoff.ts +5 -1
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +57 -9
- package/src/session/session-tools.ts +65 -4
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +96 -74
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +12 -8
- package/src/task/executor.ts +70 -20
- package/src/task/index.ts +30 -34
- package/src/task/isolation-runner.ts +24 -11
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +14 -4
- package/src/task/types.ts +4 -0
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +16 -17
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +41 -12
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/debug.ts +1 -1
- package/src/tools/fetch.ts +1 -1
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/path-utils.ts +9 -2
- package/src/tools/read.ts +21 -9
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +98 -0
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +5 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/ecosia.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +1 -1
- package/src/web/search/providers/startpage.ts +1 -1
- package/src/markit/converters/mammoth.d.ts +0 -24
|
@@ -43,6 +43,8 @@ export interface AgentTranscriptViewerDeps {
|
|
|
43
43
|
lifecycle?: () => AgentLifecycleManager;
|
|
44
44
|
ui: TUI;
|
|
45
45
|
getTool?: (name: string) => AgentTool | undefined;
|
|
46
|
+
/** Whether the active registry entry came from a built-in factory. */
|
|
47
|
+
isBuiltInTool?: (name: string) => boolean;
|
|
46
48
|
getMessageRenderer?: (customType: string) => MessageRenderer | undefined;
|
|
47
49
|
cwd: string;
|
|
48
50
|
hideThinkingBlock?: () => boolean;
|
|
@@ -161,6 +163,7 @@ export class AgentTranscriptViewer implements Component {
|
|
|
161
163
|
this.#builder = new ChatTranscriptBuilder({
|
|
162
164
|
ui: deps.ui,
|
|
163
165
|
getTool: deps.getTool,
|
|
166
|
+
isBuiltInTool: deps.isBuiltInTool,
|
|
164
167
|
getMessageRenderer: deps.getMessageRenderer,
|
|
165
168
|
cwd: deps.cwd,
|
|
166
169
|
hideThinkingBlock: deps.hideThinkingBlock,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
Text,
|
|
12
12
|
} from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
14
|
-
import chalk from "chalk";
|
|
14
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
15
15
|
import type { AssistantThinkingRenderer } from "../../extensibility/extensions/types";
|
|
16
16
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
17
17
|
import { expandKeyHint, getPreviewLines, resolveImageOptions, TRUNCATE_LENGTHS } from "../../tools/render-utils";
|
|
@@ -61,6 +61,8 @@ import { CollapsedSyntheticMessageComponent, UserMessageComponent } from "./user
|
|
|
61
61
|
export interface ChatTranscriptBuilderDeps {
|
|
62
62
|
ui: TUI;
|
|
63
63
|
getTool?: (name: string) => AgentTool | undefined;
|
|
64
|
+
/** Whether the active registry entry came from a built-in factory. */
|
|
65
|
+
isBuiltInTool?: (name: string) => boolean;
|
|
64
66
|
getMessageRenderer?: (customType: string) => MessageRenderer | undefined;
|
|
65
67
|
cwd: string;
|
|
66
68
|
hideThinkingBlock?: () => boolean;
|
|
@@ -392,6 +394,7 @@ export class ChatTranscriptBuilder {
|
|
|
392
394
|
content.name,
|
|
393
395
|
content.arguments,
|
|
394
396
|
{
|
|
397
|
+
useBuiltInRenderer: this.deps.isBuiltInTool?.(content.name) ?? true,
|
|
395
398
|
// Stable ids and Kitty placeholder cells keep images anchored
|
|
396
399
|
// while the transcript viewport scrolls and reflows.
|
|
397
400
|
showImages: settings.get("terminal.showImages"),
|
|
@@ -30,7 +30,6 @@ type ConfigurableEditorAction = Extract<
|
|
|
30
30
|
| "app.model.cycleBackward"
|
|
31
31
|
| "app.model.select"
|
|
32
32
|
| "app.model.selectTemporary"
|
|
33
|
-
| "app.tools.expand"
|
|
34
33
|
| "app.tools.toggleVisibility"
|
|
35
34
|
| "app.thinking.toggle"
|
|
36
35
|
| "app.editor.external"
|
|
@@ -53,7 +52,6 @@ const DEFAULT_ACTION_KEYS: Record<ConfigurableEditorAction, KeyId[]> = {
|
|
|
53
52
|
"app.model.cycleBackward": ["shift+ctrl+p"],
|
|
54
53
|
"app.model.select": ["alt+m"],
|
|
55
54
|
"app.model.selectTemporary": ["alt+p"],
|
|
56
|
-
"app.tools.expand": ["ctrl+o"],
|
|
57
55
|
"app.tools.toggleVisibility": ["ctrl+shift+o"],
|
|
58
56
|
"app.thinking.toggle": ["ctrl+t"],
|
|
59
57
|
"app.editor.external": ["ctrl+g"],
|
|
@@ -551,7 +549,6 @@ export class CustomEditor extends Editor {
|
|
|
551
549
|
onCycleModelForward?: () => void;
|
|
552
550
|
onCycleModelBackward?: () => void;
|
|
553
551
|
onSelectModel?: () => void;
|
|
554
|
-
onExpandTools?: () => void;
|
|
555
552
|
onToggleToolActivity?: () => void;
|
|
556
553
|
onToggleThinking?: () => void;
|
|
557
554
|
onExternalEditor?: () => void;
|
|
@@ -907,12 +904,6 @@ export class CustomEditor extends Editor {
|
|
|
907
904
|
return;
|
|
908
905
|
}
|
|
909
906
|
|
|
910
|
-
// Intercept configured tool output expansion shortcut
|
|
911
|
-
if (this.#matchesAction(canonical, "app.tools.expand") && this.onExpandTools) {
|
|
912
|
-
this.onExpandTools();
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
907
|
// Intercept configured tool activity visibility toggle
|
|
917
908
|
if (this.#matchesAction(canonical, "app.tools.toggleVisibility") && this.onToggleToolActivity) {
|
|
918
909
|
this.onToggleToolActivity();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shows name, description, origin, status, and kind-specific preview.
|
|
5
5
|
*/
|
|
6
6
|
import * as os from "node:os";
|
|
7
|
-
import {
|
|
7
|
+
import { arkToWireSchema, isArkSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
8
8
|
import { type Component, truncateToWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
|
9
9
|
import { theme } from "../../../modes/theme/theme";
|
|
10
10
|
import { shortenPath } from "../../../tools/render-utils";
|
|
@@ -168,16 +168,21 @@ export class InspectorPanel implements Component {
|
|
|
168
168
|
lines.push(theme.fg("dim", theme.boxRound.horizontal.repeat(Math.min(width - 2, 40))));
|
|
169
169
|
|
|
170
170
|
try {
|
|
171
|
-
const tool = raw as
|
|
172
|
-
const wire = (
|
|
173
|
-
const paramSchema = wire(tool
|
|
174
|
-
const inputSchema = wire(tool
|
|
171
|
+
const tool = raw as { parameters?: unknown; inputSchema?: unknown };
|
|
172
|
+
const wire = (schema: unknown): unknown => (isArkSchema(schema) ? arkToWireSchema(schema) : schema);
|
|
173
|
+
const paramSchema = wire(tool.parameters) as Record<string, unknown> | undefined;
|
|
174
|
+
const inputSchema = wire(tool.inputSchema) as Record<string, unknown> | undefined;
|
|
175
175
|
const params = paramSchema?.properties || inputSchema?.properties || {};
|
|
176
176
|
|
|
177
177
|
if (Object.keys(params).length === 0) {
|
|
178
178
|
lines.push(theme.fg("dim", " (no arguments)"));
|
|
179
179
|
} else {
|
|
180
|
-
const
|
|
180
|
+
const requiredValue = paramSchema?.required ?? inputSchema?.required;
|
|
181
|
+
const required = new Set(
|
|
182
|
+
Array.isArray(requiredValue)
|
|
183
|
+
? requiredValue.filter((value): value is string => typeof value === "string")
|
|
184
|
+
: [],
|
|
185
|
+
);
|
|
181
186
|
|
|
182
187
|
for (const [name, spec] of Object.entries(params)) {
|
|
183
188
|
const param = spec as any;
|
|
@@ -212,9 +212,7 @@ export class FooterComponent implements Component {
|
|
|
212
212
|
rightSide = `${modelName} • ${resolved ? resolved : `${theme.thinking.autoPending} auto`}`;
|
|
213
213
|
} else {
|
|
214
214
|
const thinkingLevel = state.thinkingLevel ?? ThinkingLevel.Off;
|
|
215
|
-
|
|
216
|
-
rightSide = `${modelName} • ${thinkingLevel}`;
|
|
217
|
-
}
|
|
215
|
+
rightSide = `${modelName} • ${thinkingLevel}`;
|
|
218
216
|
}
|
|
219
217
|
}
|
|
220
218
|
|
|
@@ -254,8 +254,16 @@ export function thinkingLevelGlyph(level: ConfiguredThinkingLevel): string {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
|
-
* A slim role chip: `●default ◉` — solid dot for configured assignments,
|
|
257
|
+
* A slim role chip: `● default ◉` — solid dot for configured assignments,
|
|
258
258
|
* hollow for auto-selected fallbacks, thinking glyph attached when set.
|
|
259
|
+
*
|
|
260
|
+
* The space after the status glyph is load-bearing. Under the `nerd` preset
|
|
261
|
+
* these are Nerd Font private-use icons (U+F111 / U+F10C) whose glyphs are
|
|
262
|
+
* drawn two cells wide, while `visibleWidth` counts them as one
|
|
263
|
+
* (`ambiguousIsNarrow: true` in tui/utils.ts — the PUA block is
|
|
264
|
+
* East_Asian_Width=Ambiguous). Without a separator the icon overhangs and
|
|
265
|
+
* eats the label's first character (`● default` renders as `●efault`).
|
|
266
|
+
* Mirrors the spacing already used for `status.success` in model-hub.
|
|
259
267
|
*/
|
|
260
268
|
export function formatRoleChip(role: string, assignment: RoleAssignment, settings: Settings): string {
|
|
261
269
|
const info = getRoleInfo(role, settings);
|
|
@@ -263,9 +271,9 @@ export function formatRoleChip(role: string, assignment: RoleAssignment, setting
|
|
|
263
271
|
const glyph = thinkingLevelGlyph(assignment.thinkingLevel);
|
|
264
272
|
const suffix = glyph ? ` ${theme.fg("dim", glyph)}` : "";
|
|
265
273
|
if (assignment.autoSelected) {
|
|
266
|
-
return theme.fg("dim", `${theme.status.shadowed}${label}`) + suffix;
|
|
274
|
+
return theme.fg("dim", `${theme.status.shadowed} ${label}`) + suffix;
|
|
267
275
|
}
|
|
268
|
-
return theme.fg(info.color ?? "muted", `${theme.status.enabled}${label}`) + suffix;
|
|
276
|
+
return theme.fg(info.color ?? "muted", `${theme.status.enabled} ${label}`) + suffix;
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
/** `$in/out` per-million cost pair; `free` when both legs are zero. */
|
|
@@ -837,7 +837,10 @@ export class ModelHubComponent implements Component {
|
|
|
837
837
|
chips.push({
|
|
838
838
|
label,
|
|
839
839
|
styled: assignedHere
|
|
840
|
-
?
|
|
840
|
+
? // Separator required: under the `nerd` preset this glyph is a
|
|
841
|
+
// two-cell-wide PUA icon that `visibleWidth` counts as one, so
|
|
842
|
+
// without it the icon overhangs and eats `label`'s first char.
|
|
843
|
+
theme.fg(info.color ?? "muted", `${theme.status.enabled} ${label}`) +
|
|
841
844
|
theme.fg("dim", ` ${theme.status.success}`)
|
|
842
845
|
: theme.fg(info.color ?? "muted", label),
|
|
843
846
|
role,
|
|
@@ -1556,6 +1556,7 @@ export class StatusLineComponent implements Component {
|
|
|
1556
1556
|
return {
|
|
1557
1557
|
session: this.session,
|
|
1558
1558
|
focusedAgentId: this.#focusedAgentId,
|
|
1559
|
+
sessionAccent: this.#resolveSettings().sessionAccent !== false,
|
|
1559
1560
|
activeRepo: activeRepoCache.activeRepo,
|
|
1560
1561
|
width,
|
|
1561
1562
|
options: segmentOptions ?? {},
|
|
@@ -117,9 +117,10 @@ const modelSegment: StatusLineSegment = {
|
|
|
117
117
|
: `${theme.thinking.autoPending} auto`;
|
|
118
118
|
} else {
|
|
119
119
|
const level = state.thinkingLevel ?? ThinkingLevel.Off;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
thinkingDisplay =
|
|
121
|
+
level === ThinkingLevel.Off
|
|
122
|
+
? `${theme.status.disabled} off`
|
|
123
|
+
: (theme.thinking[level as keyof typeof theme.thinking] ?? level);
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -591,10 +592,12 @@ const sessionNameSegment: StatusLineSegment = {
|
|
|
591
592
|
const name = sessionManager?.getSessionName();
|
|
592
593
|
if (!name) return { content: "", visible: false };
|
|
593
594
|
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
595
|
+
const accentEnabled = ctx.sessionAccent !== false;
|
|
596
|
+
const ansi = accentEnabled
|
|
597
|
+
? (getSessionAccentAnsi(
|
|
598
|
+
getSessionAccentHex(name, theme.getMajorThemeColorHexes(), theme.accentSurfaceLuminance),
|
|
599
|
+
) ?? theme.getFgAnsi("accent"))
|
|
600
|
+
: theme.getFgAnsi("accent");
|
|
598
601
|
return { content: `${ansi}${sanitizeStatusText(name)}\x1b[39m`, visible: true };
|
|
599
602
|
},
|
|
600
603
|
};
|
|
@@ -52,6 +52,8 @@ export interface SegmentContext {
|
|
|
52
52
|
session: AgentSession;
|
|
53
53
|
/** Focused subagent id while the view is proxied at its session, undefined otherwise. */
|
|
54
54
|
focusedAgentId?: string | undefined;
|
|
55
|
+
/** Effective `statusLine.sessionAccent`; `false` disables hash-derived accent colors, while `true` or omission enables them. */
|
|
56
|
+
sessionAccent?: boolean;
|
|
55
57
|
activeRepo: ActiveRepoContext | null;
|
|
56
58
|
width: number;
|
|
57
59
|
options: StatusLineSegmentOptions;
|
|
@@ -23,7 +23,7 @@ import { formatDefaultToolExecution } from "../../tools/default-renderer";
|
|
|
23
23
|
import { EVAL_DEFAULT_PREVIEW_LINES } from "../../tools/eval";
|
|
24
24
|
import { isWaitingPollDetails } from "../../tools/hub";
|
|
25
25
|
import { formatStatusIcon, replaceTabs, resolveImageOptions } from "../../tools/render-utils";
|
|
26
|
-
import { type FirstResultViewportRepaint, toolRenderers } from "../../tools/renderers";
|
|
26
|
+
import { type FirstResultViewportRepaint, type ToolRenderer, toolRenderers } from "../../tools/renderers";
|
|
27
27
|
import { TODO_STRIKE_TOTAL_FRAMES, type TodoToolDetails } from "../../tools/todo";
|
|
28
28
|
import type { XdevState } from "../../tools/xdev";
|
|
29
29
|
import { isFramedBlockComponent, markFramedBlockComponent, renderStatusLine, WidthAwareText } from "../../tui";
|
|
@@ -226,6 +226,8 @@ export interface ToolExecutionOptions {
|
|
|
226
226
|
/** Session-persistent edit clipboard register, forked per preview frame. */
|
|
227
227
|
clipboard?: Clipboard;
|
|
228
228
|
showImages?: boolean; // default: true (only used if terminal supports images)
|
|
229
|
+
/** Allow the name-keyed renderer registry only when the active tool is the built-in implementation. */
|
|
230
|
+
useBuiltInRenderer?: boolean;
|
|
229
231
|
editFuzzyThreshold?: number;
|
|
230
232
|
editAllowFuzzy?: boolean;
|
|
231
233
|
/** Live-region probe used to settle detached task progress once the block
|
|
@@ -312,6 +314,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
312
314
|
// forcing the common image-free result to re-shape on every resize tick.
|
|
313
315
|
#renderedImageCount = 0;
|
|
314
316
|
#tool?: AgentTool;
|
|
317
|
+
#renderer?: ToolRenderer;
|
|
315
318
|
#ui: ToolExecutionUi;
|
|
316
319
|
#cwd: string;
|
|
317
320
|
#result?: {
|
|
@@ -398,6 +401,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
398
401
|
super();
|
|
399
402
|
this.#toolName = toolName;
|
|
400
403
|
this.#toolLabel = tool?.label ?? toolName;
|
|
404
|
+
this.#renderer = options.useBuiltInRenderer === false ? undefined : toolRenderers[toolName];
|
|
401
405
|
this.#showImages = options.showImages ?? true;
|
|
402
406
|
this.#editFuzzyThreshold = options.editFuzzyThreshold;
|
|
403
407
|
this.#editAllowFuzzy = options.editAllowFuzzy;
|
|
@@ -418,10 +422,9 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
418
422
|
this.#contentBox = new Box(0, 1);
|
|
419
423
|
this.#contentText = new WidthAwareText(contentWidth => this.#renderDefaultCard(contentWidth), 1, 1);
|
|
420
424
|
|
|
421
|
-
// Use Box for custom tools or built-in tools
|
|
422
|
-
const hasRenderer = toolName in toolRenderers;
|
|
425
|
+
// Use Box for custom tools or built-in tools with rich renderers.
|
|
423
426
|
const hasCustomRenderer = !!(tool?.renderCall || tool?.renderResult);
|
|
424
|
-
this.#usesContentBox = hasCustomRenderer ||
|
|
427
|
+
this.#usesContentBox = hasCustomRenderer || this.#renderer !== undefined;
|
|
425
428
|
if (this.#usesContentBox) {
|
|
426
429
|
this.addChild(this.#contentBox);
|
|
427
430
|
} else {
|
|
@@ -668,12 +671,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
668
671
|
const isStreamingArgs = !this.#argsComplete && (isEditLikeToolName(this.#toolName) || this.#toolName === "write");
|
|
669
672
|
const isBackgroundAsyncRunning =
|
|
670
673
|
(this.#result?.details as { async?: { state?: string } } | undefined)?.async?.state === "running";
|
|
671
|
-
const renderer =
|
|
672
|
-
| {
|
|
673
|
-
animatedPendingPreview?: boolean | ((args: unknown) => boolean);
|
|
674
|
-
animatedPartialResult?: boolean | ((args: unknown) => boolean);
|
|
675
|
-
}
|
|
676
|
-
| undefined;
|
|
674
|
+
const renderer = this.#renderer;
|
|
677
675
|
const pendingAnimation = renderer?.animatedPendingPreview;
|
|
678
676
|
const partialAnimation = renderer?.animatedPartialResult;
|
|
679
677
|
const pendingCallConsumesSpinner =
|
|
@@ -933,7 +931,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
933
931
|
|
|
934
932
|
#rendererFlag(name: "forceResultViewportRepaintOnSettle"): boolean {
|
|
935
933
|
const toolValue = (this.#tool as Record<string, unknown> | undefined)?.[name];
|
|
936
|
-
const rendererValue =
|
|
934
|
+
const rendererValue = this.#renderer?.[name];
|
|
937
935
|
return toolValue === true || (toolValue === undefined && rendererValue === true);
|
|
938
936
|
}
|
|
939
937
|
|
|
@@ -948,8 +946,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
948
946
|
if (this.#result !== undefined) return false;
|
|
949
947
|
const toolValue = (this.#tool as { forceFirstResultViewportRepaint?: FirstResultViewportRepaint } | undefined)
|
|
950
948
|
?.forceFirstResultViewportRepaint;
|
|
951
|
-
const value =
|
|
952
|
-
toolValue !== undefined ? toolValue : toolRenderers[this.#toolName]?.forceFirstResultViewportRepaint;
|
|
949
|
+
const value = toolValue !== undefined ? toolValue : this.#renderer?.forceFirstResultViewportRepaint;
|
|
953
950
|
if (typeof value === "function") return value(this.#args, this.#renderState);
|
|
954
951
|
return value === true;
|
|
955
952
|
}
|
|
@@ -1101,9 +1098,9 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
1101
1098
|
const customFramed = this.#contentBox.children.some(isFramedBlockComponent);
|
|
1102
1099
|
this.#contentBox.setPaddingX(customFramed ? 0 : 1);
|
|
1103
1100
|
this.#contentBox.setBgFn(customFramed ? undefined : stateBgFn);
|
|
1104
|
-
} else if (this.#
|
|
1105
|
-
//
|
|
1106
|
-
const renderer =
|
|
1101
|
+
} else if (this.#renderer) {
|
|
1102
|
+
// The active registry entry is a built-in tool with a rich renderer.
|
|
1103
|
+
const renderer = this.#renderer;
|
|
1107
1104
|
|
|
1108
1105
|
// Clean up previous multi-file boxes
|
|
1109
1106
|
for (const box of this.#multiFileBoxes) {
|
|
@@ -286,12 +286,20 @@ class TreeList implements Component {
|
|
|
286
286
|
|
|
287
287
|
// Apply filter mode
|
|
288
288
|
let passesFilter = true;
|
|
289
|
-
// Entry types hidden in default view (settings/bookkeeping)
|
|
289
|
+
// Entry types hidden in default view (settings/bookkeeping). These carry
|
|
290
|
+
// no conversation content, so the tree only shows them in "all" mode.
|
|
290
291
|
const isSettingsEntry =
|
|
291
292
|
entry.type === "label" ||
|
|
292
293
|
entry.type === "custom" ||
|
|
293
294
|
entry.type === "model_change" ||
|
|
294
|
-
entry.type === "thinking_level_change"
|
|
295
|
+
entry.type === "thinking_level_change" ||
|
|
296
|
+
entry.type === "service_tier_change" ||
|
|
297
|
+
entry.type === "title_change" ||
|
|
298
|
+
entry.type === "credential_pin" ||
|
|
299
|
+
entry.type === "session_init" ||
|
|
300
|
+
entry.type === "ttsr_injection" ||
|
|
301
|
+
entry.type === "mode_change" ||
|
|
302
|
+
entry.type === "reset_boundary";
|
|
295
303
|
|
|
296
304
|
switch (this.#filterMode) {
|
|
297
305
|
case "user-only":
|
|
@@ -390,6 +398,34 @@ class TreeList implements Component {
|
|
|
390
398
|
case "label":
|
|
391
399
|
parts.push("label", entry.label ?? "");
|
|
392
400
|
break;
|
|
401
|
+
case "service_tier_change":
|
|
402
|
+
parts.push("service tier");
|
|
403
|
+
if (entry.serviceTier) {
|
|
404
|
+
const serviceTier = entry.serviceTier;
|
|
405
|
+
for (const family in serviceTier) {
|
|
406
|
+
const tier = serviceTier[family as keyof typeof serviceTier];
|
|
407
|
+
if (tier) parts.push(family, tier);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
break;
|
|
411
|
+
case "title_change":
|
|
412
|
+
parts.push("title", entry.title);
|
|
413
|
+
break;
|
|
414
|
+
case "mode_change":
|
|
415
|
+
parts.push("mode", entry.mode);
|
|
416
|
+
break;
|
|
417
|
+
case "credential_pin":
|
|
418
|
+
parts.push("credential pin", entry.provider);
|
|
419
|
+
break;
|
|
420
|
+
case "ttsr_injection":
|
|
421
|
+
parts.push("ttsr injection", ...entry.injectedRules);
|
|
422
|
+
break;
|
|
423
|
+
case "reset_boundary":
|
|
424
|
+
parts.push("reset boundary");
|
|
425
|
+
break;
|
|
426
|
+
case "session_init":
|
|
427
|
+
parts.push("session init");
|
|
428
|
+
break;
|
|
393
429
|
}
|
|
394
430
|
|
|
395
431
|
return parts.join(" ");
|
|
@@ -658,8 +694,31 @@ class TreeList implements Component {
|
|
|
658
694
|
case "label":
|
|
659
695
|
result = theme.fg("dim", `[label: ${entry.label ?? "(cleared)"}]`);
|
|
660
696
|
break;
|
|
697
|
+
case "service_tier_change": {
|
|
698
|
+
// Per-family map, or null when the session went back to the default.
|
|
699
|
+
const tiers = entry.serviceTier
|
|
700
|
+
? Object.entries(entry.serviceTier)
|
|
701
|
+
.map(([family, tier]) => `${family}:${tier}`)
|
|
702
|
+
.join(" ")
|
|
703
|
+
: "(default)";
|
|
704
|
+
result = theme.fg("dim", `[service tier: ${tiers}]`);
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
case "title_change":
|
|
708
|
+
result = theme.fg("dim", `[title: ${normalize(entry.title)}]`);
|
|
709
|
+
break;
|
|
710
|
+
case "mode_change":
|
|
711
|
+
result = theme.fg("dim", `[mode: ${entry.mode}]`);
|
|
712
|
+
break;
|
|
713
|
+
case "credential_pin":
|
|
714
|
+
result = theme.fg("dim", `[credential pin: ${entry.provider}]`);
|
|
715
|
+
break;
|
|
661
716
|
default:
|
|
662
|
-
|
|
717
|
+
// Bookkeeping entries with nothing worth spelling out still get their
|
|
718
|
+
// type. A row that renders to the empty string is worse than a
|
|
719
|
+
// useless one: it draws as a bare bullet with no way to tell what it
|
|
720
|
+
// is or why the tree has a gap in it.
|
|
721
|
+
result = theme.fg("dim", `[${entry.type.replaceAll("_", " ")}]`);
|
|
663
722
|
}
|
|
664
723
|
|
|
665
724
|
return isSelected ? theme.bold(result) : result;
|
|
@@ -1436,7 +1436,10 @@ export class CommandController {
|
|
|
1436
1436
|
}
|
|
1437
1437
|
} catch (error) {
|
|
1438
1438
|
const message = error instanceof Error ? error.message : String(error);
|
|
1439
|
-
|
|
1439
|
+
// `session.handoff()` normalizes genuine cancellations to this exact message; a
|
|
1440
|
+
// provider error (even one named AbortError) is re-thrown verbatim so it surfaces
|
|
1441
|
+
// as a real failure instead of a false "cancelled".
|
|
1442
|
+
if (message === "Handoff cancelled") {
|
|
1440
1443
|
this.ctx.showError("Handoff cancelled");
|
|
1441
1444
|
} else {
|
|
1442
1445
|
this.ctx.showError(`Handoff failed: ${message}`);
|
|
@@ -1073,6 +1073,7 @@ export class EventController {
|
|
|
1073
1073
|
content.name,
|
|
1074
1074
|
renderArgs,
|
|
1075
1075
|
{
|
|
1076
|
+
useBuiltInRenderer: this.ctx.viewSession.hasBuiltInTool(content.name),
|
|
1076
1077
|
snapshots: getFileSnapshotStore(this.ctx.viewSession),
|
|
1077
1078
|
clipboard: getEditClipboard(this.ctx.viewSession),
|
|
1078
1079
|
showImages: settings.get("terminal.showImages"),
|
|
@@ -1322,6 +1323,7 @@ export class EventController {
|
|
|
1322
1323
|
event.toolName,
|
|
1323
1324
|
event.args,
|
|
1324
1325
|
{
|
|
1326
|
+
useBuiltInRenderer: this.ctx.viewSession.hasBuiltInTool(event.toolName),
|
|
1325
1327
|
snapshots: getFileSnapshotStore(this.ctx.viewSession),
|
|
1326
1328
|
clipboard: getEditClipboard(this.ctx.viewSession),
|
|
1327
1329
|
showImages: settings.get("terminal.showImages"),
|
|
@@ -1605,11 +1607,29 @@ export class EventController {
|
|
|
1605
1607
|
typeof details.title === "string" &&
|
|
1606
1608
|
typeof details.planExists === "boolean"
|
|
1607
1609
|
) {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1610
|
+
// Dispatch the approval WITHOUT blocking the serialized event
|
|
1611
|
+
// dispatch chain. `handlePlanApproval` -> `#approvePlan` awaits
|
|
1612
|
+
// `session.prompt` for the ENTIRE approved-execution turn; awaiting
|
|
1613
|
+
// it here (this handler runs inside `#runSerialized`) would hold the
|
|
1614
|
+
// single dispatch link for the whole run, so the run's own
|
|
1615
|
+
// agent_start / message_start / tool / coalesced message_update
|
|
1616
|
+
// events queue behind it on `#dispatchTail` and the chat stays blank
|
|
1617
|
+
// until execution finishes (issue #7684, follow-up to #5688 which
|
|
1618
|
+
// only closed the overlay). Detaching frees the link the moment this
|
|
1619
|
+
// handler returns; the approval overlay and the turn's live events
|
|
1620
|
+
// then render. The approval flow surfaces its own failures via
|
|
1621
|
+
// `showError`, so only an unexpected rejection is logged here.
|
|
1622
|
+
void this.ctx
|
|
1623
|
+
.handlePlanApproval({
|
|
1624
|
+
planFilePath: details.planFilePath,
|
|
1625
|
+
title: details.title,
|
|
1626
|
+
planExists: details.planExists,
|
|
1627
|
+
})
|
|
1628
|
+
.catch(err => {
|
|
1629
|
+
logger.warn("Plan approval dispatch failed", {
|
|
1630
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1631
|
+
});
|
|
1632
|
+
});
|
|
1613
1633
|
}
|
|
1614
1634
|
}
|
|
1615
1635
|
}
|
|
@@ -1636,6 +1656,12 @@ export class EventController {
|
|
|
1636
1656
|
// model/thinking level until the terminal settle.
|
|
1637
1657
|
if (event.isTerminal === false) {
|
|
1638
1658
|
await this.ctx.flushPendingModelSwitch();
|
|
1659
|
+
// Reaching here means the first guard passed, so `isStreaming` is already
|
|
1660
|
+
// false: a command issued from now on mounts immediately. Leaving earlier
|
|
1661
|
+
// panels queued would render them out of order, minutes later, after the
|
|
1662
|
+
// user was told they were only waiting for the turn. The transcript is
|
|
1663
|
+
// quiescent at a settle, which is the condition #4806 wanted.
|
|
1664
|
+
this.ctx.flushPendingCommandOutput();
|
|
1639
1665
|
return;
|
|
1640
1666
|
}
|
|
1641
1667
|
setTerminalTitleState("idle");
|
|
@@ -1896,21 +1922,19 @@ export class EventController {
|
|
|
1896
1922
|
this.ctx.retryLoader = undefined;
|
|
1897
1923
|
this.ctx.statusContainer.disposeChildren();
|
|
1898
1924
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
} else {
|
|
1913
|
-
this.#clearRetrySupersededAssistantComponents();
|
|
1925
|
+
let appliedRetryUpdate = false;
|
|
1926
|
+
for (const retryError of event.retryErrors ?? []) {
|
|
1927
|
+
const component = this.#takeRetrySupersededAssistantComponent(retryError.persistenceKey);
|
|
1928
|
+
if (!component) continue;
|
|
1929
|
+
component.applyRetryRecovery(retryError.retryRecovery);
|
|
1930
|
+
if (this.#pinnedErrorComponent === component) this.#pinnedErrorComponent = undefined;
|
|
1931
|
+
appliedRetryUpdate = true;
|
|
1932
|
+
}
|
|
1933
|
+
if (appliedRetryUpdate || (event.retryErrors?.length ?? 0) > 0) {
|
|
1934
|
+
this.ctx.clearPinnedError();
|
|
1935
|
+
}
|
|
1936
|
+
this.#clearRetrySupersededAssistantComponents();
|
|
1937
|
+
if (!event.success) {
|
|
1914
1938
|
this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
1915
1939
|
}
|
|
1916
1940
|
this.#ensureWorkingLoaderWhileStreaming();
|
|
@@ -166,7 +166,7 @@ export class ExtensionUiController {
|
|
|
166
166
|
this.ctx.sessionManager.appendLabelChange(targetId, label);
|
|
167
167
|
},
|
|
168
168
|
getActiveTools: () => this.ctx.session.getEnabledToolNames(),
|
|
169
|
-
getAllTools: () => this.ctx.session.
|
|
169
|
+
getAllTools: () => this.ctx.session.getAllToolInfos(),
|
|
170
170
|
setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames),
|
|
171
171
|
setModel: async model => {
|
|
172
172
|
const key = await this.ctx.session.modelRegistry.getApiKey(model);
|
|
@@ -399,7 +399,7 @@ export class ExtensionUiController {
|
|
|
399
399
|
this.ctx.sessionManager.appendLabelChange(targetId, label);
|
|
400
400
|
},
|
|
401
401
|
getActiveTools: () => this.ctx.session.getEnabledToolNames(),
|
|
402
|
-
getAllTools: () => this.ctx.session.
|
|
402
|
+
getAllTools: () => this.ctx.session.getAllToolInfos(),
|
|
403
403
|
setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames),
|
|
404
404
|
setModel: async model => {
|
|
405
405
|
const key = await this.ctx.session.modelRegistry.getApiKey(model);
|
|
@@ -13,6 +13,7 @@ import { renderSegmentTrack } from "../../modes/components/segment-track";
|
|
|
13
13
|
import { StrippedToolCallsPlaceholder } from "../../modes/components/stripped-tool-calls-placeholder";
|
|
14
14
|
import { TinyTitleDownloadProgressComponent } from "../../modes/components/tiny-title-download-progress";
|
|
15
15
|
import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
16
|
+
import { TreeSelectorComponent } from "../../modes/components/tree-selector";
|
|
16
17
|
import { expandEmoticons } from "../../modes/emoji-autocomplete";
|
|
17
18
|
import { materializeImageReferenceLinks, shiftImageMarkers } from "../../modes/image-references";
|
|
18
19
|
import { createPromptActionAutocompleteProvider } from "../../modes/prompt-action-autocomplete";
|
|
@@ -177,6 +178,7 @@ export class InputController {
|
|
|
177
178
|
#focusedPasteListenerInstalled = false;
|
|
178
179
|
#btwBranchListenerInstalled = false;
|
|
179
180
|
#btwCopyListenerInstalled = false;
|
|
181
|
+
#expandToolsListenerInstalled = false;
|
|
180
182
|
// Tap counter for the double-← gesture; reset whenever a quiet gap
|
|
181
183
|
// (>= LEFT_DOUBLE_TAP_MAX_GAP_MS) starts a fresh sequence. See
|
|
182
184
|
// #detectLeftDoubleTap.
|
|
@@ -278,6 +280,24 @@ export class InputController {
|
|
|
278
280
|
return { consume: true };
|
|
279
281
|
});
|
|
280
282
|
}
|
|
283
|
+
if (!this.#expandToolsListenerInstalled) {
|
|
284
|
+
this.#expandToolsListenerInstalled = true;
|
|
285
|
+
// `app.tools.expand` (Ctrl+O) toggles the transcript's tool-output
|
|
286
|
+
// preview. It must fire regardless of focus so a truncated edit stays
|
|
287
|
+
// expandable while an approval prompt / select dialog holds keyboard
|
|
288
|
+
// focus (#7837). Defers when the main transcript is not the active
|
|
289
|
+
// surface (a fullscreen/anchored overlay — agent hub, transcript
|
|
290
|
+
// viewer, log viewer, model picker) or when the focused component
|
|
291
|
+
// rebinds Ctrl+O for its own use (the tree selector's filter cycle).
|
|
292
|
+
this.ctx.ui.addInputListener(data => {
|
|
293
|
+
if (!this.ctx.keybindings.matches(data, "app.tools.expand")) return undefined;
|
|
294
|
+
if (this.ctx.ui.hasOverlay()) return undefined;
|
|
295
|
+
if (this.ctx.ui.getFocused() instanceof TreeSelectorComponent && matchesKey(data, "ctrl+o"))
|
|
296
|
+
return undefined;
|
|
297
|
+
this.toggleToolOutputExpansion();
|
|
298
|
+
return { consume: true };
|
|
299
|
+
});
|
|
300
|
+
}
|
|
281
301
|
this.ctx.editor.onEscape = () => {
|
|
282
302
|
// Side-channel panels are the topmost view. Esc dismisses them before
|
|
283
303
|
// touching loop mode, maintenance, or the underlying main turn.
|
|
@@ -456,8 +476,6 @@ export class InputController {
|
|
|
456
476
|
this.ctx.keybindings.getKeys("app.clipboard.copyPrompt"),
|
|
457
477
|
);
|
|
458
478
|
this.ctx.editor.onCopyPrompt = () => this.handleCopyPrompt();
|
|
459
|
-
this.ctx.editor.setActionKeys("app.tools.expand", this.ctx.keybindings.getKeys("app.tools.expand"));
|
|
460
|
-
this.ctx.editor.onExpandTools = () => this.toggleToolOutputExpansion();
|
|
461
479
|
this.ctx.editor.setActionKeys(
|
|
462
480
|
"app.tools.toggleVisibility",
|
|
463
481
|
this.ctx.keybindings.getKeys("app.tools.toggleVisibility"),
|