@oh-my-pi/pi-coding-agent 6.7.0 → 6.7.67
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 +14 -0
- package/package.json +5 -5
- package/src/core/agent-session.ts +1 -1
- package/src/core/settings-manager.ts +20 -0
- package/src/core/tools/index.ts +1 -0
- package/src/core/tools/patch/normative.ts +27 -3
- package/src/core/tools/patch/shared.ts +31 -2
- package/src/core/tools/read.ts +4 -2
- package/src/core/tools/renderers.ts +5 -1
- package/src/core/tools/write.ts +35 -3
- package/src/modes/interactive/components/settings-defs.ts +18 -0
- package/src/modes/interactive/components/tool-execution.ts +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [6.7.67] - 2026-01-19
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added normative rewrite setting to control tool call argument normalization in session history
|
|
9
|
+
- Added read line numbers setting to prepend line numbers to read tool output by default
|
|
10
|
+
- Added streaming preview for edit and write tools with spinner animation
|
|
11
|
+
- Added automatic anchor derivation for normative patches when anchors not specified
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Enhanced edit and write tool renderers to show streaming content preview
|
|
16
|
+
- Updated read tool to respect default line numbers setting
|
|
17
|
+
- Improved normative patch anchor handling to support undefined anchors
|
|
18
|
+
|
|
5
19
|
## [6.7.0] - 2026-01-19
|
|
6
20
|
|
|
7
21
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "6.7.
|
|
3
|
+
"version": "6.7.67",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"prepublishOnly": "bun run generate-template && bun run clean && bun run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@oh-my-pi/pi-agent-core": "6.7.
|
|
44
|
-
"@oh-my-pi/pi-ai": "6.7.
|
|
45
|
-
"@oh-my-pi/pi-git-tool": "6.7.
|
|
46
|
-
"@oh-my-pi/pi-tui": "6.7.
|
|
43
|
+
"@oh-my-pi/pi-agent-core": "6.7.67",
|
|
44
|
+
"@oh-my-pi/pi-ai": "6.7.67",
|
|
45
|
+
"@oh-my-pi/pi-git-tool": "6.7.67",
|
|
46
|
+
"@oh-my-pi/pi-tui": "6.7.67",
|
|
47
47
|
"@openai/agents": "^0.3.7",
|
|
48
48
|
"@sinclair/typebox": "^0.34.46",
|
|
49
49
|
"ajv": "^8.17.1",
|
|
@@ -440,7 +440,7 @@ export class AgentSession {
|
|
|
440
440
|
details?: unknown;
|
|
441
441
|
$normative?: Record<string, unknown>;
|
|
442
442
|
};
|
|
443
|
-
if ($normative && toolCallId) {
|
|
443
|
+
if ($normative && toolCallId && this.settingsManager.getNormativeRewrite()) {
|
|
444
444
|
await this._rewriteToolCallArgs(toolCallId, $normative);
|
|
445
445
|
}
|
|
446
446
|
}
|
|
@@ -236,6 +236,8 @@ export interface Settings {
|
|
|
236
236
|
disabledExtensions?: string[]; // Individual extension IDs that are disabled (e.g., "skill:commit")
|
|
237
237
|
statusLine?: StatusLineSettings; // Status line configuration
|
|
238
238
|
showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME
|
|
239
|
+
normativeRewrite?: boolean; // default: false (rewrite tool call arguments to normalized format in session history)
|
|
240
|
+
readLineNumbers?: boolean; // default: false (prepend line numbers to read tool output by default)
|
|
239
241
|
}
|
|
240
242
|
|
|
241
243
|
export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
|
|
@@ -1299,6 +1301,24 @@ export class SettingsManager {
|
|
|
1299
1301
|
await this.save();
|
|
1300
1302
|
}
|
|
1301
1303
|
|
|
1304
|
+
getNormativeRewrite(): boolean {
|
|
1305
|
+
return this.settings.normativeRewrite ?? false;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
async setNormativeRewrite(enabled: boolean): Promise<void> {
|
|
1309
|
+
this.globalSettings.normativeRewrite = enabled;
|
|
1310
|
+
await this.save();
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
getReadLineNumbers(): boolean {
|
|
1314
|
+
return this.settings.readLineNumbers ?? false;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
async setReadLineNumbers(enabled: boolean): Promise<void> {
|
|
1318
|
+
this.globalSettings.readLineNumbers = enabled;
|
|
1319
|
+
await this.save();
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1302
1322
|
getDisabledProviders(): string[] {
|
|
1303
1323
|
return [...(this.settings.disabledProviders ?? [])];
|
|
1304
1324
|
}
|
package/src/core/tools/index.ts
CHANGED
|
@@ -125,6 +125,7 @@ export interface ToolSession {
|
|
|
125
125
|
/** Settings manager (optional) */
|
|
126
126
|
settings?: {
|
|
127
127
|
getImageAutoResize(): boolean;
|
|
128
|
+
getReadLineNumbers?(): boolean;
|
|
128
129
|
getLspFormatOnWrite(): boolean;
|
|
129
130
|
getLspDiagnosticsOnWrite(): boolean;
|
|
130
131
|
getLspDiagnosticsOnEdit(): boolean;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { generateUnifiedDiffString } from "./diff";
|
|
6
6
|
import { normalizeToLF, stripBom } from "./normalize";
|
|
7
|
+
import { parseHunks } from "./parser";
|
|
7
8
|
import type { PatchInput } from "./types";
|
|
8
9
|
|
|
9
10
|
export interface NormativePatchOptions {
|
|
@@ -17,7 +18,7 @@ export interface NormativePatchOptions {
|
|
|
17
18
|
|
|
18
19
|
/** Normative patch input is the MongoDB-style update variant */
|
|
19
20
|
|
|
20
|
-
function applyAnchors(diff: string, anchors: string
|
|
21
|
+
function applyAnchors(diff: string, anchors: Array<string | undefined> | undefined): string {
|
|
21
22
|
if (!anchors || anchors.length === 0) {
|
|
22
23
|
return diff;
|
|
23
24
|
}
|
|
@@ -28,17 +29,40 @@ function applyAnchors(diff: string, anchors: string[] | undefined): string {
|
|
|
28
29
|
const anchor = anchors[anchorIndex];
|
|
29
30
|
if (anchor !== undefined) {
|
|
30
31
|
lines[i] = anchor.trim().length === 0 ? "@@" : `@@ ${anchor}`;
|
|
31
|
-
anchorIndex++;
|
|
32
32
|
}
|
|
33
|
+
anchorIndex++;
|
|
33
34
|
}
|
|
34
35
|
return lines.join("\n");
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
function deriveAnchors(diff: string): Array<string | undefined> {
|
|
39
|
+
const hunks = parseHunks(diff);
|
|
40
|
+
return hunks.map((hunk) => {
|
|
41
|
+
if (hunk.oldLines.length === 0 || hunk.newLines.length === 0) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const newLines = new Set(hunk.newLines);
|
|
45
|
+
for (const line of hunk.oldLines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (trimmed.length === 0) continue;
|
|
48
|
+
if (!/[A-Za-z0-9_]/.test(trimmed)) continue;
|
|
49
|
+
if (newLines.has(line)) {
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
export function buildNormativeUpdateInput(options: NormativePatchOptions): PatchInput {
|
|
38
58
|
const normalizedOld = normalizeToLF(stripBom(options.oldContent).text);
|
|
39
59
|
const normalizedNew = normalizeToLF(stripBom(options.newContent).text);
|
|
40
60
|
const diffResult = generateUnifiedDiffString(normalizedOld, normalizedNew, options.contextLines ?? 3);
|
|
41
|
-
|
|
61
|
+
let anchors: Array<string | undefined> | undefined =
|
|
62
|
+
typeof options.anchor === "string" ? [options.anchor] : options.anchor;
|
|
63
|
+
if (!anchors) {
|
|
64
|
+
anchors = deriveAnchors(diffResult.diff);
|
|
65
|
+
}
|
|
42
66
|
const diff = applyAnchors(diffResult.diff, anchors);
|
|
43
67
|
return {
|
|
44
68
|
path: options.path,
|
|
@@ -5,17 +5,20 @@
|
|
|
5
5
|
import type { ToolCallContext } from "@oh-my-pi/pi-agent-core";
|
|
6
6
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
8
|
+
import { renderDiff as renderDiffColored } from "../../../modes/interactive/components/diff";
|
|
8
9
|
import { getLanguageFromPath, type Theme } from "../../../modes/interactive/theme/theme";
|
|
9
10
|
import type { RenderResultOptions } from "../../custom-tools/types";
|
|
10
11
|
import type { FileDiagnosticsResult } from "../lsp/index";
|
|
11
12
|
import {
|
|
12
13
|
createToolUIKit,
|
|
13
14
|
formatExpandHint,
|
|
15
|
+
formatStatusIcon,
|
|
14
16
|
getDiffStats,
|
|
15
17
|
shortenPath,
|
|
16
18
|
type ToolUIKit,
|
|
17
19
|
truncateDiffByHunk,
|
|
18
20
|
} from "../render-utils";
|
|
21
|
+
import type { RenderCallOptions } from "../renderers";
|
|
19
22
|
import type { DiffError, DiffResult, Operation } from "./types";
|
|
20
23
|
|
|
21
24
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -82,12 +85,29 @@ export interface EditRenderContext {
|
|
|
82
85
|
|
|
83
86
|
const EDIT_DIFF_PREVIEW_HUNKS = 2;
|
|
84
87
|
const EDIT_DIFF_PREVIEW_LINES = 24;
|
|
88
|
+
const EDIT_STREAMING_PREVIEW_LINES = 12;
|
|
85
89
|
|
|
86
90
|
function countLines(text: string): number {
|
|
87
91
|
if (!text) return 0;
|
|
88
92
|
return text.split("\n").length;
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
function formatStreamingDiff(diff: string, rawPath: string, uiTheme: Theme): string {
|
|
96
|
+
if (!diff) return "";
|
|
97
|
+
const lines = diff.split("\n");
|
|
98
|
+
const total = lines.length;
|
|
99
|
+
const displayLines = lines.slice(-EDIT_STREAMING_PREVIEW_LINES);
|
|
100
|
+
const hidden = total - displayLines.length;
|
|
101
|
+
|
|
102
|
+
let text = "\n\n";
|
|
103
|
+
if (hidden > 0) {
|
|
104
|
+
text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${hidden} earlier lines)\n`);
|
|
105
|
+
}
|
|
106
|
+
text += renderDiffColored(displayLines.join("\n"), { filePath: rawPath });
|
|
107
|
+
text += uiTheme.fg("dim", `\n${uiTheme.format.ellipsis} (streaming)`);
|
|
108
|
+
return text;
|
|
109
|
+
}
|
|
110
|
+
|
|
91
111
|
function formatMetadataLine(lineCount: number | null, language: string | undefined, uiTheme: Theme): string {
|
|
92
112
|
const icon = uiTheme.getLangIcon(language);
|
|
93
113
|
if (lineCount !== null) {
|
|
@@ -136,7 +156,7 @@ function renderDiffSection(
|
|
|
136
156
|
export const editToolRenderer = {
|
|
137
157
|
mergeCallAndResult: true,
|
|
138
158
|
|
|
139
|
-
renderCall(args: EditRenderArgs, uiTheme: Theme): Component {
|
|
159
|
+
renderCall(args: EditRenderArgs, uiTheme: Theme, options?: RenderCallOptions): Component {
|
|
140
160
|
const ui = createToolUIKit(uiTheme);
|
|
141
161
|
const rawPath = args.file_path || args.path || "";
|
|
142
162
|
const filePath = shortenPath(rawPath);
|
|
@@ -151,7 +171,16 @@ export const editToolRenderer = {
|
|
|
151
171
|
|
|
152
172
|
// Show operation type for patch mode
|
|
153
173
|
const opTitle = args.op === "create" ? "Create" : args.op === "delete" ? "Delete" : "Edit";
|
|
154
|
-
const
|
|
174
|
+
const spinner =
|
|
175
|
+
options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
|
|
176
|
+
let text = `${ui.title(opTitle)} ${spinner ? `${spinner} ` : ""}${editIcon} ${pathDisplay}`;
|
|
177
|
+
|
|
178
|
+
// Show streaming preview of diff/content
|
|
179
|
+
const streamingContent = args.diff ?? args.newText ?? args.patch;
|
|
180
|
+
if (streamingContent) {
|
|
181
|
+
text += formatStreamingDiff(streamingContent, rawPath, uiTheme);
|
|
182
|
+
}
|
|
183
|
+
|
|
155
184
|
return new Text(text, 0, 0);
|
|
156
185
|
},
|
|
157
186
|
|
package/src/core/tools/read.ts
CHANGED
|
@@ -441,11 +441,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
441
441
|
|
|
442
442
|
private readonly session: ToolSession;
|
|
443
443
|
private readonly autoResizeImages: boolean;
|
|
444
|
+
private readonly defaultLineNumbers: boolean;
|
|
444
445
|
private readonly lsTool: LsTool;
|
|
445
446
|
|
|
446
447
|
constructor(session: ToolSession) {
|
|
447
448
|
this.session = session;
|
|
448
449
|
this.autoResizeImages = session.settings?.getImageAutoResize() ?? true;
|
|
450
|
+
this.defaultLineNumbers = session.settings?.getReadLineNumbers?.() ?? false;
|
|
449
451
|
this.lsTool = new LsTool(session);
|
|
450
452
|
this.description = renderPromptTemplate(readDescription, {
|
|
451
453
|
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
@@ -622,8 +624,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
622
624
|
// Apply truncation (respects both line and byte limits)
|
|
623
625
|
const truncation = truncateHead(selectedContent);
|
|
624
626
|
|
|
625
|
-
// Add line numbers if requested (default
|
|
626
|
-
const shouldAddLineNumbers = lines
|
|
627
|
+
// Add line numbers if requested (uses setting default if not specified)
|
|
628
|
+
const shouldAddLineNumbers = lines ?? this.defaultLineNumbers;
|
|
627
629
|
const prependLineNumbers = (text: string, startNum: number): string => {
|
|
628
630
|
const textLines = text.split("\n");
|
|
629
631
|
const lastLineNum = startNum + textLines.length - 1;
|
|
@@ -26,8 +26,12 @@ import { webFetchToolRenderer } from "./web-fetch";
|
|
|
26
26
|
import { webSearchToolRenderer } from "./web-search/render";
|
|
27
27
|
import { writeToolRenderer } from "./write";
|
|
28
28
|
|
|
29
|
+
export interface RenderCallOptions {
|
|
30
|
+
spinnerFrame?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
type ToolRenderer = {
|
|
30
|
-
renderCall: (args: unknown, theme: Theme) => Component;
|
|
34
|
+
renderCall: (args: unknown, theme: Theme, options?: RenderCallOptions) => Component;
|
|
31
35
|
renderResult: (
|
|
32
36
|
result: { content: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },
|
|
33
37
|
options: RenderResultOptions & { renderContext?: Record<string, unknown> },
|
package/src/core/tools/write.ts
CHANGED
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
writethroughNoop,
|
|
22
22
|
} from "./lsp/index";
|
|
23
23
|
import { resolveToCwd } from "./path-utils";
|
|
24
|
-
import { formatDiagnostics, formatExpandHint, replaceTabs, shortenPath } from "./render-utils";
|
|
24
|
+
import { formatDiagnostics, formatExpandHint, formatStatusIcon, replaceTabs, shortenPath } from "./render-utils";
|
|
25
|
+
import type { RenderCallOptions } from "./renderers";
|
|
25
26
|
|
|
26
27
|
const writeSchema = Type.Object({
|
|
27
28
|
path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
|
|
@@ -124,11 +125,34 @@ interface WriteRenderArgs {
|
|
|
124
125
|
content?: string;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
const WRITE_STREAMING_PREVIEW_LINES = 12;
|
|
129
|
+
|
|
127
130
|
function countLines(text: string): number {
|
|
128
131
|
if (!text) return 0;
|
|
129
132
|
return text.split("\n").length;
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
function formatStreamingContent(content: string, rawPath: string, uiTheme: Theme): string {
|
|
136
|
+
if (!content) return "";
|
|
137
|
+
const lang = getLanguageFromPath(rawPath);
|
|
138
|
+
const lines = content.split("\n");
|
|
139
|
+
const total = lines.length;
|
|
140
|
+
const displayLines = lines.slice(-WRITE_STREAMING_PREVIEW_LINES);
|
|
141
|
+
const hidden = total - displayLines.length;
|
|
142
|
+
|
|
143
|
+
const formattedLines = lang
|
|
144
|
+
? highlightCode(replaceTabs(displayLines.join("\n")), lang)
|
|
145
|
+
: displayLines.map((line: string) => uiTheme.fg("toolOutput", replaceTabs(line)));
|
|
146
|
+
|
|
147
|
+
let text = "\n\n";
|
|
148
|
+
if (hidden > 0) {
|
|
149
|
+
text += uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${hidden} earlier lines)\n`);
|
|
150
|
+
}
|
|
151
|
+
text += formattedLines.join("\n");
|
|
152
|
+
text += uiTheme.fg("dim", `\n${uiTheme.format.ellipsis} (streaming)`);
|
|
153
|
+
return text;
|
|
154
|
+
}
|
|
155
|
+
|
|
132
156
|
function formatMetadataLine(lineCount: number | null, language: string | undefined, uiTheme: Theme): string {
|
|
133
157
|
const icon = uiTheme.getLangIcon(language);
|
|
134
158
|
if (lineCount !== null) {
|
|
@@ -138,11 +162,19 @@ function formatMetadataLine(lineCount: number | null, language: string | undefin
|
|
|
138
162
|
}
|
|
139
163
|
|
|
140
164
|
export const writeToolRenderer = {
|
|
141
|
-
renderCall(args: WriteRenderArgs, uiTheme: Theme): Component {
|
|
165
|
+
renderCall(args: WriteRenderArgs, uiTheme: Theme, options?: RenderCallOptions): Component {
|
|
142
166
|
const rawPath = args.file_path || args.path || "";
|
|
143
167
|
const filePath = shortenPath(rawPath);
|
|
144
168
|
const pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", uiTheme.format.ellipsis);
|
|
145
|
-
const
|
|
169
|
+
const spinner =
|
|
170
|
+
options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
|
|
171
|
+
let text = `${uiTheme.fg("toolTitle", uiTheme.bold("Write"))} ${spinner ? `${spinner} ` : ""}${pathDisplay}`;
|
|
172
|
+
|
|
173
|
+
// Show streaming preview of content
|
|
174
|
+
if (args.content) {
|
|
175
|
+
text += formatStreamingContent(args.content, rawPath, uiTheme);
|
|
176
|
+
}
|
|
177
|
+
|
|
146
178
|
return new Text(text, 0, 0);
|
|
147
179
|
},
|
|
148
180
|
|
|
@@ -209,6 +209,15 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
209
209
|
get: (sm) => sm.getCollapseChangelog(),
|
|
210
210
|
set: (sm, v) => sm.setCollapseChangelog(v),
|
|
211
211
|
},
|
|
212
|
+
{
|
|
213
|
+
id: "normativeRewrite",
|
|
214
|
+
tab: "behavior",
|
|
215
|
+
type: "boolean",
|
|
216
|
+
label: "Normative rewrite",
|
|
217
|
+
description: "Rewrite tool call arguments to normalized format in session history",
|
|
218
|
+
get: (sm) => sm.getNormativeRewrite(),
|
|
219
|
+
set: (sm, v) => sm.setNormativeRewrite(v),
|
|
220
|
+
},
|
|
212
221
|
{
|
|
213
222
|
id: "doubleEscapeAction",
|
|
214
223
|
tab: "behavior",
|
|
@@ -312,6 +321,15 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
312
321
|
get: (sm) => sm.getEditPatchMode(),
|
|
313
322
|
set: (sm, v) => sm.setEditPatchMode(v),
|
|
314
323
|
},
|
|
324
|
+
{
|
|
325
|
+
id: "readLineNumbers",
|
|
326
|
+
tab: "tools",
|
|
327
|
+
type: "boolean",
|
|
328
|
+
label: "Read line numbers",
|
|
329
|
+
description: "Prepend line numbers to read tool output by default",
|
|
330
|
+
get: (sm) => sm.getReadLineNumbers(),
|
|
331
|
+
set: (sm, v) => sm.setReadLineNumbers(v),
|
|
332
|
+
},
|
|
315
333
|
{
|
|
316
334
|
id: "mcpProjectConfig",
|
|
317
335
|
tab: "tools",
|
|
@@ -135,6 +135,8 @@ export class ToolExecutionComponent extends Container {
|
|
|
135
135
|
// Spinner animation for partial task results
|
|
136
136
|
private spinnerFrame = 0;
|
|
137
137
|
private spinnerInterval: ReturnType<typeof setInterval> | null = null;
|
|
138
|
+
// Track if args are still being streamed (for edit/write spinner)
|
|
139
|
+
private argsComplete = false;
|
|
138
140
|
|
|
139
141
|
constructor(
|
|
140
142
|
toolName: string,
|
|
@@ -175,6 +177,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
175
177
|
|
|
176
178
|
updateArgs(args: any, _toolCallId?: string): void {
|
|
177
179
|
this.args = args;
|
|
180
|
+
this.updateSpinnerAnimation();
|
|
178
181
|
this.updateDisplay();
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -183,6 +186,8 @@ export class ToolExecutionComponent extends Container {
|
|
|
183
186
|
* This triggers diff computation for edit tool.
|
|
184
187
|
*/
|
|
185
188
|
setArgsComplete(_toolCallId?: string): void {
|
|
189
|
+
this.argsComplete = true;
|
|
190
|
+
this.updateSpinnerAnimation();
|
|
186
191
|
this.maybeComputeEditDiff();
|
|
187
192
|
}
|
|
188
193
|
|
|
@@ -311,7 +316,10 @@ export class ToolExecutionComponent extends Container {
|
|
|
311
316
|
* Start or stop spinner animation based on whether this is a partial task result.
|
|
312
317
|
*/
|
|
313
318
|
private updateSpinnerAnimation(): void {
|
|
314
|
-
|
|
319
|
+
// Spinner for: task tool with partial result, or edit/write while args streaming
|
|
320
|
+
const isStreamingArgs = !this.argsComplete && (this.toolName === "edit" || this.toolName === "write");
|
|
321
|
+
const isPartialTask = this.isPartial && this.toolName === "task";
|
|
322
|
+
const needsSpinner = isStreamingArgs || isPartialTask;
|
|
315
323
|
if (needsSpinner && !this.spinnerInterval) {
|
|
316
324
|
this.spinnerInterval = setInterval(() => {
|
|
317
325
|
const frameCount = theme.spinnerFrames.length;
|
|
@@ -428,7 +436,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
428
436
|
if (shouldRenderCall) {
|
|
429
437
|
// Render call component
|
|
430
438
|
try {
|
|
431
|
-
const callComponent = renderer.renderCall(this.args, theme
|
|
439
|
+
const callComponent = renderer.renderCall(this.args, theme, {
|
|
440
|
+
spinnerFrame: this.spinnerFrame,
|
|
441
|
+
});
|
|
432
442
|
if (callComponent) {
|
|
433
443
|
// Ensure component has invalidate() method for Component interface
|
|
434
444
|
const component = callComponent as any;
|