@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
|
@@ -2,8 +2,8 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
2
2
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
4
4
|
import type { Theme } from "../modes/theme/theme";
|
|
5
|
-
import { renderStatusLine } from "../tui";
|
|
6
|
-
import { formatExpandHint, replaceTabs, shortenPath, truncateToWidth } from "./render-utils";
|
|
5
|
+
import { framedBlock, renderStatusLine } from "../tui";
|
|
6
|
+
import { formatErrorDetail, formatExpandHint, replaceTabs, shortenPath, truncateToWidth } from "./render-utils";
|
|
7
7
|
|
|
8
8
|
interface InspectImageRenderArgs {
|
|
9
9
|
path?: string;
|
|
@@ -27,17 +27,21 @@ const INSPECT_OUTPUT_COLLAPSED_LINES = 4;
|
|
|
27
27
|
const INSPECT_OUTPUT_EXPANDED_LINES = 16;
|
|
28
28
|
const INSPECT_OUTPUT_LINE_WIDTH = 120;
|
|
29
29
|
|
|
30
|
+
function questionLine(question: string, uiTheme: Theme): string {
|
|
31
|
+
return `${uiTheme.fg("dim", "Question:")} ${uiTheme.fg("accent", truncateToWidth(replaceTabs(question), INSPECT_QUESTION_PREVIEW_WIDTH))}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
export const inspectImageToolRenderer = {
|
|
31
35
|
renderCall(args: InspectImageRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
32
36
|
const rawPath = args.path ?? "";
|
|
33
37
|
const pathDisplay = rawPath ? shortenPath(rawPath) : "…";
|
|
34
|
-
const header = renderStatusLine({ icon: "pending", title: "Inspect
|
|
38
|
+
const header = renderStatusLine({ icon: "pending", title: "Inspect", description: pathDisplay }, uiTheme);
|
|
35
39
|
const question = args.question?.trim();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
return new Text(`${header}\n${
|
|
40
|
+
// Call is at most a status line plus a one-line question — too small to box.
|
|
41
|
+
// The container renders a lone Text cleanly with no chrome.
|
|
42
|
+
if (!question) return new Text(header, 0, 0);
|
|
43
|
+
const tree = ` ${uiTheme.fg("dim", uiTheme.tree.last)} ${questionLine(question, uiTheme)}`;
|
|
44
|
+
return new Text(`${header}\n${tree}`, 0, 0);
|
|
41
45
|
},
|
|
42
46
|
|
|
43
47
|
renderResult(
|
|
@@ -49,55 +53,73 @@ export const inspectImageToolRenderer = {
|
|
|
49
53
|
const details = result.details;
|
|
50
54
|
const rawPath = details?.imagePath ?? args?.path ?? "";
|
|
51
55
|
const pathDisplay = rawPath ? shortenPath(rawPath) : "image";
|
|
52
|
-
const metaParts: string[] = [];
|
|
53
|
-
if (details?.model) metaParts.push(details.model);
|
|
54
|
-
if (details?.mimeType) metaParts.push(details.mimeType);
|
|
55
56
|
const header = renderStatusLine(
|
|
56
57
|
{
|
|
57
58
|
icon: result.isError ? "error" : "success",
|
|
58
|
-
title: "Inspect
|
|
59
|
+
title: "Inspect",
|
|
59
60
|
description: pathDisplay,
|
|
60
61
|
},
|
|
61
62
|
uiTheme,
|
|
62
63
|
);
|
|
63
64
|
|
|
64
|
-
const lines: string[] = [header];
|
|
65
65
|
const question = args?.question?.trim();
|
|
66
|
-
if (question) {
|
|
67
|
-
lines.push(
|
|
68
|
-
` ${uiTheme.fg("dim", uiTheme.tree.branch)} ${uiTheme.fg("dim", "Question:")} ${uiTheme.fg("accent", truncateToWidth(replaceTabs(question), INSPECT_QUESTION_PREVIEW_WIDTH))}`,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
66
|
const outputText = result.content.find(content => content.type === "text")?.text?.trimEnd() ?? "";
|
|
73
|
-
if (!outputText) {
|
|
74
|
-
lines.push(uiTheme.fg("dim", "(no output)"));
|
|
75
|
-
if (metaParts.length > 0) {
|
|
76
|
-
lines.push("");
|
|
77
|
-
lines.push(uiTheme.fg("dim", metaParts.join(" · ")));
|
|
78
|
-
}
|
|
79
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
80
|
-
}
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
if (result.isError) {
|
|
69
|
+
return framedBlock(uiTheme, width => {
|
|
70
|
+
const bodyLines: string[] = [];
|
|
71
|
+
if (question) bodyLines.push(questionLine(question, uiTheme));
|
|
72
|
+
bodyLines.push(formatErrorDetail(outputText || "inspection failed", uiTheme));
|
|
73
|
+
return {
|
|
74
|
+
header,
|
|
75
|
+
sections: [{ lines: bodyLines }],
|
|
76
|
+
state: "error",
|
|
77
|
+
borderColor: "error",
|
|
78
|
+
applyBg: false,
|
|
79
|
+
width,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
84
|
+
const metaParts: string[] = [];
|
|
85
|
+
if (details?.model) metaParts.push(details.model);
|
|
86
|
+
if (details?.mimeType) metaParts.push(details.mimeType);
|
|
87
|
+
const metaLine = metaParts.length > 0 ? uiTheme.fg("dim", metaParts.join(" · ")) : "";
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
// No answer text: nothing worth boxing — keep it to a clean status line
|
|
90
|
+
// (plus a trailing meta line, when present).
|
|
91
|
+
if (!outputText) {
|
|
92
|
+
return new Text(metaLine ? `${header}\n${metaLine}` : header, 0, 0);
|
|
98
93
|
}
|
|
99
94
|
|
|
100
|
-
return
|
|
95
|
+
return framedBlock(uiTheme, width => {
|
|
96
|
+
const bodyLines: string[] = [];
|
|
97
|
+
if (question) {
|
|
98
|
+
bodyLines.push(questionLine(question, uiTheme));
|
|
99
|
+
bodyLines.push("");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const outputLines = replaceTabs(outputText).split("\n");
|
|
103
|
+
const maxLines = options.expanded ? INSPECT_OUTPUT_EXPANDED_LINES : INSPECT_OUTPUT_COLLAPSED_LINES;
|
|
104
|
+
for (const line of outputLines.slice(0, maxLines)) {
|
|
105
|
+
bodyLines.push(uiTheme.fg("toolOutput", truncateToWidth(line, INSPECT_OUTPUT_LINE_WIDTH)));
|
|
106
|
+
}
|
|
107
|
+
if (outputLines.length > maxLines) {
|
|
108
|
+
const remaining = outputLines.length - maxLines;
|
|
109
|
+
const hint = formatExpandHint(uiTheme, options.expanded, true);
|
|
110
|
+
bodyLines.push(`${uiTheme.fg("dim", `… ${remaining} more lines`)}${hint ? ` ${hint}` : ""}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
header,
|
|
115
|
+
headerMeta: metaLine || undefined,
|
|
116
|
+
sections: [{ lines: bodyLines }],
|
|
117
|
+
state: "success",
|
|
118
|
+
borderColor: "borderMuted",
|
|
119
|
+
applyBg: false,
|
|
120
|
+
width,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
101
123
|
},
|
|
102
124
|
mergeCallAndResult: true,
|
|
103
125
|
};
|
|
@@ -4,6 +4,7 @@ import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
|
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import * as z from "zod/v4";
|
|
6
6
|
import { extractTextContent } from "../commit/utils";
|
|
7
|
+
|
|
7
8
|
import { expandRoleAlias, resolveModelFromString } from "../config/model-resolver";
|
|
8
9
|
import inspectImageDescription from "../prompts/tools/inspect-image.md" with { type: "text" };
|
|
9
10
|
import inspectImageSystemPromptTemplate from "../prompts/tools/inspect-image-system.md" with { type: "text" };
|
|
@@ -136,7 +137,13 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
136
137
|
},
|
|
137
138
|
],
|
|
138
139
|
},
|
|
139
|
-
{
|
|
140
|
+
{
|
|
141
|
+
apiKey: modelRegistry.resolver(model.provider, {
|
|
142
|
+
sessionId: this.session.getSessionId?.() ?? undefined,
|
|
143
|
+
baseUrl: model.baseUrl,
|
|
144
|
+
}),
|
|
145
|
+
signal,
|
|
146
|
+
},
|
|
140
147
|
{ telemetry, oneshotKind: "inspect_image", completeImpl: this.completeImageRequest },
|
|
141
148
|
);
|
|
142
149
|
|
package/src/tools/job.ts
CHANGED
|
@@ -396,7 +396,7 @@ export const jobToolRenderer = {
|
|
|
396
396
|
inline: true,
|
|
397
397
|
|
|
398
398
|
renderCall(args: JobRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
399
|
-
const text = renderStatusLine({ icon: "pending", title:
|
|
399
|
+
const text = renderStatusLine({ icon: "pending", title: describeTarget(args) || "Job" }, uiTheme);
|
|
400
400
|
return new Text(text, 0, 0);
|
|
401
401
|
},
|
|
402
402
|
|
|
@@ -410,7 +410,7 @@ export const jobToolRenderer = {
|
|
|
410
410
|
|
|
411
411
|
if (jobs.length === 0) {
|
|
412
412
|
const fallback = result.content?.find(c => c.type === "text")?.text || "No jobs to process";
|
|
413
|
-
const header = renderStatusLine({ icon: "warning", title:
|
|
413
|
+
const header = renderStatusLine({ icon: "warning", title: describeTarget(args) || "Job" }, uiTheme);
|
|
414
414
|
return new Text([header, formatEmptyMessage(fallback, uiTheme)].join("\n"), 0, 0);
|
|
415
415
|
}
|
|
416
416
|
|
|
@@ -433,8 +433,7 @@ export const jobToolRenderer = {
|
|
|
433
433
|
{
|
|
434
434
|
icon: headerIcon,
|
|
435
435
|
spinnerFrame: counts.running > 0 ? options.spinnerFrame : undefined,
|
|
436
|
-
title:
|
|
437
|
-
description,
|
|
436
|
+
title: description,
|
|
438
437
|
meta,
|
|
439
438
|
},
|
|
440
439
|
uiTheme,
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* These keep the transcript terse — one status line plus, for `retain`, one
|
|
6
6
|
* `Remember: …` line per stored item — instead of the generic JSON arg tree,
|
|
7
|
-
* which exploded multi-line memory blobs into an unreadable wall.
|
|
7
|
+
* which exploded multi-line memory blobs into an unreadable wall. The tool
|
|
8
|
+
* container is a transparent passthrough, so these renderers stay frameless:
|
|
9
|
+
* a status line with a couple of dim bullets reads far cleaner than boxing a
|
|
10
|
+
* one-line memory note.
|
|
8
11
|
*/
|
|
9
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
10
13
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
1
|
import { resolveLocalUrlToPath, resolveVaultUrlToPath } from "../internal-urls";
|
|
3
2
|
import type { ToolSession } from ".";
|
|
4
3
|
import { normalizeLocalScheme, resolveToCwd } from "./path-utils";
|
|
@@ -6,10 +5,19 @@ import { ToolError } from "./tool-errors";
|
|
|
6
5
|
|
|
7
6
|
const VAULT_SCHEME_PREFIX = "vault:";
|
|
8
7
|
const LOCAL_SCHEME_PREFIX = "local:";
|
|
9
|
-
const PLAN_ALIAS_FILE = "PLAN.md";
|
|
10
|
-
const LOCAL_PLAN_ALIAS = "local://PLAN.md";
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
/** True when `targetPath` addresses the session-local artifact sandbox
|
|
10
|
+
* (`local://…`). Those files are not part of the working tree, so plan mode
|
|
11
|
+
* treats them as freely writable scratch/plan space. */
|
|
12
|
+
function targetsLocalSandbox(targetPath: string): boolean {
|
|
13
|
+
return normalizeLocalScheme(targetPath).startsWith(LOCAL_SCHEME_PREFIX);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a write/edit target to its absolute filesystem path, honoring the
|
|
18
|
+
* `local://` and `vault://` schemes. Plain paths resolve against the session cwd.
|
|
19
|
+
*/
|
|
20
|
+
export function resolvePlanPath(session: ToolSession, targetPath: string): string {
|
|
13
21
|
const normalized = normalizeLocalScheme(targetPath);
|
|
14
22
|
if (normalized.startsWith(LOCAL_SCHEME_PREFIX)) {
|
|
15
23
|
return resolveLocalUrlToPath(normalized, {
|
|
@@ -25,37 +33,12 @@ function resolveRawPath(session: ToolSession, targetPath: string): string {
|
|
|
25
33
|
return resolveToCwd(normalized, session.cwd);
|
|
26
34
|
}
|
|
27
35
|
|
|
28
|
-
function isPlanAliasTarget(session: ToolSession, targetPath: string, resolved: string): boolean {
|
|
29
|
-
const normalized = normalizeLocalScheme(targetPath);
|
|
30
|
-
if (normalized === LOCAL_PLAN_ALIAS) return true;
|
|
31
|
-
return resolved === resolveToCwd(PLAN_ALIAS_FILE, session.cwd);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* location at `state.planFilePath`. This lets `write` and `edit` accept the
|
|
40
|
-
* habitual plan filename after approval even when the active artifact has a
|
|
41
|
-
* titled path such as `local://APPROVED.md`.
|
|
42
|
-
*
|
|
43
|
-
* Outside plan mode (or when the basename does not match) this is a no-op.
|
|
37
|
+
* Plan mode keeps the working tree read-only while letting the agent draft its
|
|
38
|
+
* plan. Writes and edits to the `local://` artifact sandbox are allowed (that is
|
|
39
|
+
* where the plan and any scratch notes live); anything that would touch the
|
|
40
|
+
* working tree — or rename/delete a file — is rejected.
|
|
44
41
|
*/
|
|
45
|
-
export function resolvePlanPath(session: ToolSession, targetPath: string): string {
|
|
46
|
-
const resolved = resolveRawPath(session, targetPath);
|
|
47
|
-
|
|
48
|
-
const state = session.getPlanModeState?.();
|
|
49
|
-
if (!state?.enabled) return resolved;
|
|
50
|
-
|
|
51
|
-
const planResolved = resolveRawPath(session, state.planFilePath);
|
|
52
|
-
if (resolved === planResolved) return resolved;
|
|
53
|
-
if (isPlanAliasTarget(session, targetPath, resolved)) return planResolved;
|
|
54
|
-
if (path.basename(resolved) !== path.basename(planResolved)) return resolved;
|
|
55
|
-
|
|
56
|
-
return planResolved;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
42
|
export function enforcePlanModeWrite(
|
|
60
43
|
session: ToolSession,
|
|
61
44
|
targetPath: string,
|
|
@@ -64,9 +47,6 @@ export function enforcePlanModeWrite(
|
|
|
64
47
|
const state = session.getPlanModeState?.();
|
|
65
48
|
if (!state?.enabled) return;
|
|
66
49
|
|
|
67
|
-
const resolvedTarget = resolvePlanPath(session, targetPath);
|
|
68
|
-
const resolvedPlan = resolvePlanPath(session, state.planFilePath);
|
|
69
|
-
|
|
70
50
|
if (options?.move) {
|
|
71
51
|
throw new ToolError("Plan mode: renaming files is not allowed.");
|
|
72
52
|
}
|
|
@@ -75,7 +55,9 @@ export function enforcePlanModeWrite(
|
|
|
75
55
|
throw new ToolError("Plan mode: deleting files is not allowed.");
|
|
76
56
|
}
|
|
77
57
|
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
if (targetsLocalSandbox(targetPath)) return;
|
|
59
|
+
|
|
60
|
+
throw new ToolError(
|
|
61
|
+
"Plan mode: the working tree is read-only. Write your plan to a local://<slug>-plan.md file instead.",
|
|
62
|
+
);
|
|
81
63
|
}
|
package/src/tools/read.ts
CHANGED
|
@@ -34,7 +34,7 @@ import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
|
34
34
|
import { ImageInputTooLargeError, loadImageInput, MAX_IMAGE_INPUT_BYTES } from "../utils/image-loading";
|
|
35
35
|
import { convertFileWithMarkit } from "../utils/markit";
|
|
36
36
|
import { buildDirectoryTree, type DirectoryTree } from "../workspace-tree";
|
|
37
|
-
import { type ArchiveReader, openArchive, parseArchivePathCandidates } from "./archive-reader";
|
|
37
|
+
import { type ArchiveReader, formatArchiveEntryLines, openArchive, parseArchivePathCandidates } from "./archive-reader";
|
|
38
38
|
import {
|
|
39
39
|
type ConflictEntry,
|
|
40
40
|
type ConflictScope,
|
|
@@ -1154,17 +1154,10 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1154
1154
|
const limitedEntries = listLimit.items;
|
|
1155
1155
|
const limitMeta = listLimit.meta;
|
|
1156
1156
|
|
|
1157
|
-
|
|
1158
|
-
for (const entry of limitedEntries) {
|
|
1157
|
+
for (let index = 0; index < limitedEntries.length; index++) {
|
|
1159
1158
|
throwIfAborted(signal);
|
|
1160
|
-
if (entry.isDirectory) {
|
|
1161
|
-
results.push(`${entry.name}/`);
|
|
1162
|
-
continue;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
const sizeSuffix = entry.size > 0 ? ` (${formatBytes(entry.size)})` : "";
|
|
1166
|
-
results.push(`${entry.name}${sizeSuffix}`);
|
|
1167
1159
|
}
|
|
1160
|
+
const results = formatArchiveEntryLines(limitedEntries);
|
|
1168
1161
|
|
|
1169
1162
|
const output = results.length > 0 ? results.join("\n") : "(empty archive directory)";
|
|
1170
1163
|
const text = prependSuffixResolutionNotice(output, details.suffixResolution);
|
|
@@ -2337,10 +2330,20 @@ function firstReadSelectorLine(sel: string | undefined): number | undefined {
|
|
|
2337
2330
|
}
|
|
2338
2331
|
}
|
|
2339
2332
|
|
|
2333
|
+
/** Absolute fs path the read result actually resolved to, used as the OSC 8 link
|
|
2334
|
+
* target when the structured `resolvedPath` isn't set (the common plain-file and
|
|
2335
|
+
* image reads only record the path in `meta.source`). URL/internal sources are
|
|
2336
|
+
* not fs paths, so only `type: "path"` qualifies. */
|
|
2337
|
+
function readSourceFsPath(details: ReadToolDetails | undefined): string | undefined {
|
|
2338
|
+
const source = details?.meta?.source;
|
|
2339
|
+
return source?.type === "path" ? source.value : undefined;
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2340
2342
|
function formatReadPathLink(
|
|
2341
2343
|
rawPath: string,
|
|
2342
2344
|
options: {
|
|
2343
2345
|
resolvedPath?: string;
|
|
2346
|
+
sourcePath?: string;
|
|
2344
2347
|
suffixResolution?: { from: string; to: string };
|
|
2345
2348
|
offset?: number;
|
|
2346
2349
|
fallbackLabel?: string;
|
|
@@ -2352,7 +2355,7 @@ function formatReadPathLink(
|
|
|
2352
2355
|
const plainDisplayPath = options.suffixResolution
|
|
2353
2356
|
? shortenPath(options.suffixResolution.to)
|
|
2354
2357
|
: shortenPath(basePath || options.resolvedPath || options.fallbackLabel || rawPath);
|
|
2355
|
-
const target = options.resolvedPath ?? tryResolveInternalUrlSync(basePath);
|
|
2358
|
+
const target = options.resolvedPath ?? options.sourcePath ?? tryResolveInternalUrlSync(basePath);
|
|
2356
2359
|
const line = firstReadSelectorLine(split.sel) ?? options.offset;
|
|
2357
2360
|
const linkOptions = line !== undefined ? { line } : undefined;
|
|
2358
2361
|
const displayPath = target ? fileHyperlink(target, plainDisplayPath, linkOptions) : plainDisplayPath;
|
|
@@ -2403,7 +2406,9 @@ export const readToolRenderer = {
|
|
|
2403
2406
|
const rawErrorText = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
2404
2407
|
const errorText = (rawErrorText || "Unknown error").replace(/^Error:\s*/, "");
|
|
2405
2408
|
const rawPath = args?.file_path || args?.path || "";
|
|
2406
|
-
const filePath =
|
|
2409
|
+
const filePath =
|
|
2410
|
+
formatReadPathLink(rawPath, { offset: args?.offset, sourcePath: readSourceFsPath(result.details) }) ||
|
|
2411
|
+
shortenPath(rawPath);
|
|
2407
2412
|
let title = filePath ? `Read ${filePath}` : "Read";
|
|
2408
2413
|
if (args?.offset !== undefined || args?.limit !== undefined) {
|
|
2409
2414
|
const startLine = args.offset ?? 1;
|
|
@@ -2454,6 +2459,7 @@ export const readToolRenderer = {
|
|
|
2454
2459
|
const suffix = details?.suffixResolution;
|
|
2455
2460
|
const displayPath = formatReadPathLink(rawPath, {
|
|
2456
2461
|
resolvedPath: details?.resolvedPath,
|
|
2462
|
+
sourcePath: readSourceFsPath(details),
|
|
2457
2463
|
suffixResolution: suffix,
|
|
2458
2464
|
fallbackLabel: "image",
|
|
2459
2465
|
});
|
|
@@ -2486,12 +2492,13 @@ export const readToolRenderer = {
|
|
|
2486
2492
|
}
|
|
2487
2493
|
|
|
2488
2494
|
const suffix = details?.suffixResolution;
|
|
2489
|
-
// resolvedPath is the absolute fs path
|
|
2490
|
-
//
|
|
2491
|
-
//
|
|
2492
|
-
//
|
|
2495
|
+
// resolvedPath is the absolute fs path when a read resolved/corrected the
|
|
2496
|
+
// input (suffix match, internal URL, archive/sqlite/notebook); plain file
|
|
2497
|
+
// reads only record the absolute path in meta.source, so fall back to that
|
|
2498
|
+
// (and then to a sync internal-URL resolver) to keep the title clickable.
|
|
2493
2499
|
const displayPath = formatReadPathLink(rawPath, {
|
|
2494
2500
|
resolvedPath: details?.resolvedPath,
|
|
2501
|
+
sourcePath: readSourceFsPath(details),
|
|
2495
2502
|
suffixResolution: suffix,
|
|
2496
2503
|
offset: args?.offset,
|
|
2497
2504
|
});
|
|
@@ -10,8 +10,9 @@ import * as path from "node:path";
|
|
|
10
10
|
import type { ToolCallContext } from "@oh-my-pi/pi-agent-core";
|
|
11
11
|
import type { Ellipsis } from "@oh-my-pi/pi-natives";
|
|
12
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
13
|
-
import { replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
13
|
+
import { getKeybindings, replaceTabs, truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import { pluralize } from "@oh-my-pi/pi-utils";
|
|
15
|
+
import { formatKeyHints, type KeyId } from "../config/keybindings";
|
|
15
16
|
import { settings } from "../config/settings";
|
|
16
17
|
import type { Theme } from "../modes/theme/theme";
|
|
17
18
|
import { Hasher } from "../tui/utils";
|
|
@@ -75,8 +76,16 @@ export const TRUNCATE_LENGTHS = {
|
|
|
75
76
|
SHORT: 40,
|
|
76
77
|
} as const;
|
|
77
78
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
79
|
+
/** Keybinding action that toggles tool-output expansion. */
|
|
80
|
+
const EXPAND_ACTION = "app.tools.expand";
|
|
81
|
+
/** Fallback key when no binding is resolvable (e.g. outside an interactive session). */
|
|
82
|
+
const DEFAULT_EXPAND_KEY: KeyId = "ctrl+o";
|
|
83
|
+
|
|
84
|
+
/** Human-readable key currently bound to tool-output expansion, e.g. `Ctrl+O`. */
|
|
85
|
+
export function expandKeyHint(): string {
|
|
86
|
+
const keys = getKeybindings().getKeys(EXPAND_ACTION);
|
|
87
|
+
return formatKeyHints(keys.length > 0 ? keys : [DEFAULT_EXPAND_KEY]);
|
|
88
|
+
}
|
|
80
89
|
|
|
81
90
|
// =============================================================================
|
|
82
91
|
// Text Truncation Utilities
|
|
@@ -150,7 +159,7 @@ export function formatStatusIcon(status: ToolUIStatus, theme: Theme, spinnerFram
|
|
|
150
159
|
export function formatExpandHint(theme: Theme, expanded?: boolean, hasMore?: boolean): string {
|
|
151
160
|
if (expanded) return "";
|
|
152
161
|
if (hasMore === false) return "";
|
|
153
|
-
return theme.fg("dim", wrapBrackets(
|
|
162
|
+
return theme.fg("dim", wrapBrackets(`${expandKeyHint()}: Expand`, theme));
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
/**
|
|
@@ -221,10 +230,24 @@ export function formatMeta(meta: string[], theme: Theme): string {
|
|
|
221
230
|
return meta.length > 0 ? ` ${theme.fg("muted", meta.join(theme.sep.dot))}` : "";
|
|
222
231
|
}
|
|
223
232
|
|
|
224
|
-
|
|
233
|
+
function sanitizeErrorText(message: string | undefined): string {
|
|
225
234
|
const clean = (message ?? "").replace(/^Error:\s*/, "").trim();
|
|
226
|
-
|
|
227
|
-
|
|
235
|
+
return clean ? replaceTabs(truncateToWidth(clean, TRUNCATE_LENGTHS.LINE)) : "Unknown error";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function formatErrorMessage(message: string | undefined, theme: Theme): string {
|
|
239
|
+
return `${theme.styledSymbol("status.error", "error")} ${theme.fg("error", `Error: ${sanitizeErrorText(message)}`)}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Error message rendered as a subordinate detail line beneath a status header
|
|
244
|
+
* that already carries the error icon (e.g. `✘ Write: <path>`). The header's
|
|
245
|
+
* icon already signals failure, so this omits the redundant error symbol and
|
|
246
|
+
* "Error:" prefix that `formatErrorMessage` adds for standalone single-line
|
|
247
|
+
* errors, indenting two columns to sit under the header title instead.
|
|
248
|
+
*/
|
|
249
|
+
export function formatErrorDetail(message: string | undefined, theme: Theme): string {
|
|
250
|
+
return ` ${theme.fg("error", sanitizeErrorText(message))}`;
|
|
228
251
|
}
|
|
229
252
|
|
|
230
253
|
export function formatEmptyMessage(message: string, theme: Theme): string {
|
|
@@ -722,36 +745,6 @@ export function capParseErrors(
|
|
|
722
745
|
// Renderer helpers shared by search / find / ast tools
|
|
723
746
|
// =============================================================================
|
|
724
747
|
|
|
725
|
-
/**
|
|
726
|
-
* Group `rawLines` by blank-line separators, mirroring the historical search /
|
|
727
|
-
* ast-grep / ast-edit renderer behavior: if any blank line is present, splits on
|
|
728
|
-
* runs of blank lines; otherwise collapses non-empty lines into a single group.
|
|
729
|
-
*/
|
|
730
|
-
export function splitGroupsByBlankLine(rawLines: string[]): string[][] {
|
|
731
|
-
const hasSeparators = rawLines.some(line => line.trim().length === 0);
|
|
732
|
-
const groups: string[][] = [];
|
|
733
|
-
if (hasSeparators) {
|
|
734
|
-
let current: string[] = [];
|
|
735
|
-
for (const line of rawLines) {
|
|
736
|
-
if (line.trim().length === 0) {
|
|
737
|
-
if (current.length > 0) {
|
|
738
|
-
groups.push(current);
|
|
739
|
-
current = [];
|
|
740
|
-
}
|
|
741
|
-
continue;
|
|
742
|
-
}
|
|
743
|
-
current.push(line);
|
|
744
|
-
}
|
|
745
|
-
if (current.length > 0) groups.push(current);
|
|
746
|
-
} else {
|
|
747
|
-
const nonEmpty = rawLines.filter(line => line.trim().length > 0);
|
|
748
|
-
if (nonEmpty.length > 0) {
|
|
749
|
-
groups.push(nonEmpty);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return groups;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
748
|
/**
|
|
756
749
|
* Standard width+expand keyed render cache used by every search-style tool
|
|
757
750
|
* renderer. `compute` re-runs only when the cache key changes; the returned
|
|
@@ -760,6 +753,7 @@ export function splitGroupsByBlankLine(rawLines: string[]): string[][] {
|
|
|
760
753
|
export function createCachedComponent(
|
|
761
754
|
getExpanded: () => boolean,
|
|
762
755
|
compute: (width: number, expanded: boolean) => string[],
|
|
756
|
+
options: { paddingX?: number } = {},
|
|
763
757
|
): Component {
|
|
764
758
|
let cached: { key: bigint; lines: string[] } | undefined;
|
|
765
759
|
return {
|
|
@@ -767,9 +761,13 @@ export function createCachedComponent(
|
|
|
767
761
|
const expanded = getExpanded();
|
|
768
762
|
const key = new Hasher().bool(expanded).u32(width).digest();
|
|
769
763
|
if (cached?.key === key) return cached.lines;
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
764
|
+
const paddingX = Math.max(0, options.paddingX ?? 0);
|
|
765
|
+
const innerWidth = Math.max(1, width - paddingX * 2);
|
|
766
|
+
const lines = compute(innerWidth, expanded);
|
|
767
|
+
const pad = paddingX === 0 ? "" : " ".repeat(paddingX);
|
|
768
|
+
const paddedLines = paddingX === 0 ? lines : lines.map(line => `${pad}${line}${pad}`);
|
|
769
|
+
cached = { key, lines: paddedLines };
|
|
770
|
+
return paddedLines;
|
|
773
771
|
},
|
|
774
772
|
invalidate() {
|
|
775
773
|
cached = undefined;
|
package/src/tools/renderers.ts
CHANGED
|
@@ -31,7 +31,7 @@ import { sshToolRenderer } from "./ssh";
|
|
|
31
31
|
import { todoToolRenderer } from "./todo";
|
|
32
32
|
import { writeToolRenderer } from "./write";
|
|
33
33
|
|
|
34
|
-
type ToolRenderer = {
|
|
34
|
+
export type ToolRenderer = {
|
|
35
35
|
renderCall: (args: unknown, options: RenderResultOptions, theme: Theme) => Component;
|
|
36
36
|
renderResult: (
|
|
37
37
|
result: { content: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },
|
|
@@ -40,6 +40,21 @@ type ToolRenderer = {
|
|
|
40
40
|
args?: unknown,
|
|
41
41
|
) => Component;
|
|
42
42
|
mergeCallAndResult?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* While a tool's preview is still streaming, report whether the
|
|
45
|
+
* currently-rendered preview is append-only: its rows only grow at the bottom
|
|
46
|
+
* and never re-layout above the bottom live region (a full, top-anchored
|
|
47
|
+
* content/code preview). The transcript reports this up to the TUI so a
|
|
48
|
+
* streaming preview taller than the viewport commits its scrolled-off head to
|
|
49
|
+
* native scrollback instead of dropping it (see
|
|
50
|
+
* `ToolExecutionComponent.isTranscriptBlockAppendOnly`). `result` is the
|
|
51
|
+
* latest (possibly partial) tool result, or `undefined` before one exists —
|
|
52
|
+
* `eval`/`bash` use its presence to defer committing until the streamed input
|
|
53
|
+
* (code) has finalized. Omit (or return `false`) for previews that slide a
|
|
54
|
+
* tail window or later collapse to a compact result — committing their head
|
|
55
|
+
* would strand stale rows.
|
|
56
|
+
*/
|
|
57
|
+
isStreamingPreviewAppendOnly?: (args: unknown, options: RenderResultOptions, result?: unknown) => boolean;
|
|
43
58
|
/** Render without background box, inline in the response flow */
|
|
44
59
|
inline?: boolean;
|
|
45
60
|
};
|
|
@@ -41,7 +41,7 @@ function buildReportToolIssueParams(activeBuiltinNames: readonly string[]) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export function isAutoQaEnabled(settings?: Settings): boolean {
|
|
44
|
-
return $flag("PI_AUTO_QA"
|
|
44
|
+
return $flag("PI_AUTO_QA", !!settings?.get("dev.autoqa"));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// ───────────────────────────────────────────────────────────────────────────
|
package/src/tools/resolve.ts
CHANGED
|
@@ -187,6 +187,20 @@ export class ResolveTool implements AgentTool<typeof resolveSchema, ResolveToolD
|
|
|
187
187
|
return untilAborted(signal, async () => {
|
|
188
188
|
const invoker = this.session.peekQueueInvoker?.() ?? this.session.peekStandingResolveHandler?.();
|
|
189
189
|
if (!invoker) {
|
|
190
|
+
// `discard` is a request to cancel/abort a staged action. When nothing is
|
|
191
|
+
// pending, the desired end-state (no staged change) already holds, so honor
|
|
192
|
+
// it as a successful cancellation instead of surfacing a hard error to the
|
|
193
|
+
// model. `apply` still errors — there is nothing to apply.
|
|
194
|
+
if (params.action === "discard") {
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text" as const, text: "Nothing to discard; no pending action remains." }],
|
|
197
|
+
details: {
|
|
198
|
+
action: "discard",
|
|
199
|
+
reason: params.reason,
|
|
200
|
+
...(params.extra != null ? { extra: params.extra } : {}),
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
190
204
|
throw new ToolError("No pending action to resolve. Nothing to apply or discard.");
|
|
191
205
|
}
|
|
192
206
|
const result = (await invoker(params)) as AgentToolResult<ResolveToolDetails>;
|