@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.2
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 +128 -0
- package/dist/cli.js +4006 -3966
- package/dist/types/advisor/advise-tool.d.ts +5 -2
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/cli-commands.d.ts +14 -1
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +32 -7
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
- package/dist/types/lib/xai-http.d.ts +11 -0
- package/dist/types/mcp/transports/stdio.d.ts +13 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -2
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +23 -1
- package/dist/types/modes/components/hook-editor.d.ts +16 -2
- package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +4 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/sdk.d.ts +13 -2
- package/dist/types/session/agent-session.d.ts +43 -11
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +14 -0
- package/dist/types/session/streaming-output.d.ts +2 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/slash-commands/types.d.ts +4 -4
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/browser/registry.d.ts +7 -3
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/manage-skill.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +2 -0
- package/dist/types/web/search/providers/base.d.ts +3 -0
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1358 -86
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/advise-tool.ts +7 -3
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/bench-cli.ts +4 -1
- package/src/cli/grep-cli.ts +2 -1
- package/src/cli-commands.ts +57 -23
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +34 -6
- package/src/config/model-resolver.ts +57 -12
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +43 -6
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/codex.ts +14 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/discovery/substitute-plugin-root.ts +23 -7
- package/src/exec/bash-executor.ts +14 -5
- package/src/export/html/template.js +2 -0
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
- package/src/internal-urls/agent-protocol.ts +73 -39
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lib/xai-http.ts +28 -2
- package/src/lsp/client.ts +1 -1
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/transports/stdio.ts +103 -23
- package/src/modes/acp/acp-agent.ts +28 -9
- package/src/modes/acp/acp-event-mapper.ts +8 -0
- package/src/modes/acp/acp-mode.ts +18 -4
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/assistant-message.ts +35 -14
- package/src/modes/components/chat-transcript-builder.ts +2 -0
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
- package/src/modes/components/custom-editor.ts +62 -1
- package/src/modes/components/hook-editor.ts +35 -5
- package/src/modes/components/login-dialog.test.ts +56 -0
- package/src/modes/components/login-dialog.ts +7 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/plan-review-overlay.ts +51 -10
- package/src/modes/components/session-selector.ts +10 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/component.ts +2 -4
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/components/tool-execution.test.ts +101 -0
- package/src/modes/components/tool-execution.ts +117 -9
- package/src/modes/components/transcript-container.ts +30 -2
- package/src/modes/controllers/command-controller-shared.ts +1 -1
- package/src/modes/controllers/command-controller.ts +168 -48
- package/src/modes/controllers/event-controller.ts +6 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +208 -46
- package/src/modes/interactive-mode.ts +205 -65
- package/src/modes/noninteractive-dispose.test.ts +60 -0
- package/src/modes/print-mode.ts +10 -5
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +31 -7
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/types.ts +10 -0
- package/src/modes/utils/interactive-context-helpers.ts +3 -1
- package/src/modes/utils/transcript-render-helpers.ts +3 -2
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/sdk.ts +227 -44
- package/src/session/agent-session.ts +1364 -382
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-listing.ts +6 -5
- package/src/session/session-loader.ts +9 -0
- package/src/session/session-manager.ts +48 -0
- package/src/session/session-persistence.ts +11 -0
- package/src/session/streaming-output.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +9 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/slash-commands/types.ts +4 -4
- package/src/system-prompt.ts +2 -2
- package/src/task/executor.ts +1 -1
- package/src/task/label.ts +2 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/browser/registry.ts +9 -2
- package/src/tools/browser/tab-supervisor.ts +65 -10
- package/src/tools/browser.ts +4 -3
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/eval.ts +3 -0
- package/src/tools/gh.ts +169 -2
- package/src/tools/image-gen.ts +624 -513
- package/src/tools/index.ts +10 -6
- package/src/tools/manage-skill.ts +5 -3
- package/src/tools/output-meta.ts +7 -0
- package/src/tools/path-utils.ts +8 -5
- package/src/tools/read.ts +48 -3
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/utils/title-generator.ts +15 -4
- package/src/web/search/index.ts +21 -6
- package/src/web/search/providers/base.ts +3 -0
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/providers/xai.ts +40 -9
- package/src/web/search/types.ts +6 -1
|
@@ -129,9 +129,9 @@ const modelSegment: StatusLineSegment = {
|
|
|
129
129
|
|
|
130
130
|
// Fast-mode icon and thinking-level suffix trail the model name and are
|
|
131
131
|
// colored together with it as `statusLineModel`. The advisor "++" badge
|
|
132
|
-
// sits between the name and that tail
|
|
133
|
-
//
|
|
134
|
-
//
|
|
132
|
+
// sits between the name and that tail, so it reads as a distinct marker.
|
|
133
|
+
// theme.fg resets only the fg, so the spans are concatenated (not
|
|
134
|
+
// nested) to keep each color intact.
|
|
135
135
|
let tail = "";
|
|
136
136
|
if (ctx.session.isFastModeActive() && theme.icon.fast) {
|
|
137
137
|
tail += ` ${theme.icon.fast}`;
|
|
@@ -141,10 +141,25 @@ const modelSegment: StatusLineSegment = {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// `statusLineModel` is aliased to `accent` in many themes, so the badge
|
|
144
|
-
// uses
|
|
144
|
+
// uses status colors to stay visibly distinct from the model name color.
|
|
145
145
|
let content = theme.fg("statusLineModel", withIcon(modelIcon, modelName));
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
// Advisor "++" badge, colored by the worst status in the roster:
|
|
147
|
+
// success = all running, warning = quota-exhausted, error = failed,
|
|
148
|
+
// dim = everything paused/no-model. Per-advisor detail lives in
|
|
149
|
+
// `/advisor status`.
|
|
150
|
+
// Optional chaining: lightweight session doubles (test mocks) that don't
|
|
151
|
+
// implement getAdvisorStatusOverview skip the badge instead of crashing.
|
|
152
|
+
const advisorStats = ctx.session.getAdvisorStatusOverview?.();
|
|
153
|
+
if (advisorStats?.configured && advisorStats.advisors.length > 0) {
|
|
154
|
+
const statuses = advisorStats.advisors.map(a => a.status);
|
|
155
|
+
const badgeColor = statuses.includes("error")
|
|
156
|
+
? "error"
|
|
157
|
+
: statuses.includes("quota_exhausted")
|
|
158
|
+
? "warning"
|
|
159
|
+
: statuses.includes("running")
|
|
160
|
+
? "success"
|
|
161
|
+
: "dim";
|
|
162
|
+
content += theme.fg(badgeColor, "++");
|
|
148
163
|
}
|
|
149
164
|
if (tail) {
|
|
150
165
|
content += theme.fg("statusLineModel", tail);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "bun:test";
|
|
2
|
+
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
3
|
+
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import { Settings } from "../../config/settings";
|
|
5
|
+
import { getThemeByName, setThemeInstance, theme } from "../theme/theme";
|
|
6
|
+
import { ToolExecutionComponent, type ToolExecutionUi } from "./tool-execution";
|
|
7
|
+
|
|
8
|
+
class BoldTypeErrorComponent implements Component {
|
|
9
|
+
render(_width: number): readonly string[] {
|
|
10
|
+
throw new TypeError("th.bold is not a function");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function visibleText(lines: readonly string[]): string {
|
|
15
|
+
let text = lines.join("\n");
|
|
16
|
+
text = text.replace(/\x1b\]8;[^\x1b\x07]*(?:\x07|\x1b\\)/g, "");
|
|
17
|
+
text = text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("ToolExecutionComponent custom renderer failures", () => {
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
await Settings.init({ inMemory: true });
|
|
24
|
+
const loaded = await getThemeByName("dark");
|
|
25
|
+
if (!loaded) throw new Error("theme unavailable");
|
|
26
|
+
setThemeInstance(loaded);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("falls back to the custom tool label when a renderCall child component throws during render", () => {
|
|
30
|
+
const tool: AgentTool = {
|
|
31
|
+
name: "graphify_graph",
|
|
32
|
+
label: "Graphify Graph",
|
|
33
|
+
description: "renders a graph",
|
|
34
|
+
parameters: { type: "object", additionalProperties: true },
|
|
35
|
+
renderCall() {
|
|
36
|
+
return new BoldTypeErrorComponent();
|
|
37
|
+
},
|
|
38
|
+
async execute() {
|
|
39
|
+
return { content: [{ type: "text", text: "ok" }] };
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const ui: ToolExecutionUi = {
|
|
43
|
+
requestRender() {},
|
|
44
|
+
requestComponentRender(_component: Component) {},
|
|
45
|
+
resetDisplay() {},
|
|
46
|
+
};
|
|
47
|
+
const component = new ToolExecutionComponent(
|
|
48
|
+
"graphify_graph",
|
|
49
|
+
{},
|
|
50
|
+
{ showImages: false },
|
|
51
|
+
tool,
|
|
52
|
+
ui,
|
|
53
|
+
process.cwd(),
|
|
54
|
+
);
|
|
55
|
+
let text = "";
|
|
56
|
+
|
|
57
|
+
expect(() => {
|
|
58
|
+
text = visibleText(component.render(80));
|
|
59
|
+
}).not.toThrow();
|
|
60
|
+
expect(text).toContain("Graphify Graph");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("preserves raw result text when a renderResult child component throws during render", () => {
|
|
64
|
+
const rawResultText = "raw result survives child renderer failure";
|
|
65
|
+
const tool: AgentTool = {
|
|
66
|
+
name: "crashy_result_renderer",
|
|
67
|
+
label: "Crashy Result Renderer",
|
|
68
|
+
description: "renders result output",
|
|
69
|
+
parameters: { type: "object", additionalProperties: true },
|
|
70
|
+
renderCall() {
|
|
71
|
+
return new Text(theme.fg("toolTitle", theme.bold("Crashy Result Renderer")), 0, 0);
|
|
72
|
+
},
|
|
73
|
+
renderResult() {
|
|
74
|
+
return new BoldTypeErrorComponent();
|
|
75
|
+
},
|
|
76
|
+
async execute() {
|
|
77
|
+
return { content: [{ type: "text", text: rawResultText }] };
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
const ui: ToolExecutionUi = {
|
|
81
|
+
requestRender() {},
|
|
82
|
+
requestComponentRender(_component: Component) {},
|
|
83
|
+
resetDisplay() {},
|
|
84
|
+
};
|
|
85
|
+
const component = new ToolExecutionComponent(
|
|
86
|
+
"crashy_result_renderer",
|
|
87
|
+
{},
|
|
88
|
+
{ showImages: false },
|
|
89
|
+
tool,
|
|
90
|
+
ui,
|
|
91
|
+
process.cwd(),
|
|
92
|
+
);
|
|
93
|
+
component.updateResult({ content: [{ type: "text", text: rawResultText }] }, false);
|
|
94
|
+
let text = "";
|
|
95
|
+
|
|
96
|
+
expect(() => {
|
|
97
|
+
text = visibleText(component.render(80));
|
|
98
|
+
}).not.toThrow();
|
|
99
|
+
expect(text).toContain(rawResultText);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "../../tools/render-utils";
|
|
41
41
|
import { type FirstResultViewportRepaint, toolRenderers } from "../../tools/renderers";
|
|
42
42
|
import { TODO_STRIKE_TOTAL_FRAMES, type TodoToolDetails } from "../../tools/todo";
|
|
43
|
-
import { isFramedBlockComponent, renderStatusLine, WidthAwareText } from "../../tui";
|
|
43
|
+
import { isFramedBlockComponent, markFramedBlockComponent, renderStatusLine, WidthAwareText } from "../../tui";
|
|
44
44
|
import { sanitizeWithOptionalSixelPassthrough } from "../../utils/sixel";
|
|
45
45
|
import { renderDiff } from "./diff";
|
|
46
46
|
|
|
@@ -148,6 +148,68 @@ function getArgsWithStreamedTextInput(args: unknown): unknown {
|
|
|
148
148
|
return input === undefined ? args : { ...record, input };
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
type ToolRendererStage = "call" | "result";
|
|
152
|
+
|
|
153
|
+
class SafeToolRendererComponent implements Component {
|
|
154
|
+
#toolName: string;
|
|
155
|
+
#stage: ToolRendererStage;
|
|
156
|
+
#component: Component;
|
|
157
|
+
#fallback: () => Component | undefined;
|
|
158
|
+
#warned = false;
|
|
159
|
+
readonly wantsKeyRelease: boolean | undefined;
|
|
160
|
+
|
|
161
|
+
constructor(
|
|
162
|
+
toolName: string,
|
|
163
|
+
stage: ToolRendererStage,
|
|
164
|
+
component: Component,
|
|
165
|
+
fallback: () => Component | undefined,
|
|
166
|
+
) {
|
|
167
|
+
this.#toolName = toolName;
|
|
168
|
+
this.#stage = stage;
|
|
169
|
+
this.#component = component;
|
|
170
|
+
this.#fallback = fallback;
|
|
171
|
+
this.wantsKeyRelease = component.wantsKeyRelease;
|
|
172
|
+
if (isFramedBlockComponent(component)) {
|
|
173
|
+
markFramedBlockComponent(this);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
render(width: number): readonly string[] {
|
|
178
|
+
try {
|
|
179
|
+
return this.#component.render(width);
|
|
180
|
+
} catch (err) {
|
|
181
|
+
if (!this.#warned) {
|
|
182
|
+
this.#warned = true;
|
|
183
|
+
logger.warn("Tool renderer failed", { tool: this.#toolName, stage: this.#stage, error: String(err) });
|
|
184
|
+
}
|
|
185
|
+
return this.#fallback()?.render(width) ?? [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
handleInput(data: string): void {
|
|
190
|
+
const handleInput = this.#component.handleInput;
|
|
191
|
+
if (handleInput === undefined) return;
|
|
192
|
+
handleInput.call(this.#component, data);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
invalidate(): void {
|
|
196
|
+
const invalidate = this.#component.invalidate;
|
|
197
|
+
if (invalidate === undefined) return;
|
|
198
|
+
invalidate.call(this.#component);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
setIgnoreTight(ignore: boolean): void {
|
|
202
|
+
const setIgnoreTight = this.#component.setIgnoreTight;
|
|
203
|
+
if (setIgnoreTight === undefined) return;
|
|
204
|
+
setIgnoreTight.call(this.#component, ignore);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
dispose(): void {
|
|
208
|
+
const dispose = this.#component.dispose;
|
|
209
|
+
if (dispose === undefined) return;
|
|
210
|
+
dispose.call(this.#component);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
151
213
|
/**
|
|
152
214
|
* Transcript-side probe telling a block whether it is still inside the live
|
|
153
215
|
* (repaintable) region. Implemented by `TranscriptContainer`; injected rather
|
|
@@ -157,6 +219,14 @@ export interface TranscriptLiveRegionProbe {
|
|
|
157
219
|
isBlockInLiveRegion(component: Component): boolean;
|
|
158
220
|
}
|
|
159
221
|
|
|
222
|
+
/** Minimal TUI surface ToolExecutionComponent uses to schedule repaints and share image budget. */
|
|
223
|
+
export interface ToolExecutionUi {
|
|
224
|
+
requestRender(): void;
|
|
225
|
+
requestComponentRender(component: Component): void;
|
|
226
|
+
resetDisplay(): void;
|
|
227
|
+
imageBudget?: TUI["imageBudget"];
|
|
228
|
+
}
|
|
229
|
+
|
|
160
230
|
export interface ToolExecutionOptions {
|
|
161
231
|
snapshots?: SnapshotStore;
|
|
162
232
|
showImages?: boolean; // default: true (only used if terminal supports images)
|
|
@@ -240,7 +310,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
240
310
|
// forcing the common image-free result to re-shape on every resize tick.
|
|
241
311
|
#renderedImageCount = 0;
|
|
242
312
|
#tool?: AgentTool;
|
|
243
|
-
#ui:
|
|
313
|
+
#ui: ToolExecutionUi;
|
|
244
314
|
#cwd: string;
|
|
245
315
|
#result?: {
|
|
246
316
|
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
|
|
@@ -308,7 +378,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
308
378
|
args: any,
|
|
309
379
|
options: ToolExecutionOptions = {},
|
|
310
380
|
tool: AgentTool | undefined,
|
|
311
|
-
ui:
|
|
381
|
+
ui: ToolExecutionUi,
|
|
312
382
|
cwd: string = getProjectDir(),
|
|
313
383
|
_toolCallId?: string,
|
|
314
384
|
) {
|
|
@@ -902,8 +972,17 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
902
972
|
if (tool.renderCall) {
|
|
903
973
|
try {
|
|
904
974
|
const callArgs = this.#getCallArgsForRender();
|
|
905
|
-
const callComponent = tool.renderCall(callArgs, this.#renderState, theme);
|
|
906
|
-
if (callComponent)
|
|
975
|
+
const callComponent = tool.renderCall(callArgs, this.#renderState, theme) as Component | undefined;
|
|
976
|
+
if (callComponent) {
|
|
977
|
+
this.#contentBox.addChild(
|
|
978
|
+
new SafeToolRendererComponent(
|
|
979
|
+
this.#toolName,
|
|
980
|
+
"call",
|
|
981
|
+
callComponent,
|
|
982
|
+
() => new Text(theme.fg("toolTitle", theme.bold(this.#toolLabel)), 0, 0),
|
|
983
|
+
),
|
|
984
|
+
);
|
|
985
|
+
}
|
|
907
986
|
} catch (err) {
|
|
908
987
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
909
988
|
// Fall back to default on error
|
|
@@ -934,7 +1013,15 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
934
1013
|
theme,
|
|
935
1014
|
this.#args,
|
|
936
1015
|
);
|
|
937
|
-
if (resultComponent)
|
|
1016
|
+
if (resultComponent) {
|
|
1017
|
+
this.#contentBox.addChild(
|
|
1018
|
+
new SafeToolRendererComponent(this.#toolName, "result", resultComponent, () => {
|
|
1019
|
+
const output = this.#getTextOutput();
|
|
1020
|
+
if (!output) return undefined;
|
|
1021
|
+
return new Text(theme.fg("toolOutput", replaceTabs(output)), 0, 0);
|
|
1022
|
+
}),
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
938
1025
|
} catch (err) {
|
|
939
1026
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
940
1027
|
// Fall back to showing raw output on error
|
|
@@ -991,7 +1078,11 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
991
1078
|
this.#renderState,
|
|
992
1079
|
theme,
|
|
993
1080
|
);
|
|
994
|
-
if (resultComponent)
|
|
1081
|
+
if (resultComponent) {
|
|
1082
|
+
fileBox.addChild(
|
|
1083
|
+
new SafeToolRendererComponent(this.#toolName, "result", resultComponent, () => undefined),
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
995
1086
|
} catch (err) {
|
|
996
1087
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
997
1088
|
}
|
|
@@ -1038,7 +1129,16 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
1038
1129
|
try {
|
|
1039
1130
|
const callArgs = this.#getCallArgsForRender();
|
|
1040
1131
|
const callComponent = renderer.renderCall(callArgs, this.#renderState, theme);
|
|
1041
|
-
if (callComponent)
|
|
1132
|
+
if (callComponent) {
|
|
1133
|
+
this.#contentBox.addChild(
|
|
1134
|
+
new SafeToolRendererComponent(
|
|
1135
|
+
this.#toolName,
|
|
1136
|
+
"call",
|
|
1137
|
+
callComponent,
|
|
1138
|
+
() => new Text(theme.fg("toolTitle", theme.bold(this.#toolLabel)), 0, 0),
|
|
1139
|
+
),
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1042
1142
|
} catch (err) {
|
|
1043
1143
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
1044
1144
|
// Fall back to default on error
|
|
@@ -1059,7 +1159,15 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
1059
1159
|
theme,
|
|
1060
1160
|
this.#getCallArgsForRender(),
|
|
1061
1161
|
);
|
|
1062
|
-
if (resultComponent)
|
|
1162
|
+
if (resultComponent) {
|
|
1163
|
+
this.#contentBox.addChild(
|
|
1164
|
+
new SafeToolRendererComponent(this.#toolName, "result", resultComponent, () => {
|
|
1165
|
+
const output = this.#getTextOutput();
|
|
1166
|
+
if (!output) return undefined;
|
|
1167
|
+
return new Text(theme.fg("toolOutput", replaceTabs(output)), 0, 0);
|
|
1168
|
+
}),
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1063
1171
|
} catch (err) {
|
|
1064
1172
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
1065
1173
|
// Fall back to showing raw output on error
|
|
@@ -79,6 +79,10 @@ function sealCommittedSnapshot(child: Component): void {
|
|
|
79
79
|
if (block.isDisplaceableBlock?.()) block.seal?.();
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
function setBlockCommittedRows(child: Component, rows: number): void {
|
|
83
|
+
(child as Component & Partial<NativeScrollbackCommittedRows>).setNativeScrollbackCommittedRows?.(rows);
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
// A "plain blank" row is empty or whitespace-only with no ANSI bytes. It marks
|
|
83
87
|
// separation padding (a `Spacer`, or a no-background `paddingY` row) as opposed
|
|
84
88
|
// to a background-colored padding row, whose escape sequences contain `\S` and
|
|
@@ -208,11 +212,35 @@ export class TranscriptContainer
|
|
|
208
212
|
this.#replayPending = false;
|
|
209
213
|
}
|
|
210
214
|
|
|
211
|
-
setNativeScrollbackCommittedRows(rows: number): void {
|
|
215
|
+
override setNativeScrollbackCommittedRows(rows: number): void {
|
|
212
216
|
this.#committedRows = Number.isFinite(rows) ? Math.max(0, Math.trunc(rows)) : 0;
|
|
217
|
+
for (let i = this.#compactedChildStart; i < this.children.length; i++) {
|
|
218
|
+
const child = this.children[i]!;
|
|
219
|
+
const segment = this.#segments[i];
|
|
220
|
+
if (segment === undefined || segment.component !== child) continue;
|
|
221
|
+
const committedContribution = Math.min(
|
|
222
|
+
segment.contribution.length,
|
|
223
|
+
Math.max(0, this.#committedRows - segment.startRow - segment.sep),
|
|
224
|
+
);
|
|
225
|
+
if (committedContribution === 0) {
|
|
226
|
+
setBlockCommittedRows(child, 0);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
// Transcript assembly strips plain blank edges from each block. Map the
|
|
230
|
+
// committed contribution back into the child's raw render coordinates so
|
|
231
|
+
// nested containers can split the prefix against their exact child rows.
|
|
232
|
+
let leadingTrimmedRows = 0;
|
|
233
|
+
while (leadingTrimmedRows < segment.rawRef.length && isPlainBlank(segment.rawRef[leadingTrimmedRows]!)) {
|
|
234
|
+
leadingTrimmedRows++;
|
|
235
|
+
}
|
|
236
|
+
setBlockCommittedRows(child, Math.min(segment.rawRef.length, leadingTrimmedRows + committedContribution));
|
|
237
|
+
}
|
|
213
238
|
}
|
|
214
239
|
|
|
215
|
-
prepareNativeScrollbackReplay(): void {
|
|
240
|
+
override prepareNativeScrollbackReplay(): void {
|
|
241
|
+
// Replay retires the old terminal tape, so descendants may discard layout
|
|
242
|
+
// locks whose only purpose was keeping that immutable history byte-stable.
|
|
243
|
+
super.prepareNativeScrollbackReplay();
|
|
216
244
|
if (this.#compactedChildStart === 0) return;
|
|
217
245
|
this.#compactedChildStart = 0;
|
|
218
246
|
this.#replayPending = true;
|
|
@@ -105,5 +105,5 @@ export function showCommandMessage(ctx: InteractiveModeContext, text: string): v
|
|
|
105
105
|
block.addChild(new DynamicBorder());
|
|
106
106
|
block.addChild(new Text(text, 1, 1));
|
|
107
107
|
block.addChild(new DynamicBorder());
|
|
108
|
-
ctx.
|
|
108
|
+
ctx.presentCommandOutput(block);
|
|
109
109
|
}
|