@bubblebrain-ai/bubble 0.0.13 → 0.0.14
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/dist/agent/execution-governor.js +1 -1
- package/dist/agent/tool-intent.js +1 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +589 -316
- package/dist/approval/controller.d.ts +1 -0
- package/dist/approval/controller.js +20 -3
- package/dist/approval/tool-helper.js +2 -0
- package/dist/approval/types.d.ts +14 -1
- package/dist/context/compact.js +9 -3
- package/dist/context/projector.js +27 -12
- package/dist/debug-trace.d.ts +27 -0
- package/dist/debug-trace.js +385 -0
- package/dist/feishu/agent-host/approval-card.js +9 -0
- package/dist/feishu/serve.js +7 -1
- package/dist/main.js +28 -0
- package/dist/model-catalog.js +1 -0
- package/dist/orchestrator/default-hooks.js +19 -8
- package/dist/orchestrator/hooks.d.ts +1 -0
- package/dist/prompt/environment.js +2 -0
- package/dist/prompt/reminders.d.ts +5 -6
- package/dist/prompt/reminders.js +8 -9
- package/dist/prompt/runtime.js +2 -2
- package/dist/provider-openai-codex.d.ts +7 -0
- package/dist/provider-openai-codex.js +265 -124
- package/dist/provider-registry.d.ts +2 -0
- package/dist/provider-registry.js +58 -9
- package/dist/provider.d.ts +3 -0
- package/dist/provider.js +5 -1
- package/dist/session-log.js +13 -1
- package/dist/slash-commands/commands.js +12 -0
- package/dist/slash-commands/types.d.ts +2 -0
- package/dist/stats/usage.d.ts +52 -0
- package/dist/stats/usage.js +414 -0
- package/dist/tools/apply-patch.d.ts +9 -0
- package/dist/tools/apply-patch.js +330 -0
- package/dist/tools/bash.js +205 -44
- package/dist/tools/edit-apply.d.ts +5 -2
- package/dist/tools/edit-apply.js +221 -31
- package/dist/tools/edit.js +12 -3
- package/dist/tools/file-mutation-queue.d.ts +1 -0
- package/dist/tools/file-mutation-queue.js +12 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +7 -1
- package/dist/tools/patch-apply.d.ts +41 -0
- package/dist/tools/patch-apply.js +312 -0
- package/dist/tools/server-manager.d.ts +36 -0
- package/dist/tools/server-manager.js +234 -0
- package/dist/tools/server.d.ts +6 -0
- package/dist/tools/server.js +245 -0
- package/dist/tools/write.d.ts +3 -6
- package/dist/tools/write.js +26 -46
- package/dist/tui/display-history.d.ts +1 -0
- package/dist/tui/display-history.js +5 -4
- package/dist/tui/edit-diff.js +6 -1
- package/dist/tui/model-picker-data.d.ts +10 -0
- package/dist/tui/model-picker-data.js +32 -0
- package/dist/tui/run.js +632 -89
- package/dist/tui/tool-renderers/fallback.js +1 -1
- package/dist/tui/tool-renderers/write-preview.js +2 -0
- package/dist/tui/trace-groups.js +10 -3
- package/dist/tui-ink/app.js +1 -4
- package/dist/tui-ink/approval/approval-dialog.js +7 -1
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +5 -4
- package/dist/tui-ink/message-list.js +14 -8
- package/dist/tui-ink/trace-groups.js +1 -1
- package/dist/tui-opentui/app.js +2 -0
- package/dist/tui-opentui/approval/approval-dialog.js +7 -1
- package/dist/tui-opentui/display-history.d.ts +1 -0
- package/dist/tui-opentui/display-history.js +5 -4
- package/dist/tui-opentui/edit-diff.js +6 -1
- package/dist/tui-opentui/message-list.js +6 -3
- package/dist/tui-opentui/trace-groups.js +10 -3
- package/dist/types.d.ts +12 -2
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ function renderFallbackTool({ ctx, tool, syntaxStyle, width, helpers }) {
|
|
|
10
10
|
const header = helpers.toolHeader(tool);
|
|
11
11
|
const diff = helpers.extractToolDiff(tool);
|
|
12
12
|
const isError = tool.isError === true || tool.status === "error";
|
|
13
|
-
if (diff && !isError && tool.name === "edit") {
|
|
13
|
+
if (diff && !isError && (tool.name === "edit" || tool.name === "apply_patch")) {
|
|
14
14
|
return helpers.createBox(ctx, {
|
|
15
15
|
paddingLeft: 3,
|
|
16
16
|
marginTop: 1,
|
package/dist/tui/trace-groups.js
CHANGED
|
@@ -226,8 +226,8 @@ function buildExecuteGroup(classifier, tool, options, pending, startedAt, hasErr
|
|
|
226
226
|
function buildMutationGroup(classifier, raw, options, pending, startedAt, hasError, errorCount) {
|
|
227
227
|
const items = raw
|
|
228
228
|
.map((tool) => {
|
|
229
|
-
const path = formatTracePath(tool.args.path ?? "", options.homeDir);
|
|
230
|
-
const details = tool.name === "edit" ? getEditDiffDetails(tool) : null;
|
|
229
|
+
const path = formatTracePath(tool.args.path ?? firstMetadataPath(tool) ?? "", options.homeDir);
|
|
230
|
+
const details = tool.name === "edit" || tool.name === "apply_patch" ? getEditDiffDetails(tool) : null;
|
|
231
231
|
const suffix = details ? ` ${formatCompactEditStats(details.added, details.removed)}` : "";
|
|
232
232
|
return path ? `${path}${suffix}` : "";
|
|
233
233
|
})
|
|
@@ -323,7 +323,7 @@ function isFailedSubagent(subagent) {
|
|
|
323
323
|
return subagent.status === "failed" || subagent.status === "blocked" || subagent.status === "cancelled";
|
|
324
324
|
}
|
|
325
325
|
function isToolPending(tool) {
|
|
326
|
-
return tool.result === undefined;
|
|
326
|
+
return tool.result === undefined && tool.resultCollapsed !== true;
|
|
327
327
|
}
|
|
328
328
|
function isDirectoryLikeGlob(pattern) {
|
|
329
329
|
const normalized = pattern.trim();
|
|
@@ -400,8 +400,15 @@ function toolHeader(tool, homeDir) {
|
|
|
400
400
|
return formatTracePath(value, homeDir);
|
|
401
401
|
}
|
|
402
402
|
}
|
|
403
|
+
const path = firstMetadataPath(tool);
|
|
404
|
+
if (path)
|
|
405
|
+
return formatTracePath(path, homeDir);
|
|
403
406
|
return undefined;
|
|
404
407
|
}
|
|
408
|
+
function firstMetadataPath(tool) {
|
|
409
|
+
const paths = tool.metadata?.paths;
|
|
410
|
+
return Array.isArray(paths) && typeof paths[0] === "string" ? paths[0] : undefined;
|
|
411
|
+
}
|
|
405
412
|
function formatCompactEditStats(added, removed) {
|
|
406
413
|
const parts = [];
|
|
407
414
|
if (added > 0)
|
package/dist/tui-ink/app.js
CHANGED
|
@@ -996,10 +996,7 @@ export function App({ agent, args, sessionManager, createProvider, registry, ski
|
|
|
996
996
|
catch (err) {
|
|
997
997
|
commitAssistantMessage();
|
|
998
998
|
if (err instanceof AgentAbortError || err?.name === "AbortError") {
|
|
999
|
-
updateDisplayMessages((
|
|
1000
|
-
...prev,
|
|
1001
|
-
withMessageKey({ role: "assistant", content: "Cancelled." }),
|
|
1002
|
-
]);
|
|
999
|
+
updateDisplayMessages(() => reconstructDisplayMessages(agent.messages));
|
|
1003
1000
|
}
|
|
1004
1001
|
else {
|
|
1005
1002
|
updateDisplayMessages((prev) => [
|
|
@@ -53,7 +53,7 @@ function buildOptions(request) {
|
|
|
53
53
|
},
|
|
54
54
|
];
|
|
55
55
|
}
|
|
56
|
-
// edit / write
|
|
56
|
+
// edit / write / patch
|
|
57
57
|
return [
|
|
58
58
|
{ id: "yes", label: "Yes", allowAmend: true, amendPlaceholder: "and tell Claude what to do next" },
|
|
59
59
|
{
|
|
@@ -69,6 +69,8 @@ function dialogTitle(req) {
|
|
|
69
69
|
switch (req.type) {
|
|
70
70
|
case "edit":
|
|
71
71
|
return "Edit file";
|
|
72
|
+
case "patch":
|
|
73
|
+
return "Apply patch";
|
|
72
74
|
case "write":
|
|
73
75
|
return req.fileExists ? "Overwrite file" : "Create file";
|
|
74
76
|
case "bash":
|
|
@@ -81,6 +83,8 @@ function dialogQuestion(req) {
|
|
|
81
83
|
switch (req.type) {
|
|
82
84
|
case "edit":
|
|
83
85
|
return `Do you want to make this edit to ${basename(req.path)}?`;
|
|
86
|
+
case "patch":
|
|
87
|
+
return `Do you want to apply this patch to ${req.paths.length} file${req.paths.length === 1 ? "" : "s"}?`;
|
|
84
88
|
case "write":
|
|
85
89
|
return `Do you want to ${req.fileExists ? "overwrite" : "create"} ${basename(req.path)}?`;
|
|
86
90
|
case "bash":
|
|
@@ -99,6 +103,8 @@ function RequestPreview({ request }) {
|
|
|
99
103
|
return _jsx(BashPreview, { command: request.command, cwd: request.cwd });
|
|
100
104
|
case "edit":
|
|
101
105
|
return _jsx(DiffView, { diff: request.diff });
|
|
106
|
+
case "patch":
|
|
107
|
+
return _jsx(DiffView, { diff: request.diff });
|
|
102
108
|
case "write":
|
|
103
109
|
return _jsx(WritePreview, { path: request.path, content: request.content });
|
|
104
110
|
}
|
|
@@ -46,7 +46,6 @@ export function toolCallsFromParts(parts) {
|
|
|
46
46
|
const FULL_DETAIL_WINDOW = 24;
|
|
47
47
|
const MAX_OLD_CONTENT_CHARS = 1200;
|
|
48
48
|
const MAX_OLD_REASONING_CHARS = 600;
|
|
49
|
-
const MAX_OLD_TOOL_RESULT_CHARS = 800;
|
|
50
49
|
export function compactDisplayMessages(messages) {
|
|
51
50
|
if (messages.length === 0) {
|
|
52
51
|
return messages;
|
|
@@ -97,11 +96,13 @@ function compactDisplayPart(part) {
|
|
|
97
96
|
};
|
|
98
97
|
}
|
|
99
98
|
function compactToolCall(toolCall) {
|
|
99
|
+
if (toolCall.result === undefined) {
|
|
100
|
+
return toolCall;
|
|
101
|
+
}
|
|
100
102
|
return {
|
|
101
103
|
...toolCall,
|
|
102
|
-
result:
|
|
103
|
-
|
|
104
|
-
: toolCall.result,
|
|
104
|
+
result: undefined,
|
|
105
|
+
resultCollapsed: true,
|
|
105
106
|
};
|
|
106
107
|
}
|
|
107
108
|
const PREVIOUS_SUMMARY_PREFIX = /^Previous conversation summary:\s*\n?([\s\S]*)$/;
|
|
@@ -105,8 +105,8 @@ function ToolsPart({ toolCalls, terminalColumns, verboseTrace, pendingApproval,
|
|
|
105
105
|
}
|
|
106
106
|
const lastIdx = toolCalls.length - 1;
|
|
107
107
|
return (_jsx(Box, { flexDirection: "column", children: toolCalls.map((tc, idx) => {
|
|
108
|
-
const isWaitingApproval = tc
|
|
109
|
-
return (_jsx(ToolCallDisplay, { toolCall: tc, isStreaming: tc
|
|
108
|
+
const isWaitingApproval = isToolPending(tc) && !!pendingApproval && approvalMatchesTool(pendingApproval, tc);
|
|
109
|
+
return (_jsx(ToolCallDisplay, { toolCall: tc, isStreaming: isToolPending(tc), verbose: verboseTrace, terminalColumns: terminalColumns, showExpandHint: showExpandHint && idx === lastIdx, waitingApproval: isWaitingApproval, compactTop: idx === 0 && compactTop, nowTick: nowTick }, tc.id));
|
|
110
110
|
}) }));
|
|
111
111
|
}
|
|
112
112
|
function fallbackStreamingParts(content, tools) {
|
|
@@ -192,7 +192,7 @@ function findActiveTraceGroup(groups, pendingApproval) {
|
|
|
192
192
|
return undefined;
|
|
193
193
|
}
|
|
194
194
|
function isTraceGroupWaitingForApproval(group, pendingApproval) {
|
|
195
|
-
return !!pendingApproval && group.raw.some((tool) => tool
|
|
195
|
+
return !!pendingApproval && group.raw.some((tool) => isToolPending(tool) && approvalMatchesTool(pendingApproval, tool));
|
|
196
196
|
}
|
|
197
197
|
function approvalMatchesTool(hint, tc) {
|
|
198
198
|
if (hint.toolName !== tc.name)
|
|
@@ -202,6 +202,9 @@ function approvalMatchesTool(hint, tc) {
|
|
|
202
202
|
}
|
|
203
203
|
return !hint.path || hint.path === tc.args.path;
|
|
204
204
|
}
|
|
205
|
+
function isToolPending(tool) {
|
|
206
|
+
return tool.result === undefined && tool.resultCollapsed !== true;
|
|
207
|
+
}
|
|
205
208
|
function ReasoningTraceBlock({ reasoning }) {
|
|
206
209
|
const theme = useTheme();
|
|
207
210
|
const lines = React.useMemo(() => reasoning.split("\n").filter((l) => l.trim() !== ""), [reasoning]);
|
|
@@ -280,6 +283,8 @@ function getToolHeader(toolCall) {
|
|
|
280
283
|
}
|
|
281
284
|
}
|
|
282
285
|
function summarizeToolResult(tc) {
|
|
286
|
+
if (tc.resultCollapsed)
|
|
287
|
+
return tc.isError ? "error output collapsed" : "result collapsed";
|
|
283
288
|
if (tc.result === undefined)
|
|
284
289
|
return "pending";
|
|
285
290
|
const raw = tc.result.replace(/\r\n/g, "\n");
|
|
@@ -441,7 +446,7 @@ function ToolCallDisplay({ toolCall, isStreaming, verbose, terminalColumns, show
|
|
|
441
446
|
summary = "⏸ waiting for approval";
|
|
442
447
|
summaryColor = theme.warning;
|
|
443
448
|
}
|
|
444
|
-
else if (toolCall
|
|
449
|
+
else if (isToolPending(toolCall) && toolCall.startedAt) {
|
|
445
450
|
void nowTick;
|
|
446
451
|
summary = "running";
|
|
447
452
|
summaryColor = theme.toolPending;
|
|
@@ -454,18 +459,19 @@ function ToolCallDisplay({ toolCall, isStreaming, verbose, terminalColumns, show
|
|
|
454
459
|
summaryColor = theme.success;
|
|
455
460
|
}
|
|
456
461
|
const editDetails = getEditDiffDetails(toolCall);
|
|
457
|
-
const isEditDiff = editDetails !== null && toolCall.result !== undefined;
|
|
462
|
+
const isEditDiff = editDetails !== null && toolCall.result !== undefined && !toolCall.resultCollapsed;
|
|
463
|
+
const showSummary = !toolCall.resultCollapsed || waitingApproval;
|
|
458
464
|
// Only show the file preview once the tool actually executed. During the
|
|
459
465
|
// streaming-args phase, args.content is incomplete and re-rendering the
|
|
460
466
|
// entire body per delta both looks chaotic and breaks on partial escapes.
|
|
461
|
-
const isWritePreview = toolCall.name === "write" && !toolCall.isError && toolCall.result !== undefined;
|
|
462
|
-
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: compactTop ? 0 : 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: bulletColor, children: [glyph, " "] }), _jsx(Text, { bold: true, color: theme.toolName, children: name }), header && _jsxs(Text, { color: theme.muted, children: ["(", header, ")"] })] }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: summaryColor, children: ["\u23BF ", summary] }) }), toolCall.isError && toolCall.result && (_jsx(Box, { marginLeft: 4, flexDirection: "column", children: toolCall.result.replace(/\r\n/g, "\n").split("\n").slice(0, 6).map((line, i) => (_jsx(Text, { color: theme.error, children: line }, i))) })), isEditDiff && (_jsx(DiffBlock, { diff: editDetails.diff, terminalColumns: terminalColumns, maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint })), isWritePreview && (_jsx(WritePreview, { content: String(toolCall.args.content || ""), maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint })), !toolCall.isError && !isEditDiff && !isWritePreview && highlighted && (_jsx(OutputPreview, { text: highlighted, maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint }))] }));
|
|
467
|
+
const isWritePreview = toolCall.name === "write" && !toolCall.isError && toolCall.result !== undefined && !toolCall.resultCollapsed;
|
|
468
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: compactTop ? 0 : 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: bulletColor, children: [glyph, " "] }), _jsx(Text, { bold: true, color: theme.toolName, children: name }), header && _jsxs(Text, { color: theme.muted, children: ["(", header, ")"] })] }), showSummary && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: summaryColor, children: ["\u23BF ", summary] }) })), toolCall.isError && toolCall.result && (_jsx(Box, { marginLeft: 4, flexDirection: "column", children: toolCall.result.replace(/\r\n/g, "\n").split("\n").slice(0, 6).map((line, i) => (_jsx(Text, { color: theme.error, children: line }, i))) })), isEditDiff && (_jsx(DiffBlock, { diff: editDetails.diff, terminalColumns: terminalColumns, maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint })), isWritePreview && (_jsx(WritePreview, { content: String(toolCall.args.content || ""), maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint })), !toolCall.isError && !isEditDiff && !isWritePreview && highlighted && (_jsx(OutputPreview, { text: highlighted, maxLines: maxLines, verbose: verbose, showExpandHint: showExpandHint }))] }));
|
|
463
469
|
}
|
|
464
470
|
function SubagentToolDisplay({ toolCall, verbose, terminalColumns, compactTop, }) {
|
|
465
471
|
const theme = useTheme();
|
|
466
472
|
const subagents = subagentsFrom(toolCall);
|
|
467
473
|
const hasError = toolCall.isError || subagents.some((subagent) => (subagent.status === "failed" || subagent.status === "blocked" || subagent.status === "cancelled"));
|
|
468
|
-
const bulletColor = hasError ? theme.error : toolCall
|
|
474
|
+
const bulletColor = hasError ? theme.error : isToolPending(toolCall) ? theme.toolPending : theme.user;
|
|
469
475
|
const detailWidth = Math.max(24, terminalColumns - 10);
|
|
470
476
|
const rows = verbose ? sortSubagents(subagents) : sortSubagents(subagents).slice(0, 4);
|
|
471
477
|
const omitted = Math.max(0, subagents.length - rows.length);
|
|
@@ -323,7 +323,7 @@ function isFailedSubagent(subagent) {
|
|
|
323
323
|
return subagent.status === "failed" || subagent.status === "blocked" || subagent.status === "cancelled";
|
|
324
324
|
}
|
|
325
325
|
function isToolPending(tool) {
|
|
326
|
-
return tool.result === undefined;
|
|
326
|
+
return tool.result === undefined && tool.resultCollapsed !== true;
|
|
327
327
|
}
|
|
328
328
|
function isDirectoryLikeGlob(pattern) {
|
|
329
329
|
const normalized = pattern.trim();
|
package/dist/tui-opentui/app.js
CHANGED
|
@@ -1147,6 +1147,8 @@ export function App({ agent, args, sessionManager, createProvider, registry, ski
|
|
|
1147
1147
|
return { toolName: "bash", command: r.command };
|
|
1148
1148
|
if (r.type === "edit")
|
|
1149
1149
|
return { toolName: "edit", path: r.path };
|
|
1150
|
+
if (r.type === "patch")
|
|
1151
|
+
return { toolName: "edit", path: r.paths[0] ?? r.path };
|
|
1150
1152
|
if (r.type === "write")
|
|
1151
1153
|
return { toolName: "write", path: r.path };
|
|
1152
1154
|
return null;
|
|
@@ -60,7 +60,7 @@ function buildOptions(request) {
|
|
|
60
60
|
},
|
|
61
61
|
];
|
|
62
62
|
}
|
|
63
|
-
// edit / write
|
|
63
|
+
// edit / write / patch
|
|
64
64
|
return [
|
|
65
65
|
{ id: "yes", label: "Yes", allowAmend: true, amendPlaceholder: "and tell Claude what to do next" },
|
|
66
66
|
{
|
|
@@ -76,6 +76,8 @@ function dialogTitle(req) {
|
|
|
76
76
|
switch (req.type) {
|
|
77
77
|
case "edit":
|
|
78
78
|
return "Edit file";
|
|
79
|
+
case "patch":
|
|
80
|
+
return "Apply patch";
|
|
79
81
|
case "write":
|
|
80
82
|
return req.fileExists ? "Overwrite file" : "Create file";
|
|
81
83
|
case "bash":
|
|
@@ -88,6 +90,8 @@ function dialogQuestion(req) {
|
|
|
88
90
|
switch (req.type) {
|
|
89
91
|
case "edit":
|
|
90
92
|
return `Do you want to make this edit to ${basename(req.path)}?`;
|
|
93
|
+
case "patch":
|
|
94
|
+
return `Do you want to apply this patch to ${req.paths.length} file${req.paths.length === 1 ? "" : "s"}?`;
|
|
91
95
|
case "write":
|
|
92
96
|
return `Do you want to ${req.fileExists ? "overwrite" : "create"} ${basename(req.path)}?`;
|
|
93
97
|
case "bash":
|
|
@@ -106,6 +110,8 @@ function RequestPreview({ request }) {
|
|
|
106
110
|
return _jsx(BashPreview, { command: request.command, cwd: request.cwd });
|
|
107
111
|
case "edit":
|
|
108
112
|
return _jsx(DiffView, { diff: request.diff });
|
|
113
|
+
case "patch":
|
|
114
|
+
return _jsx(DiffView, { diff: request.diff });
|
|
109
115
|
case "write":
|
|
110
116
|
return _jsx(WritePreview, { path: request.path, content: request.content });
|
|
111
117
|
}
|
|
@@ -46,7 +46,6 @@ export function toolCallsFromParts(parts) {
|
|
|
46
46
|
const FULL_DETAIL_WINDOW = 24;
|
|
47
47
|
const MAX_OLD_CONTENT_CHARS = 1200;
|
|
48
48
|
const MAX_OLD_REASONING_CHARS = 600;
|
|
49
|
-
const MAX_OLD_TOOL_RESULT_CHARS = 800;
|
|
50
49
|
export function compactDisplayMessages(messages) {
|
|
51
50
|
if (messages.length === 0) {
|
|
52
51
|
return messages;
|
|
@@ -97,11 +96,13 @@ function compactDisplayPart(part) {
|
|
|
97
96
|
};
|
|
98
97
|
}
|
|
99
98
|
function compactToolCall(toolCall) {
|
|
99
|
+
if (toolCall.result === undefined) {
|
|
100
|
+
return toolCall;
|
|
101
|
+
}
|
|
100
102
|
return {
|
|
101
103
|
...toolCall,
|
|
102
|
-
result:
|
|
103
|
-
|
|
104
|
-
: toolCall.result,
|
|
104
|
+
result: undefined,
|
|
105
|
+
resultCollapsed: true,
|
|
105
106
|
};
|
|
106
107
|
}
|
|
107
108
|
const PREVIOUS_SUMMARY_PREFIX = /^Previous conversation summary:\s*\n?([\s\S]*)$/;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { countUnifiedDiffChanges } from "../diff-stats.js";
|
|
2
2
|
export const EDIT_COLLAPSED_DIFF_LINES = 20;
|
|
3
3
|
export function getEditDiffDetails(tool) {
|
|
4
|
-
if (tool.name !== "edit" || tool.isError)
|
|
4
|
+
if ((tool.name !== "edit" && tool.name !== "apply_patch") || tool.isError)
|
|
5
5
|
return null;
|
|
6
6
|
const metadata = tool.metadata;
|
|
7
7
|
const metadataDiff = readMetadataString(metadata, "diff");
|
|
@@ -12,9 +12,14 @@ export function getEditDiffDetails(tool) {
|
|
|
12
12
|
const added = readMetadataNumber(metadata, "addedLines") ?? counted.added;
|
|
13
13
|
const removed = readMetadataNumber(metadata, "removedLines") ?? counted.removed;
|
|
14
14
|
const path = readMetadataString(metadata, "path")
|
|
15
|
+
?? readFirstMetadataPath(metadata)
|
|
15
16
|
?? (typeof tool.args.path === "string" ? tool.args.path : undefined);
|
|
16
17
|
return { diff, added, removed, path };
|
|
17
18
|
}
|
|
19
|
+
function readFirstMetadataPath(metadata) {
|
|
20
|
+
const value = metadata?.paths;
|
|
21
|
+
return Array.isArray(value) && typeof value[0] === "string" ? value[0] : undefined;
|
|
22
|
+
}
|
|
18
23
|
export function formatEditSuccessSummary(details) {
|
|
19
24
|
const stats = details ? formatEditStats(details.added, details.removed) : "";
|
|
20
25
|
return `Succeeded. File edited.${stats ? ` ${stats}` : ""}`;
|
|
@@ -84,15 +84,18 @@ function ToolsPart({ toolCalls, terminalColumns, verboseTrace, theme, }) {
|
|
|
84
84
|
* body as content. Matches opencode's `theme.block` pattern.
|
|
85
85
|
*/
|
|
86
86
|
function ToolCard({ tool, terminalColumns, verboseTrace, theme, }) {
|
|
87
|
-
const pending = tool
|
|
87
|
+
const pending = isToolPending(tool);
|
|
88
88
|
const error = tool.isError;
|
|
89
89
|
const target = describeToolTarget(tool);
|
|
90
90
|
const headerColor = pending ? theme.toolPending : error ? theme.toolError : theme.textMuted;
|
|
91
91
|
const titleText = target ? `${tool.name} ${target}` : tool.name;
|
|
92
|
-
const editDetails = (tool.name === "edit" || tool.name === "multiedit" || tool.name === "write")
|
|
92
|
+
const editDetails = (tool.name === "edit" || tool.name === "apply_patch" || tool.name === "multiedit" || tool.name === "write")
|
|
93
93
|
? getEditDiffDetails(tool)
|
|
94
94
|
: null;
|
|
95
|
-
return (_jsxs("box", { style: { flexDirection: "column", marginBottom: 1 }, children: [_jsx("text", { fg: headerColor, content: titleText }), _jsxs("box", { style: { paddingLeft: 1, flexDirection: "column" }, children: [!pending && tool.result && verboseTrace && (_jsx(ToolResultPreview, { result: tool.result, error: error, terminalColumns: terminalColumns, theme: theme })), editDetails && editDetails.diff && (_jsx(EditDiffPreview, { diff: editDetails.diff, theme: theme }))] })] }));
|
|
95
|
+
return (_jsxs("box", { style: { flexDirection: "column", marginBottom: 1 }, children: [_jsx("text", { fg: headerColor, content: titleText }), _jsxs("box", { style: { paddingLeft: 1, flexDirection: "column" }, children: [!pending && tool.result && verboseTrace && (_jsx(ToolResultPreview, { result: tool.result, error: error, terminalColumns: terminalColumns, theme: theme })), !tool.resultCollapsed && editDetails && editDetails.diff && (_jsx(EditDiffPreview, { diff: editDetails.diff, theme: theme }))] })] }));
|
|
96
|
+
}
|
|
97
|
+
function isToolPending(tool) {
|
|
98
|
+
return tool.result === undefined && tool.resultCollapsed !== true;
|
|
96
99
|
}
|
|
97
100
|
function describeToolTarget(tool) {
|
|
98
101
|
const args = tool.args || {};
|
|
@@ -226,8 +226,8 @@ function buildExecuteGroup(classifier, tool, options, pending, startedAt, hasErr
|
|
|
226
226
|
function buildMutationGroup(classifier, raw, options, pending, startedAt, hasError, errorCount) {
|
|
227
227
|
const items = raw
|
|
228
228
|
.map((tool) => {
|
|
229
|
-
const path = formatTracePath(tool.args.path ?? "", options.homeDir);
|
|
230
|
-
const details = tool.name === "edit" ? getEditDiffDetails(tool) : null;
|
|
229
|
+
const path = formatTracePath(tool.args.path ?? firstMetadataPath(tool) ?? "", options.homeDir);
|
|
230
|
+
const details = tool.name === "edit" || tool.name === "apply_patch" ? getEditDiffDetails(tool) : null;
|
|
231
231
|
const suffix = details ? ` ${formatCompactEditStats(details.added, details.removed)}` : "";
|
|
232
232
|
return path ? `${path}${suffix}` : "";
|
|
233
233
|
})
|
|
@@ -323,7 +323,7 @@ function isFailedSubagent(subagent) {
|
|
|
323
323
|
return subagent.status === "failed" || subagent.status === "blocked" || subagent.status === "cancelled";
|
|
324
324
|
}
|
|
325
325
|
function isToolPending(tool) {
|
|
326
|
-
return tool.result === undefined;
|
|
326
|
+
return tool.result === undefined && tool.resultCollapsed !== true;
|
|
327
327
|
}
|
|
328
328
|
function isDirectoryLikeGlob(pattern) {
|
|
329
329
|
const normalized = pattern.trim();
|
|
@@ -400,8 +400,15 @@ function toolHeader(tool, homeDir) {
|
|
|
400
400
|
return formatTracePath(value, homeDir);
|
|
401
401
|
}
|
|
402
402
|
}
|
|
403
|
+
const path = firstMetadataPath(tool);
|
|
404
|
+
if (path)
|
|
405
|
+
return formatTracePath(path, homeDir);
|
|
403
406
|
return undefined;
|
|
404
407
|
}
|
|
408
|
+
function firstMetadataPath(tool) {
|
|
409
|
+
const paths = tool.metadata?.paths;
|
|
410
|
+
return Array.isArray(paths) && typeof paths[0] === "string" ? paths[0] : undefined;
|
|
411
|
+
}
|
|
405
412
|
function formatCompactEditStats(added, removed) {
|
|
406
413
|
const parts = [];
|
|
407
414
|
if (added > 0)
|
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,16 @@ export interface AssistantMessage {
|
|
|
23
23
|
content: string;
|
|
24
24
|
reasoning?: string;
|
|
25
25
|
toolCalls?: ToolCall[];
|
|
26
|
+
/** Model metadata captured for local usage statistics. */
|
|
27
|
+
model?: string;
|
|
28
|
+
providerId?: string;
|
|
29
|
+
modelId?: string;
|
|
30
|
+
usage?: TokenUsage;
|
|
31
|
+
error?: {
|
|
32
|
+
name: string;
|
|
33
|
+
message: string;
|
|
34
|
+
aborted?: boolean;
|
|
35
|
+
};
|
|
26
36
|
}
|
|
27
37
|
export interface ToolMessage {
|
|
28
38
|
role: "tool";
|
|
@@ -90,9 +100,9 @@ export interface ParsedToolCall extends ToolCall {
|
|
|
90
100
|
*/
|
|
91
101
|
argsCorrupt?: boolean;
|
|
92
102
|
}
|
|
93
|
-
export type ToolResultStatus = "success" | "no_match" | "partial" | "timeout" | "blocked" | "command_error";
|
|
103
|
+
export type ToolResultStatus = "success" | "no_match" | "partial" | "timeout" | "blocked" | "cancelled" | "command_error";
|
|
94
104
|
export interface ToolResultMetadata {
|
|
95
|
-
kind?: "search" | "read" | "write" | "edit" | "shell" | "web" | "security" | "lsp" | "question" | "subagent";
|
|
105
|
+
kind?: "search" | "read" | "write" | "edit" | "patch" | "shell" | "server" | "web" | "security" | "lsp" | "question" | "subagent";
|
|
96
106
|
path?: string;
|
|
97
107
|
pattern?: string;
|
|
98
108
|
matches?: number;
|