mastracode 0.42.2 → 0.42.3-alpha.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/dist/cli.cjs +2 -2
- package/dist/cli.js +2 -2
- package/dist/tui/chat-boundary-reconciliation.d.ts.map +1 -1
- package/dist/tui/components/assistant-message.d.ts +8 -0
- package/dist/tui/components/assistant-message.d.ts.map +1 -1
- package/dist/tui/components/chat-spacing.d.ts +6 -0
- package/dist/tui/components/chat-spacing.d.ts.map +1 -1
- package/dist/tui/components/idle-counter.d.ts +3 -0
- package/dist/tui/components/idle-counter.d.ts.map +1 -1
- package/dist/tui/components/tool-execution-enhanced.d.ts +84 -16
- package/dist/tui/components/tool-execution-enhanced.d.ts.map +1 -1
- package/dist/tui/components/tool-execution-interface.d.ts +9 -0
- package/dist/tui/components/tool-execution-interface.d.ts.map +1 -1
- package/dist/tui/db-message-parts.d.ts +8 -3
- package/dist/tui/db-message-parts.d.ts.map +1 -1
- package/dist/tui/event-dispatch.d.ts.map +1 -1
- package/dist/tui/handlers/agent-lifecycle.d.ts.map +1 -1
- package/dist/tui/handlers/index.d.ts +1 -1
- package/dist/tui/handlers/index.d.ts.map +1 -1
- package/dist/tui/handlers/message.d.ts.map +1 -1
- package/dist/tui/handlers/tool.d.ts +5 -0
- package/dist/tui/handlers/tool.d.ts.map +1 -1
- package/dist/tui/quiet-mode.d.ts +4 -4
- package/dist/tui/quiet-mode.d.ts.map +1 -1
- package/dist/tui/render-messages.d.ts.map +1 -1
- package/dist/tui/setup.d.ts.map +1 -1
- package/dist/{tui-CJ1G5lRF.cjs → tui-DrOMn6H1.cjs} +599 -145
- package/dist/tui-DrOMn6H1.cjs.map +1 -0
- package/dist/{tui-BNnzmiap.js → tui-m_R8I6Y_.js} +552 -98
- package/dist/tui-m_R8I6Y_.js.map +1 -0
- package/dist/tui.cjs +1 -1
- package/dist/tui.js +1 -1
- package/package.json +10 -10
- package/dist/tui-BNnzmiap.js.map +0 -1
- package/dist/tui-CJ1G5lRF.cjs.map +0 -1
|
@@ -61,12 +61,12 @@ let process$1 = require("process");
|
|
|
61
61
|
process$1 = __toESM(process$1, 1);
|
|
62
62
|
let os = require("os");
|
|
63
63
|
os = __toESM(os, 1);
|
|
64
|
+
let path = require("path");
|
|
65
|
+
path = __toESM(path, 1);
|
|
64
66
|
let _mastra_code_sdk_tool_names = require("@mastra/code-sdk/tool-names");
|
|
65
67
|
let cli_highlight = require("cli-highlight");
|
|
66
68
|
let _mastra_code_sdk_permissions = require("@mastra/code-sdk/permissions");
|
|
67
69
|
let jsonriver = require("jsonriver");
|
|
68
|
-
let path = require("path");
|
|
69
|
-
path = __toESM(path, 1);
|
|
70
70
|
let util = require("util");
|
|
71
71
|
let _mastra_code_sdk_onboarding_packs = require("@mastra/code-sdk/onboarding/packs");
|
|
72
72
|
let _mastra_code_sdk_clipboard_index = require("@mastra/code-sdk/clipboard/index");
|
|
@@ -757,10 +757,65 @@ function accountSwitchRenderPart(data) {
|
|
|
757
757
|
* document order. Text/reasoning/tool-invocation/data-om-* parts are surfaced;
|
|
758
758
|
* bookkeeping parts (`step-start`, `data-om-status`, etc.) are skipped.
|
|
759
759
|
*/
|
|
760
|
+
const partCreatedAt = (part) => {
|
|
761
|
+
const createdAt = part.createdAt;
|
|
762
|
+
return typeof createdAt === "number" ? createdAt : void 0;
|
|
763
|
+
};
|
|
764
|
+
const isDataPart = (part) => String(part.type).startsWith("data-");
|
|
765
|
+
/**
|
|
766
|
+
* Tool invocation parts are not reliably stamped, so recover timing from their neighbours: tools
|
|
767
|
+
* emit `data-*` parts while they run, and the next stamped non-data part (usually the following
|
|
768
|
+
* `step-start`) lands right after the result.
|
|
769
|
+
*/
|
|
770
|
+
function getToolPartTiming(parts, index, messageCreatedAt) {
|
|
771
|
+
let endedAt;
|
|
772
|
+
let firstDataAt;
|
|
773
|
+
for (let i = index + 1; i < parts.length; i++) {
|
|
774
|
+
const part = parts[i];
|
|
775
|
+
if (part.type === "tool-invocation") continue;
|
|
776
|
+
const createdAt = partCreatedAt(part);
|
|
777
|
+
if (createdAt === void 0) continue;
|
|
778
|
+
if (isDataPart(part)) {
|
|
779
|
+
firstDataAt ??= createdAt;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
endedAt = createdAt;
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
let previousAt;
|
|
786
|
+
for (let i = index - 1; i >= 0 && previousAt === void 0; i--) previousAt = partCreatedAt(parts[i]);
|
|
787
|
+
const startedAt = partCreatedAt(parts[index]) ?? firstDataAt ?? previousAt ?? messageCreatedAt;
|
|
788
|
+
if (startedAt === void 0 || endedAt === void 0 || endedAt < startedAt) return {};
|
|
789
|
+
return {
|
|
790
|
+
startedAt,
|
|
791
|
+
endedAt
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Collects the sandbox exit records (`data-sandbox-exit`) of shell calls by tool call id. They
|
|
796
|
+
* carry the real exit status and run time, and can land in a later message than the call itself,
|
|
797
|
+
* so collect them across the whole thread.
|
|
798
|
+
*/
|
|
799
|
+
function collectCommandExits(messages) {
|
|
800
|
+
const exits = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const message of messages) for (const part of getParts(message)) {
|
|
802
|
+
if (part.type !== "data-sandbox-exit") continue;
|
|
803
|
+
const data = part.data;
|
|
804
|
+
if (typeof data?.toolCallId !== "string" || typeof data.exitCode !== "number") continue;
|
|
805
|
+
exits.set(data.toolCallId, {
|
|
806
|
+
exitCode: data.exitCode,
|
|
807
|
+
success: typeof data.success === "boolean" ? data.success : data.exitCode === 0,
|
|
808
|
+
...typeof data.executionTimeMs === "number" ? { executionTimeMs: data.executionTimeMs } : {}
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
return exits;
|
|
812
|
+
}
|
|
760
813
|
function getAssistantRenderParts(message) {
|
|
761
814
|
const out = [];
|
|
762
815
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
763
|
-
|
|
816
|
+
const parts = getParts(message);
|
|
817
|
+
const messageCreatedAt = message.createdAt ? new Date(message.createdAt).getTime() : void 0;
|
|
818
|
+
for (const [index, part] of parts.entries()) {
|
|
764
819
|
const partType = part.type;
|
|
765
820
|
switch (partType) {
|
|
766
821
|
case "text":
|
|
@@ -787,7 +842,8 @@ function getAssistantRenderParts(message) {
|
|
|
787
842
|
args: inv.args,
|
|
788
843
|
result: inv.result,
|
|
789
844
|
hasResult,
|
|
790
|
-
isError: hasResult && (typeof inv.isError === "boolean" ? inv.isError : isErrorResult(inv.result))
|
|
845
|
+
isError: hasResult && (typeof inv.isError === "boolean" ? inv.isError : isErrorResult(inv.result)),
|
|
846
|
+
...hasResult ? getToolPartTiming(parts, index, Number.isNaN(messageCreatedAt) ? void 0 : messageCreatedAt) : {}
|
|
791
847
|
});
|
|
792
848
|
break;
|
|
793
849
|
}
|
|
@@ -1095,6 +1151,7 @@ var AssistantMessageComponent = class extends _earendil_works_pi_tui.Container {
|
|
|
1095
1151
|
terminalStatus;
|
|
1096
1152
|
renderNodes = /* @__PURE__ */ new Map();
|
|
1097
1153
|
renderOrder = [];
|
|
1154
|
+
quiet = false;
|
|
1098
1155
|
constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme()) {
|
|
1099
1156
|
super();
|
|
1100
1157
|
this.hideThinkingBlock = hideThinkingBlock;
|
|
@@ -1111,6 +1168,12 @@ var AssistantMessageComponent = class extends _earendil_works_pi_tui.Container {
|
|
|
1111
1168
|
this.hideThinkingBlock = hide;
|
|
1112
1169
|
this.reconcileChildren();
|
|
1113
1170
|
}
|
|
1171
|
+
setQuietModeDisplay(mode) {
|
|
1172
|
+
const quiet = mode === "quiet";
|
|
1173
|
+
if (this.quiet === quiet) return;
|
|
1174
|
+
this.quiet = quiet;
|
|
1175
|
+
this.syncVisibleChildren();
|
|
1176
|
+
}
|
|
1114
1177
|
getChatSpacingKind() {
|
|
1115
1178
|
return this.contentContainer.children.length > 0 ? "assistant-message" : void 0;
|
|
1116
1179
|
}
|
|
@@ -1153,12 +1216,21 @@ var AssistantMessageComponent = class extends _earendil_works_pi_tui.Container {
|
|
|
1153
1216
|
nextNodes.set(node.key, owned);
|
|
1154
1217
|
nextOrder.push(node.key);
|
|
1155
1218
|
}
|
|
1156
|
-
if (nextOrder.length !== this.renderOrder.length || nextOrder.some((key, index) => key !== this.renderOrder[index])) {
|
|
1157
|
-
this.contentContainer.clear();
|
|
1158
|
-
for (const key of nextOrder) this.contentContainer.addChild(nextNodes.get(key).component);
|
|
1159
|
-
}
|
|
1160
1219
|
this.renderNodes = nextNodes;
|
|
1161
1220
|
this.renderOrder = nextOrder;
|
|
1221
|
+
this.syncVisibleChildren();
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Quiet mode leaves "Thinking..." placeholders out of the chat entirely; the live one is shown in
|
|
1225
|
+
* the status line above the input instead, so hiding it never shifts the layout. Works from the
|
|
1226
|
+
* owned render nodes so it still applies after finalizeRenderState() has released the source parts.
|
|
1227
|
+
*/
|
|
1228
|
+
syncVisibleChildren() {
|
|
1229
|
+
const visible = this.renderOrder.filter((key) => !this.quiet || !key.includes(":hidden-thinking")).map((key) => this.renderNodes.get(key).component);
|
|
1230
|
+
const current = this.contentContainer.children;
|
|
1231
|
+
if (!(current.length !== visible.length || visible.some((component, index) => component !== current[index]))) return;
|
|
1232
|
+
this.contentContainer.clear();
|
|
1233
|
+
for (const component of visible) this.contentContainer.addChild(component);
|
|
1162
1234
|
}
|
|
1163
1235
|
createRenderNode(node) {
|
|
1164
1236
|
switch (node.kind) {
|
|
@@ -1480,7 +1552,11 @@ var AssistantRenderRegistry = class {
|
|
|
1480
1552
|
};
|
|
1481
1553
|
function ensureAssistantRenderSegment(state, messageId, addChild, precedingToolCallId) {
|
|
1482
1554
|
const key = getAssistantSegmentKey(messageId, precedingToolCallId);
|
|
1483
|
-
const { segment, created } = state.assistantRenderRegistry.start(messageId, key, () =>
|
|
1555
|
+
const { segment, created } = state.assistantRenderRegistry.start(messageId, key, () => {
|
|
1556
|
+
const component = new AssistantMessageComponent(void 0, state.hideThinkingBlock, getMarkdownTheme());
|
|
1557
|
+
component.setQuietModeDisplay(state.quietMode ? "quiet" : "normal");
|
|
1558
|
+
return component;
|
|
1559
|
+
});
|
|
1484
1560
|
state.streamingComponent = segment.component;
|
|
1485
1561
|
if (created) addChild(segment.component);
|
|
1486
1562
|
return segment.component;
|
|
@@ -1559,12 +1635,19 @@ function isChatBoundarySpacer(component) {
|
|
|
1559
1635
|
}
|
|
1560
1636
|
//#endregion
|
|
1561
1637
|
//#region src/tui/components/chat-spacing.ts
|
|
1638
|
+
/**
|
|
1639
|
+
* Group key of a quiet shell call whose directory isn't known yet (its arguments are still
|
|
1640
|
+
* streaming). It joins the shell box above it rather than opening a box that disappears again
|
|
1641
|
+
* once the directory arrives.
|
|
1642
|
+
*/
|
|
1643
|
+
const PENDING_SHELL_GROUP_KEY = "$ \0pending";
|
|
1562
1644
|
function getChatSpacingKind(component) {
|
|
1563
1645
|
return component?.getChatSpacingKind?.();
|
|
1564
1646
|
}
|
|
1565
1647
|
function getSpacingBetweenComponents(prev, next, _prevPrev, _nextNext) {
|
|
1566
1648
|
const prevKind = getChatSpacingKind(prev);
|
|
1567
1649
|
const nextKind = getChatSpacingKind(next);
|
|
1650
|
+
if (isQuietShellBox(prev, prevKind) && isQuietShellBox(next, nextKind)) return 0;
|
|
1568
1651
|
if (prevKind === "quiet-compact-tool" && nextKind === "quiet-compact-tool") {
|
|
1569
1652
|
const prevKey = getCompactToolGroupKey(prev);
|
|
1570
1653
|
const nextKey = getCompactToolGroupKey(next);
|
|
@@ -1577,6 +1660,10 @@ function getSpacingBetweenComponents(prev, next, _prevPrev, _nextNext) {
|
|
|
1577
1660
|
function getCompactToolGroupKey(component) {
|
|
1578
1661
|
return component?.getCompactToolGroupKey?.();
|
|
1579
1662
|
}
|
|
1663
|
+
function isQuietShellBox(component, kind) {
|
|
1664
|
+
if (kind === "quiet-shell-tool") return true;
|
|
1665
|
+
return kind === "quiet-compact-tool" && !!getCompactToolGroupKey(component)?.startsWith("$ ");
|
|
1666
|
+
}
|
|
1580
1667
|
function hasQuietStreamingPreview(component) {
|
|
1581
1668
|
return component?.hasQuietStreamingPreview?.() ?? false;
|
|
1582
1669
|
}
|
|
@@ -1620,37 +1707,57 @@ function reconcileChatBoundarySpacers(chatContainer) {
|
|
|
1620
1707
|
const components = children.filter((child) => !isChatBoundarySpacer(child));
|
|
1621
1708
|
const spacerPool = children.filter(isChatBoundarySpacer);
|
|
1622
1709
|
let poolIndex = 0;
|
|
1710
|
+
const compactToolGroupKeys = new Array(components.length);
|
|
1711
|
+
let previousSpacingGroupKey;
|
|
1712
|
+
for (let i = 0; i < components.length; i++) {
|
|
1713
|
+
const component = components[i];
|
|
1714
|
+
const participant = component;
|
|
1715
|
+
let key = participant.getCompactToolGroupKey?.();
|
|
1716
|
+
const held = key === "$ \0pending" && !!previousSpacingGroupKey?.startsWith("$ ");
|
|
1717
|
+
participant.setQuietShellHeld?.(held);
|
|
1718
|
+
if (held) key = void 0;
|
|
1719
|
+
compactToolGroupKeys[i] = key;
|
|
1720
|
+
if (getChatSpacingKind(component)) previousSpacingGroupKey = key;
|
|
1721
|
+
}
|
|
1623
1722
|
const nextCompactToolGroupKeys = new Array(components.length);
|
|
1624
1723
|
let nextSpacingComponentGroupKey;
|
|
1625
1724
|
for (let i = components.length - 1; i >= 0; i--) {
|
|
1626
1725
|
nextCompactToolGroupKeys[i] = nextSpacingComponentGroupKey;
|
|
1627
1726
|
const component = components[i];
|
|
1628
|
-
if (component && getChatSpacingKind(component)) nextSpacingComponentGroupKey =
|
|
1727
|
+
if (component && getChatSpacingKind(component)) nextSpacingComponentGroupKey = compactToolGroupKeys[i];
|
|
1629
1728
|
}
|
|
1630
1729
|
const nextChildren = [];
|
|
1631
1730
|
let previousCompactToolGroupKey;
|
|
1632
1731
|
let previousCompactToolSummary;
|
|
1633
1732
|
let currentCompactRun = [];
|
|
1634
1733
|
let previousSpacingComponent;
|
|
1635
|
-
const
|
|
1734
|
+
const flushCompactRun = () => {
|
|
1636
1735
|
const color = getCompactRunLabelColor(currentCompactRun);
|
|
1637
|
-
|
|
1736
|
+
const widths = currentCompactRun.map((participant) => participant.getQuietShellNaturalWidth?.()).filter((width) => width !== void 0);
|
|
1737
|
+
const groupWidth = widths.length > 0 ? Math.max(...widths) : void 0;
|
|
1738
|
+
const previews = currentCompactRun.map((participant) => participant.getQuietShellPreviewLines?.());
|
|
1739
|
+
const preview = previews.findLast((lines) => lines && lines.length > 0) ?? previews.find((lines) => lines);
|
|
1740
|
+
currentCompactRun.forEach((participant, index) => {
|
|
1741
|
+
participant.setCompactToolGroupLabelColor?.(color);
|
|
1742
|
+
participant.setQuietShellGroupWidth?.(groupWidth);
|
|
1743
|
+
participant.setQuietShellGroupPreview?.(index === 0 ? preview : void 0);
|
|
1744
|
+
});
|
|
1638
1745
|
currentCompactRun = [];
|
|
1639
1746
|
};
|
|
1640
1747
|
for (let i = 0; i < components.length; i++) {
|
|
1641
1748
|
const component = components[i];
|
|
1642
1749
|
const participant = component;
|
|
1643
|
-
const compactToolGroupKey =
|
|
1750
|
+
const compactToolGroupKey = compactToolGroupKeys[i];
|
|
1644
1751
|
const compactToolGroupSummary = participant.getCompactToolGroupSummary?.();
|
|
1645
1752
|
const nextCompactToolGroupKey = nextCompactToolGroupKeys[i];
|
|
1646
1753
|
const isContinuation = !!compactToolGroupKey && compactToolGroupKey === previousCompactToolGroupKey;
|
|
1647
1754
|
participant.setCompactToolContinuation?.(isContinuation, isContinuation ? previousCompactToolSummary : void 0);
|
|
1648
1755
|
participant.setCompactToolHasFollowingContinuation?.(!!compactToolGroupKey && compactToolGroupKey === nextCompactToolGroupKey);
|
|
1649
1756
|
if (compactToolGroupKey) {
|
|
1650
|
-
if (!isContinuation)
|
|
1757
|
+
if (!isContinuation) flushCompactRun();
|
|
1651
1758
|
currentCompactRun.push(participant);
|
|
1652
1759
|
} else {
|
|
1653
|
-
|
|
1760
|
+
if (getChatSpacingKind(component)) flushCompactRun();
|
|
1654
1761
|
participant.setCompactToolGroupLabelColor?.(void 0);
|
|
1655
1762
|
}
|
|
1656
1763
|
if (getChatSpacingKind(component)) {
|
|
@@ -1672,7 +1779,7 @@ function reconcileChatBoundarySpacers(chatContainer) {
|
|
|
1672
1779
|
if (getChatSpacingKind(component)) previousSpacingComponent = component;
|
|
1673
1780
|
nextChildren.push(component);
|
|
1674
1781
|
}
|
|
1675
|
-
|
|
1782
|
+
flushCompactRun();
|
|
1676
1783
|
if (children.length !== nextChildren.length || children.some((child, index) => child !== nextChildren[index])) chatContainer.children = nextChildren;
|
|
1677
1784
|
}
|
|
1678
1785
|
function getCompactRunLabelColor(participants) {
|
|
@@ -7084,6 +7191,22 @@ function resolveGapText(options) {
|
|
|
7084
7191
|
return separatorIndex >= 0 ? message.slice(0, separatorIndex).trim() : message;
|
|
7085
7192
|
}
|
|
7086
7193
|
//#endregion
|
|
7194
|
+
//#region src/tui/status-duration.ts
|
|
7195
|
+
const MINUTE_MS$1 = 6e4;
|
|
7196
|
+
const HOUR_MS = 60 * MINUTE_MS$1;
|
|
7197
|
+
const DAY_MS = 24 * HOUR_MS;
|
|
7198
|
+
function formatStatusDuration(ms, opts = {}) {
|
|
7199
|
+
const safeMs = Math.max(0, ms);
|
|
7200
|
+
const days = Math.floor(safeMs / DAY_MS);
|
|
7201
|
+
const hours = Math.floor(safeMs % DAY_MS / HOUR_MS);
|
|
7202
|
+
const minutes = Math.floor(safeMs % HOUR_MS / MINUTE_MS$1);
|
|
7203
|
+
const seconds = Math.floor(safeMs % MINUTE_MS$1 / 1e3);
|
|
7204
|
+
if (days > 0) return `${days}d${hours > 0 ? `${hours}hr` : ""}${minutes > 0 ? `${minutes}m` : ""}`;
|
|
7205
|
+
if (hours > 0) return `${hours}hr${minutes > 0 ? `${minutes}m` : ""}`;
|
|
7206
|
+
if (opts.includeSeconds) return minutes > 0 ? `${minutes}m${seconds}s` : `${Math.max(1, seconds)}s`;
|
|
7207
|
+
return `${Math.max(1, minutes)}m`;
|
|
7208
|
+
}
|
|
7209
|
+
//#endregion
|
|
7087
7210
|
//#region src/tui/components/collapsible.ts
|
|
7088
7211
|
/**
|
|
7089
7212
|
* Generic collapsible container component.
|
|
@@ -7424,6 +7547,8 @@ var ToolValidationErrorComponent = class extends _earendil_works_pi_tui.Containe
|
|
|
7424
7547
|
* Enhanced tool execution component with better collapsible support.
|
|
7425
7548
|
* This will replace the existing tool-execution.ts
|
|
7426
7549
|
*/
|
|
7550
|
+
/** CSI, OSC, and DCS/SOS/PM/APC sequences (terminated or not), and other two-byte ESC sequences. */
|
|
7551
|
+
const TERMINAL_SEQUENCE_RE = /\x1b\[[\x20-\x3f]*[\x40-\x7e]?|\x1b[\]PX^_][^\x07\x1b]*(?:\x07|\x1b\\)?|\x9b[\x20-\x3f]*[\x40-\x7e]?|\x1b[\x20-\x7e]?/g;
|
|
7427
7552
|
const CODE_HIGHLIGHT_THEME = {
|
|
7428
7553
|
default: (text) => theme.fg("toolArgs", text),
|
|
7429
7554
|
keyword: chalk.default.hex("#c084fc"),
|
|
@@ -7490,10 +7615,37 @@ const SHELL_CONTROL_WORDS = /* @__PURE__ */ new Set([
|
|
|
7490
7615
|
/**
|
|
7491
7616
|
* Convert absolute path to tilde notation if it's in home directory
|
|
7492
7617
|
*/
|
|
7493
|
-
|
|
7618
|
+
const SPINNER_FRAMES = [
|
|
7619
|
+
"⠋",
|
|
7620
|
+
"⠙",
|
|
7621
|
+
"⠹",
|
|
7622
|
+
"⠸",
|
|
7623
|
+
"⠼",
|
|
7624
|
+
"⠴",
|
|
7625
|
+
"⠦",
|
|
7626
|
+
"⠧",
|
|
7627
|
+
"⠇",
|
|
7628
|
+
"⠏"
|
|
7629
|
+
];
|
|
7630
|
+
/** Grouped quiet shell boxes start this narrow and widen, up to the full width, for longer rows. */
|
|
7631
|
+
const QUIET_SHELL_MIN_CONTENT_WIDTH = 76;
|
|
7632
|
+
/** First line of `message` when a failed result is a JSON error object, e.g. a rejected tool input. */
|
|
7633
|
+
function parseErrorMessage(output) {
|
|
7634
|
+
const trimmed = output.trim();
|
|
7635
|
+
if (!trimmed.startsWith("{")) return void 0;
|
|
7636
|
+
try {
|
|
7637
|
+
const message = JSON.parse(trimmed).message;
|
|
7638
|
+
return typeof message === "string" ? message.split("\n").find((line) => line.trim())?.trim() : void 0;
|
|
7639
|
+
} catch {
|
|
7640
|
+
return;
|
|
7641
|
+
}
|
|
7642
|
+
}
|
|
7643
|
+
/** Room kept for the right-aligned time so a ticking counter never changes the box width. */
|
|
7644
|
+
const QUIET_SHELL_TIME_WIDTH = 7;
|
|
7645
|
+
function shortenPath(path$3) {
|
|
7494
7646
|
const home = os.homedir();
|
|
7495
|
-
if (path.startsWith(home)) return `~${path.slice(home.length)}`;
|
|
7496
|
-
return path;
|
|
7647
|
+
if (path$3.startsWith(home)) return `~${path$3.slice(home.length)}`;
|
|
7648
|
+
return path$3;
|
|
7497
7649
|
}
|
|
7498
7650
|
/**
|
|
7499
7651
|
* Resolve a file path to an absolute path for use in file:// URLs.
|
|
@@ -7567,6 +7719,19 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7567
7719
|
options;
|
|
7568
7720
|
startTime = Date.now();
|
|
7569
7721
|
streamingOutput = "";
|
|
7722
|
+
argsStreaming = false;
|
|
7723
|
+
endTime;
|
|
7724
|
+
/** Set for history entries whose run time could not be recovered, so no fake `0ms` is shown. */
|
|
7725
|
+
durationUnknown = false;
|
|
7726
|
+
/** The sandbox's exit record for a shell call; more reliable than parsing the result text. */
|
|
7727
|
+
commandExit;
|
|
7728
|
+
liveUpdatesStopped = false;
|
|
7729
|
+
quietShellTicker;
|
|
7730
|
+
quietShellGroupWidth;
|
|
7731
|
+
quietShellGroupPreview;
|
|
7732
|
+
quietShellHeld = false;
|
|
7733
|
+
/** Rows the shell box preview has used so far; it never shrinks, so rows below don't jump. */
|
|
7734
|
+
quietShellPreviewRowFloor = 0;
|
|
7570
7735
|
quietDisplayMode;
|
|
7571
7736
|
quietPreviewLineLimit;
|
|
7572
7737
|
quietPreviewRowFloor = 0;
|
|
@@ -7598,12 +7763,32 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7598
7763
|
this.args = args;
|
|
7599
7764
|
if (rebuild) this.rebuild();
|
|
7600
7765
|
}
|
|
7766
|
+
setArgsStreaming(streaming) {
|
|
7767
|
+
if (this.argsStreaming === streaming) return;
|
|
7768
|
+
this.argsStreaming = streaming;
|
|
7769
|
+
this.rebuild();
|
|
7770
|
+
}
|
|
7601
7771
|
refresh() {
|
|
7602
7772
|
this.rebuild();
|
|
7603
7773
|
}
|
|
7604
7774
|
updateResult(result, isPartial = false) {
|
|
7605
7775
|
this.result = result;
|
|
7606
7776
|
this.isPartial = isPartial;
|
|
7777
|
+
if (!isPartial) this.endTime ??= Date.now();
|
|
7778
|
+
this.rebuild();
|
|
7779
|
+
}
|
|
7780
|
+
/** Restores the run time of a tool call rendered from history. */
|
|
7781
|
+
setRecordedTiming(startedAt, endedAt) {
|
|
7782
|
+
if (startedAt === void 0 || endedAt === void 0) this.durationUnknown = true;
|
|
7783
|
+
else {
|
|
7784
|
+
this.durationUnknown = false;
|
|
7785
|
+
this.startTime = startedAt;
|
|
7786
|
+
this.endTime = endedAt;
|
|
7787
|
+
}
|
|
7788
|
+
this.rebuild();
|
|
7789
|
+
}
|
|
7790
|
+
setCommandExit(exit) {
|
|
7791
|
+
this.commandExit = exit;
|
|
7607
7792
|
this.rebuild();
|
|
7608
7793
|
}
|
|
7609
7794
|
setBackgroundTaskId(taskId) {
|
|
@@ -7640,6 +7825,7 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7640
7825
|
const normalizedLimit = Number.isFinite(limit) ? limit : 2;
|
|
7641
7826
|
this.quietPreviewLineLimit = Math.min(8, Math.max(0, Math.floor(normalizedLimit)));
|
|
7642
7827
|
this.quietPreviewRowFloor = Math.min(this.quietPreviewRowFloor, this.quietPreviewLineLimit);
|
|
7828
|
+
this.quietShellPreviewRowFloor = Math.min(this.quietShellPreviewRowFloor, this.quietPreviewLineLimit);
|
|
7643
7829
|
this.rebuild();
|
|
7644
7830
|
}
|
|
7645
7831
|
setCompactToolModeColor(color) {
|
|
@@ -7649,17 +7835,142 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7649
7835
|
if (this.quietDisplayMode === "quiet") this.rebuild();
|
|
7650
7836
|
}
|
|
7651
7837
|
getChatSpacingKind() {
|
|
7652
|
-
if (this.quietDisplayMode === "quiet")
|
|
7838
|
+
if (this.quietDisplayMode === "quiet") {
|
|
7839
|
+
if (this.toolName !== _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND) return "quiet-compact-tool";
|
|
7840
|
+
if (this.isQuietCompactShell()) return this.quietShellHeld ? void 0 : "quiet-compact-tool";
|
|
7841
|
+
return "quiet-shell-tool";
|
|
7842
|
+
}
|
|
7653
7843
|
return "normal-tool";
|
|
7654
7844
|
}
|
|
7655
7845
|
getCompactToolGroupKey() {
|
|
7846
|
+
if (this.toolName === _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND && this.isQuietCompactShell()) return this.isShellDirectoryPending() ? PENDING_SHELL_GROUP_KEY : `$ ${this.getShellHeaderPath()}`;
|
|
7656
7847
|
if (this.getChatSpacingKind() !== "quiet-compact-tool") return void 0;
|
|
7657
7848
|
return this.getCompactToolLabel();
|
|
7658
7849
|
}
|
|
7659
7850
|
getCompactToolGroupSummary() {
|
|
7660
7851
|
if (this.getChatSpacingKind() !== "quiet-compact-tool") return void 0;
|
|
7852
|
+
if (this.toolName === _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND) return void 0;
|
|
7661
7853
|
return this.getCompactToolSummary();
|
|
7662
7854
|
}
|
|
7855
|
+
/**
|
|
7856
|
+
* Split a leading "cd <path>" off the command, since the path is shown separately. Callers bake
|
|
7857
|
+
* this prefix into the command instead of passing `cwd`, and separate it with "&&", ";", or a
|
|
7858
|
+
* bare newline — with quoted paths and leading whitespace also showing up.
|
|
7859
|
+
*/
|
|
7860
|
+
parseShellCommand() {
|
|
7861
|
+
const argsObj = this.args;
|
|
7862
|
+
const command = argsObj?.command ? String(argsObj.command) : "...";
|
|
7863
|
+
const cdMatch = command.match(/^\s*cd\s+(?:"([^"]*)"|'([^']*)'|([^\s;]+))\s*(?:&&|;|\n)\s*(?=\S)/);
|
|
7864
|
+
if (!cdMatch) return {
|
|
7865
|
+
command,
|
|
7866
|
+
cdPath: ""
|
|
7867
|
+
};
|
|
7868
|
+
return {
|
|
7869
|
+
command: command.slice(cdMatch[0].length),
|
|
7870
|
+
cdPath: cdMatch[1] ?? cdMatch[2] ?? cdMatch[3] ?? ""
|
|
7871
|
+
};
|
|
7872
|
+
}
|
|
7873
|
+
/**
|
|
7874
|
+
* The directory the command runs in, as written: the shell starts in `cwd`, then a leading `cd`
|
|
7875
|
+
* moves it, relative to `cwd` unless the `cd` path is absolute or starts at home.
|
|
7876
|
+
*/
|
|
7877
|
+
getShellDirectory() {
|
|
7878
|
+
const argsObj = this.args;
|
|
7879
|
+
const cwd = argsObj?.cwd ? String(argsObj.cwd) : "";
|
|
7880
|
+
const { cdPath } = this.parseShellCommand();
|
|
7881
|
+
if (!cwd || !cdPath) return cwd || cdPath;
|
|
7882
|
+
if (cdPath.startsWith("/") || cdPath === "~" || cdPath.startsWith("~/")) return cdPath;
|
|
7883
|
+
return (0, path.join)(cwd === "~" || cwd.startsWith("~/") ? os.homedir() + cwd.slice(1) : cwd, cdPath);
|
|
7884
|
+
}
|
|
7885
|
+
/** The directory a quiet shell group shows in its `$ <path>` header, resolved the way the sandbox resolves it. */
|
|
7886
|
+
getShellHeaderPath() {
|
|
7887
|
+
const raw = this.getShellDirectory();
|
|
7888
|
+
const expanded = raw === "~" || raw.startsWith("~/") ? os.homedir() + raw.slice(1) : raw;
|
|
7889
|
+
const projectRoot = this.options.projectRoot ?? process.cwd();
|
|
7890
|
+
const resolved = (0, path.resolve)(projectRoot, expanded || ".");
|
|
7891
|
+
const relative = (0, path.relative)(projectRoot, resolved);
|
|
7892
|
+
if (relative && !relative.startsWith("..") && !(0, path.isAbsolute)(relative)) return `./${relative}`;
|
|
7893
|
+
return shortenPath(resolved);
|
|
7894
|
+
}
|
|
7895
|
+
/**
|
|
7896
|
+
* While args stream, the directory is unknown until a complete `cd <dir> &&` prefix arrives. A
|
|
7897
|
+
* streaming `cwd` may still be partial, and without either the call may yet get one.
|
|
7898
|
+
*/
|
|
7899
|
+
isShellDirectoryPending() {
|
|
7900
|
+
if (!this.argsStreaming || this.result) return false;
|
|
7901
|
+
const argsObj = this.args;
|
|
7902
|
+
if (typeof argsObj?.cwd === "string") return true;
|
|
7903
|
+
if (typeof argsObj?.command !== "string") return true;
|
|
7904
|
+
if (this.parseShellCommand().cdPath) return false;
|
|
7905
|
+
return /^\s*(?:c|cd|cd\s[\s\S]*)?$/.test(argsObj.command);
|
|
7906
|
+
}
|
|
7907
|
+
/**
|
|
7908
|
+
* Held while its directory is unknown and a shell box sits right above it: drawing the row in
|
|
7909
|
+
* that box, then moving it to its own directory's box, would make the chat jump. Rendered once
|
|
7910
|
+
* the directory is known, the row only ever adds lines.
|
|
7911
|
+
*/
|
|
7912
|
+
setQuietShellHeld(held) {
|
|
7913
|
+
if (this.quietShellHeld === held) return;
|
|
7914
|
+
this.quietShellHeld = held;
|
|
7915
|
+
if (this.isQuietCompactShell()) this.rebuild();
|
|
7916
|
+
}
|
|
7917
|
+
getShellDescription() {
|
|
7918
|
+
const description = this.args?.description;
|
|
7919
|
+
return typeof description === "string" ? description.replace(/\s+/g, " ").trim() : "";
|
|
7920
|
+
}
|
|
7921
|
+
/**
|
|
7922
|
+
* In quiet mode, consecutive shell calls in one directory share a box: an optional preview of the
|
|
7923
|
+
* latest output, a `$ <path>` header, then one status row per call. Expanding shows the full box.
|
|
7924
|
+
*/
|
|
7925
|
+
isQuietCompactShell() {
|
|
7926
|
+
return this.quietDisplayMode === "quiet" && this.toolName === _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND && !this.expanded;
|
|
7927
|
+
}
|
|
7928
|
+
/**
|
|
7929
|
+
* The last `quietPreviewLineLimit` lines this call printed, for its box's shared preview; `[]` when
|
|
7930
|
+
* it printed nothing and `undefined` when previews are off.
|
|
7931
|
+
*/
|
|
7932
|
+
getQuietShellPreviewLines() {
|
|
7933
|
+
if (!this.isQuietCompactShell() || this.quietShellHeld || this.quietPreviewLineLimit <= 0) return void 0;
|
|
7934
|
+
const lines = (this.streamingOutput.trim() ? this.streamingOutput : this.getFormattedOutput()).split("\n").filter((line) => !/^(?:stdout:|stderr:|Exit code: -?\d+)$/.test(line.trim()));
|
|
7935
|
+
while (lines.length > 0 && lines[0].trim() === "") lines.shift();
|
|
7936
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines.pop();
|
|
7937
|
+
return lines.slice(-this.quietPreviewLineLimit);
|
|
7938
|
+
}
|
|
7939
|
+
/** Set on the first call of a shell box, which draws the box's preview above its header. */
|
|
7940
|
+
setQuietShellGroupPreview(lines) {
|
|
7941
|
+
const current = this.quietShellGroupPreview;
|
|
7942
|
+
if (current === lines || current && lines && current.join("\n") === lines.join("\n")) return;
|
|
7943
|
+
this.quietShellGroupPreview = lines;
|
|
7944
|
+
if (this.isQuietCompactShell()) this.rebuild();
|
|
7945
|
+
}
|
|
7946
|
+
/**
|
|
7947
|
+
* Text for a grouped quiet shell row: the description; while it may still stream in, `...` or how
|
|
7948
|
+
* much of the command has arrived (some models write a long command first); or the command's
|
|
7949
|
+
* first line when the call has none (e.g. it was rejected for a missing description).
|
|
7950
|
+
* Muted text is not the description, and truncates rather than widening the box.
|
|
7951
|
+
*/
|
|
7952
|
+
getQuietShellRowText() {
|
|
7953
|
+
const description = this.getShellDescription();
|
|
7954
|
+
if (description) return {
|
|
7955
|
+
text: description,
|
|
7956
|
+
muted: false
|
|
7957
|
+
};
|
|
7958
|
+
const command = this.args?.command;
|
|
7959
|
+
if (this.argsStreaming && !this.result) {
|
|
7960
|
+
if (typeof command !== "string" || !command) return {
|
|
7961
|
+
text: "...",
|
|
7962
|
+
muted: false
|
|
7963
|
+
};
|
|
7964
|
+
return {
|
|
7965
|
+
text: `writing command (${command.length < 1e3 ? `${command.length}` : `${(command.length / 1e3).toFixed(1)}k`} chars)`,
|
|
7966
|
+
muted: true
|
|
7967
|
+
};
|
|
7968
|
+
}
|
|
7969
|
+
return {
|
|
7970
|
+
text: (this.parseShellCommand().command.split("\n").find((line) => line.trim()) ?? "...").trim(),
|
|
7971
|
+
muted: true
|
|
7972
|
+
};
|
|
7973
|
+
}
|
|
7663
7974
|
hasQuietStreamingPreview() {
|
|
7664
7975
|
return this.quietDisplayMode === "quiet" && this.toolName !== _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND && this.quietPreviewLineLimit > 0 && (this.quietPreviewRowFloor > 0 || this.getQuietActivePreview() !== "");
|
|
7665
7976
|
}
|
|
@@ -7703,35 +8014,6 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7703
8014
|
if (this.shouldShowLeadingPadding()) this.contentBox.addChild(new _earendil_works_pi_tui.Text("", 0, 0));
|
|
7704
8015
|
}
|
|
7705
8016
|
/**
|
|
7706
|
-
* Quiet shell output follows the same rule as every other tool's preview:
|
|
7707
|
-
* `quietPreviewLineLimit` output lines (the tail, since that is where errors
|
|
7708
|
-
* land), or none at all when the limit is 0. Expanding (ctrl+e) shows everything.
|
|
7709
|
-
*/
|
|
7710
|
-
limitQuietShellLines(lines) {
|
|
7711
|
-
if (this.quietDisplayMode !== "quiet" || this.expanded) return lines;
|
|
7712
|
-
const limit = this.quietPreviewLineLimit;
|
|
7713
|
-
if (limit <= 0) return [];
|
|
7714
|
-
if (this.fitsQuietLimit(lines.length, limit)) return lines;
|
|
7715
|
-
return [this.quietHiddenLinesMarker(lines.length - limit), ...lines.slice(-limit)];
|
|
7716
|
-
}
|
|
7717
|
-
/**
|
|
7718
|
-
* The command is the shell tool's header, so at least one line always shows;
|
|
7719
|
-
* the rest of a long command (heredocs, inline scripts) is capped like output.
|
|
7720
|
-
*/
|
|
7721
|
-
limitQuietShellCommandLines(lines) {
|
|
7722
|
-
if (this.quietDisplayMode !== "quiet" || this.expanded) return lines;
|
|
7723
|
-
const limit = Math.max(1, this.quietPreviewLineLimit);
|
|
7724
|
-
if (this.fitsQuietLimit(lines.length, limit)) return lines;
|
|
7725
|
-
return [...lines.slice(0, limit), this.quietHiddenLinesMarker(lines.length - limit)];
|
|
7726
|
-
}
|
|
7727
|
-
/** A `⋯ (+1 line)` marker costs the same row as the line it hides, so just show the line. */
|
|
7728
|
-
fitsQuietLimit(count, limit) {
|
|
7729
|
-
return count <= limit + 1;
|
|
7730
|
-
}
|
|
7731
|
-
quietHiddenLinesMarker(hidden) {
|
|
7732
|
-
return theme.fg("muted", `⋯ (+${hidden} ${hidden === 1 ? "line" : "lines"})`);
|
|
7733
|
-
}
|
|
7734
|
-
/**
|
|
7735
8017
|
* Full clear-and-rebuild. Called when:
|
|
7736
8018
|
* - args change (updateArgs)
|
|
7737
8019
|
* - result arrives or changes (updateResult)
|
|
@@ -7741,6 +8023,7 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7741
8023
|
rebuildForWidth(_width) {
|
|
7742
8024
|
this.updateBgColor();
|
|
7743
8025
|
this.contentBox.clear();
|
|
8026
|
+
if (!this.isQuietCompactShell()) this.syncQuietShellTicker(false);
|
|
7744
8027
|
if (this.quietDisplayMode === "quiet" && this.toolName !== _mastra_code_sdk_tool_names.MC_TOOLS.EXECUTE_COMMAND) {
|
|
7745
8028
|
this.renderCompactTool();
|
|
7746
8029
|
return;
|
|
@@ -7821,10 +8104,10 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
7821
8104
|
return theme.fg("text", preview);
|
|
7822
8105
|
}
|
|
7823
8106
|
highlightQuietCodePreview(preview) {
|
|
7824
|
-
const path = this.getFirstStringArg("path");
|
|
8107
|
+
const path$4 = this.getFirstStringArg("path");
|
|
7825
8108
|
try {
|
|
7826
8109
|
return (0, cli_highlight.highlight)(preview, {
|
|
7827
|
-
language: getLanguageFromPath(path),
|
|
8110
|
+
language: getLanguageFromPath(path$4),
|
|
7828
8111
|
ignoreIllegals: true,
|
|
7829
8112
|
theme: QUIET_CODE_HIGHLIGHT_THEME
|
|
7830
8113
|
});
|
|
@@ -8179,16 +8462,16 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8179
8462
|
}
|
|
8180
8463
|
getImmediateDirnameStart(summary) {
|
|
8181
8464
|
const pathEnd = summary.indexOf(":");
|
|
8182
|
-
const path = pathEnd >= 0 ? summary.slice(0, pathEnd) : summary;
|
|
8183
|
-
const filenameSlashIndex = path.lastIndexOf("/");
|
|
8465
|
+
const path$5 = pathEnd >= 0 ? summary.slice(0, pathEnd) : summary;
|
|
8466
|
+
const filenameSlashIndex = path$5.lastIndexOf("/");
|
|
8184
8467
|
if (filenameSlashIndex < 0) return void 0;
|
|
8185
|
-
const dirnameSlashIndex = path.lastIndexOf("/", filenameSlashIndex - 1);
|
|
8468
|
+
const dirnameSlashIndex = path$5.lastIndexOf("/", filenameSlashIndex - 1);
|
|
8186
8469
|
return dirnameSlashIndex >= 0 ? dirnameSlashIndex : filenameSlashIndex;
|
|
8187
8470
|
}
|
|
8188
8471
|
hasCompletePathSegment(summary) {
|
|
8189
8472
|
const pathEnd = summary.indexOf(":");
|
|
8190
|
-
const path = pathEnd >= 0 ? summary.slice(0, pathEnd) : summary;
|
|
8191
|
-
const lastSegment = path.slice(path.lastIndexOf("/") + 1);
|
|
8473
|
+
const path$6 = pathEnd >= 0 ? summary.slice(0, pathEnd) : summary;
|
|
8474
|
+
const lastSegment = path$6.slice(path$6.lastIndexOf("/") + 1);
|
|
8192
8475
|
return lastSegment.length > 0 && (pathEnd >= 0 || lastSegment.includes("."));
|
|
8193
8476
|
}
|
|
8194
8477
|
formatSharedPrefixPlaceholder(summary, sharedPrefixLength) {
|
|
@@ -8200,11 +8483,11 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8200
8483
|
let i = 0;
|
|
8201
8484
|
while (i < a.length && i < b.length && a[i] === b[i]) i++;
|
|
8202
8485
|
const pathEnd = b.indexOf(":");
|
|
8203
|
-
const path = pathEnd >= 0 ? b.slice(0, pathEnd) : b;
|
|
8204
|
-
const sharedPathLength = Math.min(i, path.length);
|
|
8486
|
+
const path$7 = pathEnd >= 0 ? b.slice(0, pathEnd) : b;
|
|
8487
|
+
const sharedPathLength = Math.min(i, path$7.length);
|
|
8205
8488
|
if (sharedPathLength === 0) return 0;
|
|
8206
|
-
const matchingSegmentBoundary = path.lastIndexOf("/", sharedPathLength - 1);
|
|
8207
|
-
const currentSegmentStart = path.lastIndexOf("/", Math.max(0, sharedPathLength - 1));
|
|
8489
|
+
const matchingSegmentBoundary = path$7.lastIndexOf("/", sharedPathLength - 1);
|
|
8490
|
+
const currentSegmentStart = path$7.lastIndexOf("/", Math.max(0, sharedPathLength - 1));
|
|
8208
8491
|
if (i === b.length && i === a.length) return this.getImmediateDirnameStart(b) ?? b.length;
|
|
8209
8492
|
if (i === b.length || i === a.length) return b.length;
|
|
8210
8493
|
if (i < b.length && i < a.length) {
|
|
@@ -8265,17 +8548,17 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8265
8548
|
const line = typeof argsObj?.line === "number" ? argsObj.line : void 0;
|
|
8266
8549
|
const start = viewRange?.[0] ?? offset ?? line;
|
|
8267
8550
|
const end = viewRange?.[1] ?? (offset !== void 0 && limit !== void 0 ? offset + limit - 1 : line);
|
|
8268
|
-
const path = rawPath;
|
|
8269
|
-
if (start === void 0) return path;
|
|
8270
|
-
return end !== void 0 && end !== start ? `${path}:${start}-${end}` : `${path}:${start}`;
|
|
8551
|
+
const path$8 = rawPath;
|
|
8552
|
+
if (start === void 0) return path$8;
|
|
8553
|
+
return end !== void 0 && end !== start ? `${path$8}:${start}-${end}` : `${path$8}:${start}`;
|
|
8271
8554
|
}
|
|
8272
8555
|
formatEditSummary() {
|
|
8273
|
-
const path = this.getFirstStringArg("path");
|
|
8274
|
-
if (!path) return "";
|
|
8556
|
+
const path$9 = this.getFirstStringArg("path");
|
|
8557
|
+
if (!path$9) return "";
|
|
8275
8558
|
const output = this.getFormattedOutput();
|
|
8276
|
-
const escapedPath = path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8559
|
+
const escapedPath = path$9.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8277
8560
|
const match = output.match(new RegExp(`Replaced \\d+ occurrences? in ${escapedPath} \\(lines ([^)]+)\\)`));
|
|
8278
|
-
return match?.[1] ? `${path}:${match[1]}` : path;
|
|
8561
|
+
return match?.[1] ? `${path$9}:${match[1]}` : path$9;
|
|
8279
8562
|
}
|
|
8280
8563
|
formatListSummary() {
|
|
8281
8564
|
const target = this.getFirstStringArg("path") || this.getFirstStringArg("pattern");
|
|
@@ -8373,9 +8656,9 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8373
8656
|
}
|
|
8374
8657
|
const border = (char) => this.formatToolBorder(char);
|
|
8375
8658
|
if (!this.result || this.isPartial) {
|
|
8376
|
-
const path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8659
|
+
const path$11 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8377
8660
|
const status = this.getStatusIndicator();
|
|
8378
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath, startLine) : theme.fg("toolArgs", path);
|
|
8661
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$11), fullPath, startLine) : theme.fg("toolArgs", path$11);
|
|
8379
8662
|
const footerText = `${theme.bold(theme.fg("toolTitle", "view"))} ${pathDisplay}${rangeDisplay}${status}`;
|
|
8380
8663
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
8381
8664
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(`${border("╰──")} ${footerText}`, 0, 0));
|
|
@@ -8383,9 +8666,9 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8383
8666
|
}
|
|
8384
8667
|
const status = this.getStatusIndicator();
|
|
8385
8668
|
const availableForPath = this.renderWidth - ("╰── view " + (rangeDisplay ? `:XXX,XXX` : "") + " ✓").length - 6 - 0;
|
|
8386
|
-
let path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8387
|
-
if (path.length > availableForPath && availableForPath > 10) path = "…" + path.slice(-(availableForPath - 1));
|
|
8388
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath, startLine) : theme.fg("toolArgs", path);
|
|
8669
|
+
let path$10 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8670
|
+
if (path$10.length > availableForPath && availableForPath > 10) path$10 = "…" + path$10.slice(-(availableForPath - 1));
|
|
8671
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$10), fullPath, startLine) : theme.fg("toolArgs", path$10);
|
|
8389
8672
|
const footerText = `${theme.bold(theme.fg("toolTitle", "view"))} ${pathDisplay}${rangeDisplay}${status}`;
|
|
8390
8673
|
this.addLeadingPadding();
|
|
8391
8674
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
@@ -8411,10 +8694,15 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8411
8694
|
}
|
|
8412
8695
|
renderBashToolEnhanced() {
|
|
8413
8696
|
const argsObj = this.args;
|
|
8414
|
-
|
|
8697
|
+
const { command } = this.parseShellCommand();
|
|
8415
8698
|
const timeout = argsObj?.timeout;
|
|
8416
|
-
|
|
8417
|
-
|
|
8699
|
+
if (this.isQuietCompactShell()) {
|
|
8700
|
+
if (this.quietShellHeld) this.syncQuietShellTicker(false);
|
|
8701
|
+
else this.renderQuietShellGroupRow();
|
|
8702
|
+
return;
|
|
8703
|
+
}
|
|
8704
|
+
const directory = this.getShellDirectory();
|
|
8705
|
+
const cwd = directory ? shortenPath(directory) : "";
|
|
8418
8706
|
let maxStreamLines;
|
|
8419
8707
|
const tailMatch = command.match(/\|\s*tail\s+(?:-n\s+)?(-?\d+)\s*$/);
|
|
8420
8708
|
if (tailMatch) maxStreamLines = Math.abs(parseInt(tailMatch[1], 10));
|
|
@@ -8442,7 +8730,7 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8442
8730
|
}
|
|
8443
8731
|
const footerPromptWidth = (0, _earendil_works_pi_tui.visibleWidth)(footerPrompt);
|
|
8444
8732
|
const footerWrapWidth = Math.max(1, contentWidth - 2 - footerPromptWidth);
|
|
8445
|
-
const footerLines = this.
|
|
8733
|
+
const footerLines = this.wrapQuietShellCommand(command, footerWrapWidth);
|
|
8446
8734
|
const footerSuffixWidth = (0, _earendil_works_pi_tui.visibleWidth)(footerSuffix);
|
|
8447
8735
|
const continuationIndent = " ".repeat(footerPromptWidth);
|
|
8448
8736
|
footerLines.forEach((footerLine, index) => {
|
|
@@ -8459,7 +8747,7 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8459
8747
|
while (lines.length > 0 && lines[0] === "") lines.shift();
|
|
8460
8748
|
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
8461
8749
|
if (maxStreamLines && lines.length > maxStreamLines) lines = lines.slice(-maxStreamLines);
|
|
8462
|
-
renderBorderedShell(status,
|
|
8750
|
+
renderBorderedShell(status, lines);
|
|
8463
8751
|
return;
|
|
8464
8752
|
}
|
|
8465
8753
|
const prepareOutputLines = (output) => {
|
|
@@ -8469,21 +8757,119 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8469
8757
|
if (maxStreamLines && lines.length > maxStreamLines) lines = lines.slice(-maxStreamLines);
|
|
8470
8758
|
return lines;
|
|
8471
8759
|
};
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8760
|
+
const failed = this.getShellFailureLine() !== void 0;
|
|
8761
|
+
const output = this.streamingOutput.trim() || this.getFormattedOutput();
|
|
8762
|
+
renderBorderedShell(this.getStatusIndicator(failed), prepareOutputLines(output));
|
|
8763
|
+
}
|
|
8764
|
+
/**
|
|
8765
|
+
* One call's slice of a shared quiet shell box. The first call in a run opens the box with a
|
|
8766
|
+
* `$ <path>` header, a call in a new directory adds another header, and the last call closes it.
|
|
8767
|
+
*/
|
|
8768
|
+
renderQuietShellGroupRow() {
|
|
8769
|
+
const border = (char) => this.formatToolBorder(char);
|
|
8770
|
+
const fullWidth = Math.max(20, this.renderWidth - 0 - 4);
|
|
8771
|
+
const naturalWidth = this.quietShellGroupWidth ?? this.getQuietShellNaturalWidth() ?? 0;
|
|
8772
|
+
const contentWidth = Math.min(fullWidth, Math.max(QUIET_SHELL_MIN_CONTENT_WIDTH, naturalWidth));
|
|
8773
|
+
const rule = (left, right) => `${border(left)}${border("─".repeat(contentWidth + 2))}${border(right)}`;
|
|
8774
|
+
const singleLine = (text) => text.replace(TERMINAL_SEQUENCE_RE, "").replace(/[\t\n\r\v\f]/g, " ").replace(/[\x00-\x08\x0e-\x1f\x7f-\x9f]/g, "");
|
|
8775
|
+
const row = (left, right = "") => {
|
|
8776
|
+
const rightWidth = (0, _earendil_works_pi_tui.visibleWidth)(right);
|
|
8777
|
+
const leftText = truncateAnsi(left, Math.max(1, contentWidth - (rightWidth ? rightWidth + 1 : 0)));
|
|
8778
|
+
const padding = " ".repeat(Math.max(rightWidth ? 1 : 0, contentWidth - (0, _earendil_works_pi_tui.visibleWidth)(leftText) - rightWidth));
|
|
8779
|
+
return `${border("│")} ${leftText}${padding}${right} ${border("│")}`;
|
|
8780
|
+
};
|
|
8781
|
+
const headerPath = singleLine(this.getShellHeaderPath());
|
|
8782
|
+
const header = row(`${theme.bold(theme.fg("toolTitle", "$"))} ${theme.fg("muted", headerPath)}`);
|
|
8783
|
+
const lines = [];
|
|
8784
|
+
if (!this.compactToolContinuation) {
|
|
8785
|
+
lines.push(rule("╭", "╮"));
|
|
8786
|
+
const preview = this.quietShellGroupPreview ?? [];
|
|
8787
|
+
this.quietShellPreviewRowFloor = Math.min(this.quietPreviewLineLimit, Math.max(this.quietShellPreviewRowFloor, preview.length));
|
|
8788
|
+
if (this.quietShellPreviewRowFloor > 0) {
|
|
8789
|
+
for (let i = 0; i < this.quietShellPreviewRowFloor; i++) lines.push(row(theme.fg("toolOutput", singleLine(preview[i] ?? ""))));
|
|
8790
|
+
lines.push(rule("├", "┤"));
|
|
8791
|
+
}
|
|
8792
|
+
lines.push(header, rule("├", "┤"));
|
|
8793
|
+
}
|
|
8794
|
+
const rowText = this.getQuietShellRowText();
|
|
8795
|
+
const description = rowText.muted ? theme.fg("muted", singleLine(rowText.text)) : singleLine(rowText.text);
|
|
8796
|
+
const isBackground = !!this.backgroundTaskId || this.args?.background === true;
|
|
8797
|
+
const running = !this.result || this.isPartial;
|
|
8798
|
+
let mark;
|
|
8799
|
+
let time;
|
|
8800
|
+
let errorLine;
|
|
8801
|
+
if (isBackground && (this.backgroundTaskId || !running)) {
|
|
8802
|
+
mark = theme.fg("accent", "◷");
|
|
8803
|
+
time = "started";
|
|
8804
|
+
} else if (running && this.liveUpdatesStopped) {
|
|
8805
|
+
mark = theme.fg("muted", "■");
|
|
8806
|
+
time = "stopped";
|
|
8807
|
+
} else if (running) {
|
|
8808
|
+
mark = theme.fg("warning", SPINNER_FRAMES[Math.floor(Date.now() / 100) % SPINNER_FRAMES.length]);
|
|
8809
|
+
const elapsed = Date.now() - this.startTime;
|
|
8810
|
+
time = elapsed < 6e4 ? `${Math.floor(elapsed / 1e3)}s` : formatStatusDuration(elapsed, { includeSeconds: true });
|
|
8811
|
+
} else {
|
|
8812
|
+
errorLine = this.getShellFailureLine();
|
|
8813
|
+
mark = errorLine !== void 0 ? theme.fg("error", "✗") : theme.fg("success", "✓");
|
|
8814
|
+
time = this.formatDuration();
|
|
8477
8815
|
}
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8816
|
+
lines.push(row(`${mark} ${description}`, theme.fg("muted", time)));
|
|
8817
|
+
if (errorLine) lines.push(row(theme.fg("error", ` └▸ ${singleLine(errorLine)}`)));
|
|
8818
|
+
if (!this.compactToolHasFollowingContinuation) lines.push(rule("╰", "╯"));
|
|
8819
|
+
const maxLineWidth = Math.max(1, this.renderWidth - 0);
|
|
8820
|
+
this.contentBox.addChild(new _earendil_works_pi_tui.Text(lines.map((line) => truncateAnsi(line, maxLineWidth)).join("\n"), 0, 0));
|
|
8821
|
+
this.syncQuietShellTicker(running && !isBackground && !this.liveUpdatesStopped);
|
|
8822
|
+
}
|
|
8823
|
+
/**
|
|
8824
|
+
* Returns the line explaining a failed shell call, or `undefined` when it succeeded. Failure comes
|
|
8825
|
+
* from the sandbox's exit record, an error result, or ordinary output ending in a nonzero
|
|
8826
|
+
* "Exit code: N" — never from the output text, which routinely contains words like "error:" on success.
|
|
8827
|
+
*/
|
|
8828
|
+
getShellFailureLine() {
|
|
8829
|
+
const resultLines = this.getFormattedOutput().split("\n");
|
|
8830
|
+
const exitCode = resultLines.findLast((line) => line.trim() !== "")?.trim().match(/^Exit code: (-?\d+)$/)?.[1] ?? (this.commandExit && this.commandExit.exitCode >= 0 ? String(this.commandExit.exitCode) : void 0);
|
|
8831
|
+
if (!(this.result?.isError || this.commandExit?.success === false || exitCode !== void 0 && exitCode !== "0")) return void 0;
|
|
8832
|
+
const message = parseErrorMessage(resultLines.join("\n"));
|
|
8833
|
+
if (message) return message;
|
|
8834
|
+
const contentLines = (text) => text.split("\n").map((line) => line.trim()).filter((line) => line && !/^(?:stdout:|stderr:|Exit code: -?\d+)$/.test(line));
|
|
8835
|
+
const errorPattern = /Error:|TypeError:|SyntaxError:|ReferenceError:|command not found|fatal:|error:|No such file or directory|Permission denied/i;
|
|
8836
|
+
const stderrLines = contentLines(resultLines.join("\n").match(/^stderr:\n([\s\S]*)$/m)?.[1] ?? "");
|
|
8837
|
+
const lines = contentLines(this.streamingOutput.trim() || resultLines.join("\n"));
|
|
8838
|
+
return stderrLines.find((line) => errorPattern.test(line)) ?? stderrLines.at(-1) ?? lines.find((line) => errorPattern.test(line)) ?? (exitCode !== void 0 ? `exit code ${exitCode}` : lines.at(-1) ?? "");
|
|
8839
|
+
}
|
|
8840
|
+
/** Keeps a running grouped row's spinner and seconds counter moving between output events. */
|
|
8841
|
+
syncQuietShellTicker(active) {
|
|
8842
|
+
if (!active) {
|
|
8843
|
+
if (this.quietShellTicker) clearInterval(this.quietShellTicker);
|
|
8844
|
+
this.quietShellTicker = void 0;
|
|
8482
8845
|
return;
|
|
8483
8846
|
}
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8847
|
+
if (this.quietShellTicker) return;
|
|
8848
|
+
this.quietShellTicker = setInterval(() => {
|
|
8849
|
+
this.rebuild();
|
|
8850
|
+
this.ui.requestRender();
|
|
8851
|
+
}, 100);
|
|
8852
|
+
this.quietShellTicker.unref?.();
|
|
8853
|
+
}
|
|
8854
|
+
/** Content width this call's rows need; reconciliation gives the whole group the widest one. */
|
|
8855
|
+
getQuietShellNaturalWidth() {
|
|
8856
|
+
if (!this.isQuietCompactShell() || this.quietShellHeld) return void 0;
|
|
8857
|
+
const header = 2 + (0, _earendil_works_pi_tui.visibleWidth)(this.getShellHeaderPath());
|
|
8858
|
+
const rowText = this.getQuietShellRowText();
|
|
8859
|
+
const row = 2 + (rowText.muted ? 0 : (0, _earendil_works_pi_tui.visibleWidth)(rowText.text)) + 1 + QUIET_SHELL_TIME_WIDTH;
|
|
8860
|
+
return Math.max(header, row);
|
|
8861
|
+
}
|
|
8862
|
+
setQuietShellGroupWidth(width) {
|
|
8863
|
+
if (this.quietShellGroupWidth === width) return;
|
|
8864
|
+
this.quietShellGroupWidth = width;
|
|
8865
|
+
if (this.isQuietCompactShell()) this.rebuild();
|
|
8866
|
+
}
|
|
8867
|
+
/** Called when the agent run ends without this tool finishing, so nothing keeps animating. */
|
|
8868
|
+
stopLiveUpdates() {
|
|
8869
|
+
if (this.liveUpdatesStopped) return;
|
|
8870
|
+
this.liveUpdatesStopped = true;
|
|
8871
|
+
this.syncQuietShellTicker(false);
|
|
8872
|
+
this.rebuild();
|
|
8487
8873
|
}
|
|
8488
8874
|
renderProcessToolEnhanced() {
|
|
8489
8875
|
const argsObj = this.args;
|
|
@@ -8526,9 +8912,9 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8526
8912
|
const startLineNum = argsObj?.start_line ? Number(argsObj.start_line) : void 0;
|
|
8527
8913
|
const startLine = startLineNum ? `:${String(startLineNum)}` : "";
|
|
8528
8914
|
if (!this.result || this.isPartial) {
|
|
8529
|
-
const path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8915
|
+
const path$13 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8530
8916
|
const status = this.getStatusIndicator();
|
|
8531
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath, startLineNum) : theme.fg("toolArgs", path);
|
|
8917
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$13), fullPath, startLineNum) : theme.fg("toolArgs", path$13);
|
|
8532
8918
|
const oldStr = argsObj?.old_str ?? argsObj?.old_string;
|
|
8533
8919
|
const newStr = argsObj?.new_str ?? argsObj?.new_string;
|
|
8534
8920
|
if (oldStr != null && newStr != null) {
|
|
@@ -8566,9 +8952,9 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8566
8952
|
const status = this.getStatusIndicator();
|
|
8567
8953
|
const termWidth = this.renderWidth;
|
|
8568
8954
|
const availableForPath = termWidth - ("╰── edit " + startLine + " ✓").length - 6 - 0;
|
|
8569
|
-
let path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8570
|
-
if (path.length > availableForPath && availableForPath > 10) path = "…" + path.slice(-(availableForPath - 1));
|
|
8571
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath, startLineNum) : theme.fg("toolArgs", path);
|
|
8955
|
+
let path$12 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8956
|
+
if (path$12.length > availableForPath && availableForPath > 10) path$12 = "…" + path$12.slice(-(availableForPath - 1));
|
|
8957
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$12), fullPath, startLineNum) : theme.fg("toolArgs", path$12);
|
|
8572
8958
|
const footerText = `${theme.bold(theme.fg("toolTitle", "edit"))} ${pathDisplay}${theme.fg("muted", startLine)}${status}`;
|
|
8573
8959
|
this.addLeadingPadding();
|
|
8574
8960
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
@@ -8693,9 +9079,9 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8693
9079
|
if (!this.result || this.isPartial) {
|
|
8694
9080
|
if (!content) {
|
|
8695
9081
|
const writeBorder = (char) => this.formatToolBorder(char);
|
|
8696
|
-
const path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
9082
|
+
const path$16 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8697
9083
|
const status = this.getStatusIndicator();
|
|
8698
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath) : theme.fg("toolArgs", path);
|
|
9084
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$16), fullPath) : theme.fg("toolArgs", path$16);
|
|
8699
9085
|
const footerText = `${theme.bold(theme.fg("toolTitle", "write"))} ${pathDisplay}${status}`;
|
|
8700
9086
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(writeBorder("╭──"), 0, 0));
|
|
8701
9087
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(`${writeBorder("╰──")} ${footerText}`, 0, 0));
|
|
@@ -8705,10 +9091,10 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8705
9091
|
const status = this.getStatusIndicator();
|
|
8706
9092
|
const termWidth = this.renderWidth;
|
|
8707
9093
|
const maxLineWidth = termWidth - 4 - 0;
|
|
8708
|
-
let path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
9094
|
+
let path$15 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8709
9095
|
const availableForPath = termWidth - 13 - 6 - 0;
|
|
8710
|
-
if (path.length > availableForPath && availableForPath > 10) path = "…" + path.slice(-(availableForPath - 1));
|
|
8711
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath) : theme.fg("toolArgs", path);
|
|
9096
|
+
if (path$15.length > availableForPath && availableForPath > 10) path$15 = "…" + path$15.slice(-(availableForPath - 1));
|
|
9097
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$15), fullPath) : theme.fg("toolArgs", path$15);
|
|
8712
9098
|
const footerText = `${theme.bold(theme.fg("toolTitle", "write"))} ${pathDisplay}${status}`;
|
|
8713
9099
|
this.addLeadingPadding();
|
|
8714
9100
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
@@ -8734,10 +9120,10 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8734
9120
|
const status = this.getStatusIndicator();
|
|
8735
9121
|
const termWidth = this.renderWidth;
|
|
8736
9122
|
const maxLineWidth = termWidth - 4 - 0;
|
|
8737
|
-
let path = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
9123
|
+
let path$14 = argsObj?.path ? shortenPath(String(argsObj.path)) : "...";
|
|
8738
9124
|
const availableForPath = termWidth - 13 - 6 - 0;
|
|
8739
|
-
if (path.length > availableForPath && availableForPath > 10) path = "…" + path.slice(-(availableForPath - 1));
|
|
8740
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath) : theme.fg("toolArgs", path);
|
|
9125
|
+
if (path$14.length > availableForPath && availableForPath > 10) path$14 = "…" + path$14.slice(-(availableForPath - 1));
|
|
9126
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$14), fullPath) : theme.fg("toolArgs", path$14);
|
|
8741
9127
|
const footerText = `${theme.bold(theme.fg("toolTitle", "write"))} ${pathDisplay}${status}`;
|
|
8742
9128
|
this.addLeadingPadding();
|
|
8743
9129
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
@@ -8772,14 +9158,14 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8772
9158
|
renderListFilesEnhanced() {
|
|
8773
9159
|
const argsObj = this.args;
|
|
8774
9160
|
const fullPath = argsObj?.path ? String(argsObj.path) : "";
|
|
8775
|
-
const path = argsObj?.path ? shortenPath(String(argsObj.path)) : "/";
|
|
9161
|
+
const path$17 = argsObj?.path ? shortenPath(String(argsObj.path)) : "/";
|
|
8776
9162
|
const pattern = argsObj?.pattern ? String(argsObj.pattern) : "";
|
|
8777
9163
|
const patternDisplay = pattern ? " " + theme.fg("muted", pattern) : "";
|
|
8778
9164
|
const border = (char) => this.formatToolBorder(char);
|
|
8779
9165
|
const status = this.getStatusIndicator();
|
|
8780
9166
|
const maxLineWidth = this.renderWidth - 4 - 0;
|
|
8781
9167
|
if (!this.result || this.isPartial) {
|
|
8782
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath) : theme.fg("toolArgs", path);
|
|
9168
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$17), fullPath) : theme.fg("toolArgs", path$17);
|
|
8783
9169
|
const footerText = `${theme.bold(theme.fg("toolTitle", "list"))} ${pathDisplay}${patternDisplay}${status}`;
|
|
8784
9170
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
8785
9171
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(`${border("╰──")} ${footerText}`, 0, 0));
|
|
@@ -8800,7 +9186,7 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
8800
9186
|
skippedAbove = totalLines - collapsedLines;
|
|
8801
9187
|
lines = lines.slice(-collapsedLines);
|
|
8802
9188
|
}
|
|
8803
|
-
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path), fullPath) : theme.fg("toolArgs", path);
|
|
9189
|
+
const pathDisplay = fullPath ? fileLink(theme.fg("toolArgs", path$17), fullPath) : theme.fg("toolArgs", path$17);
|
|
8804
9190
|
const footerText = `${theme.bold(theme.fg("toolTitle", "list"))} ${pathDisplay}${patternDisplay}${summaryDisplay}${status}`;
|
|
8805
9191
|
this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("╭──"), 0, 0));
|
|
8806
9192
|
if (skippedAbove > 0) this.contentBox.addChild(new _earendil_works_pi_tui.Text(border("│") + " " + theme.fg("muted", `... ${skippedAbove} lines above (ctrl+e to expand)`), 0, 0));
|
|
@@ -9212,10 +9598,16 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
9212
9598
|
return this.isPartial ? theme.fg("muted", " ⋯") : isError ? theme.fg("error", " ✗") : theme.fg("success", " ✓");
|
|
9213
9599
|
}
|
|
9214
9600
|
getDurationSuffix() {
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9601
|
+
const duration = this.formatDuration();
|
|
9602
|
+
if (this.isPartial || !duration) return "";
|
|
9603
|
+
return theme.fg("muted", ` ${duration}`);
|
|
9604
|
+
}
|
|
9605
|
+
formatDuration() {
|
|
9606
|
+
if (this.durationUnknown) return "";
|
|
9607
|
+
const ms = (this.endTime ?? Date.now()) - this.startTime;
|
|
9608
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
9609
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
9610
|
+
return formatStatusDuration(ms, { includeSeconds: true });
|
|
9219
9611
|
}
|
|
9220
9612
|
getFormattedOutput() {
|
|
9221
9613
|
if (!this.result) return "";
|
|
@@ -9258,8 +9650,8 @@ var ToolExecutionComponentEnhanced = class extends WidthAwareContainer {
|
|
|
9258
9650
|
}
|
|
9259
9651
|
};
|
|
9260
9652
|
/** Map file extensions to highlight.js language names */
|
|
9261
|
-
function getLanguageFromPath(path) {
|
|
9262
|
-
const ext = path.split(".").pop()?.toLowerCase();
|
|
9653
|
+
function getLanguageFromPath(path$18) {
|
|
9654
|
+
const ext = path$18.split(".").pop()?.toLowerCase();
|
|
9263
9655
|
return ext ? {
|
|
9264
9656
|
ts: "typescript",
|
|
9265
9657
|
tsx: "typescript",
|
|
@@ -9325,11 +9717,11 @@ function getPlainCodeFromViewOutput(content, startLine) {
|
|
|
9325
9717
|
return codeLines.join("\n");
|
|
9326
9718
|
}
|
|
9327
9719
|
/** Strip line number formatting (cat -n or workspace →) and apply syntax highlighting */
|
|
9328
|
-
function highlightCode(content, path, startLine) {
|
|
9720
|
+
function highlightCode(content, path$19, startLine) {
|
|
9329
9721
|
const code = getPlainCodeFromViewOutput(content, startLine);
|
|
9330
9722
|
try {
|
|
9331
9723
|
return (0, cli_highlight.highlight)(code, {
|
|
9332
|
-
language: getLanguageFromPath(path),
|
|
9724
|
+
language: getLanguageFromPath(path$19),
|
|
9333
9725
|
ignoreIllegals: true,
|
|
9334
9726
|
theme: CODE_HIGHLIGHT_THEME
|
|
9335
9727
|
});
|
|
@@ -9718,6 +10110,7 @@ function createPostToolAssistantComponent(ctx, toolCallId) {
|
|
|
9718
10110
|
const messageId = state.streamingMessage?.id;
|
|
9719
10111
|
if (!messageId) {
|
|
9720
10112
|
const component = new AssistantMessageComponent(void 0, state.hideThinkingBlock, getMarkdownTheme());
|
|
10113
|
+
component.setQuietModeDisplay(state.quietMode ? "quiet" : "normal");
|
|
9721
10114
|
state.streamingComponent = component;
|
|
9722
10115
|
ctx.addChildBeforeFollowUps(component);
|
|
9723
10116
|
return component;
|
|
@@ -9924,7 +10317,9 @@ function ensureSubmitPlanComponent(ctx, toolCallId, args) {
|
|
|
9924
10317
|
* Extracts content from common tool return structures like { content: "...", isError: false }
|
|
9925
10318
|
*/
|
|
9926
10319
|
function isToolResultError(result) {
|
|
9927
|
-
|
|
10320
|
+
if (typeof result !== "object" || result === null) return false;
|
|
10321
|
+
const record = result;
|
|
10322
|
+
return record.isError === true || record.error === true;
|
|
9928
10323
|
}
|
|
9929
10324
|
function formatToolResult(result) {
|
|
9930
10325
|
if (result === null || result === void 0) return "";
|
|
@@ -9997,6 +10392,7 @@ function handleToolStart(ctx, toolCallId, toolName, args) {
|
|
|
9997
10392
|
return;
|
|
9998
10393
|
}
|
|
9999
10394
|
if (existingComponent) {
|
|
10395
|
+
existingComponent.setArgsStreaming?.(false);
|
|
10000
10396
|
existingComponent.updateArgs(args);
|
|
10001
10397
|
reconcileToolBoundaries(ctx);
|
|
10002
10398
|
} else if (existingSubmitPlanComponent) existingSubmitPlanComponent.updateArgs(args);
|
|
@@ -10025,7 +10421,8 @@ function handleToolStart(ctx, toolCallId, toolName, args) {
|
|
|
10025
10421
|
}
|
|
10026
10422
|
const component = new ToolExecutionComponentEnhanced(toolName, args, {
|
|
10027
10423
|
showImages: false,
|
|
10028
|
-
collapsedByDefault: !state.toolOutputExpanded
|
|
10424
|
+
collapsedByDefault: !state.toolOutputExpanded,
|
|
10425
|
+
projectRoot: state.projectInfo?.rootPath
|
|
10029
10426
|
}, state.ui);
|
|
10030
10427
|
component.setExpanded(state.toolOutputExpanded);
|
|
10031
10428
|
applyQuietDisplayForNewTool(ctx, component);
|
|
@@ -10085,6 +10482,19 @@ function handleShellOutput(ctx, toolCallId, output, _stream) {
|
|
|
10085
10482
|
});
|
|
10086
10483
|
}
|
|
10087
10484
|
/**
|
|
10485
|
+
* Handle the sandbox's exit record for an execute_command call. It decides pass/fail even when the
|
|
10486
|
+
* result text doesn't say, e.g. when the sandbox itself threw and the result is a bare `Error: …`.
|
|
10487
|
+
*/
|
|
10488
|
+
function handleCommandExit(ctx, toolCallId, exitCode, success) {
|
|
10489
|
+
const component = ctx.state.pendingTools.get(toolCallId);
|
|
10490
|
+
if (!component?.setCommandExit) return;
|
|
10491
|
+
component.setCommandExit({
|
|
10492
|
+
exitCode,
|
|
10493
|
+
success
|
|
10494
|
+
});
|
|
10495
|
+
requestRender(ctx.state);
|
|
10496
|
+
}
|
|
10497
|
+
/**
|
|
10088
10498
|
* Handle the start of streaming tool call input arguments.
|
|
10089
10499
|
* Creates the tool component early so partial args can render as they arrive.
|
|
10090
10500
|
*/
|
|
@@ -10126,10 +10536,12 @@ function handleToolInputStart(ctx, toolCallId, toolName) {
|
|
|
10126
10536
|
if (createStaticSubagentComponent(ctx, toolCallId, toolName, {})) return;
|
|
10127
10537
|
const component = new ToolExecutionComponentEnhanced(toolName, {}, {
|
|
10128
10538
|
showImages: false,
|
|
10129
|
-
collapsedByDefault: !state.toolOutputExpanded
|
|
10539
|
+
collapsedByDefault: !state.toolOutputExpanded,
|
|
10540
|
+
projectRoot: state.projectInfo?.rootPath
|
|
10130
10541
|
}, state.ui);
|
|
10131
10542
|
component.setExpanded(state.toolOutputExpanded);
|
|
10132
10543
|
applyQuietDisplayForNewTool(ctx, component);
|
|
10544
|
+
component.setArgsStreaming(true);
|
|
10133
10545
|
ctx.addChildBeforeFollowUps(component);
|
|
10134
10546
|
state.pendingTools.set(toolCallId, component);
|
|
10135
10547
|
state.allToolComponents.push(component);
|
|
@@ -10145,6 +10557,7 @@ function applyParsedToolArgs(ctx, toolCallId, toolName, partialArgs) {
|
|
|
10145
10557
|
const { state } = ctx;
|
|
10146
10558
|
const component = state.pendingTools.get(toolCallId);
|
|
10147
10559
|
if (component) {
|
|
10560
|
+
component.setArgsStreaming?.(true);
|
|
10148
10561
|
component.updateArgs(partialArgs, false);
|
|
10149
10562
|
reconcileToolBoundaries(ctx);
|
|
10150
10563
|
component.refresh?.();
|
|
@@ -10224,6 +10637,10 @@ function handleToolInputDelta(ctx, toolCallId, argsTextDelta) {
|
|
|
10224
10637
|
function handleToolInputEnd(ctx, toolCallId) {
|
|
10225
10638
|
flushLatestParsedToolArgs(ctx, toolCallId);
|
|
10226
10639
|
closeToolInputParser(toolCallId);
|
|
10640
|
+
const component = ctx.state.pendingTools.get(toolCallId);
|
|
10641
|
+
if (!component?.setArgsStreaming) return;
|
|
10642
|
+
component.setArgsStreaming(false);
|
|
10643
|
+
reconcileToolBoundaries(ctx);
|
|
10227
10644
|
}
|
|
10228
10645
|
function handleToolEnd(ctx, toolCallId, result, isError, providerMetadata) {
|
|
10229
10646
|
flushPendingShellOutput(ctx, toolCallId);
|
|
@@ -10958,6 +11375,7 @@ async function renderExistingMessages(state, isCurrent = () => true) {
|
|
|
10958
11375
|
state.messageComponentsById.clear();
|
|
10959
11376
|
state.pendingSignalMessageComponentsById.clear();
|
|
10960
11377
|
state.allShellComponents = [];
|
|
11378
|
+
const commandExits = collectCommandExits(messages);
|
|
10961
11379
|
const backgroundTasksByToolCallId = /* @__PURE__ */ new Map();
|
|
10962
11380
|
const cancelledBackgroundToolCalls = /* @__PURE__ */ new Set();
|
|
10963
11381
|
for (const message of messages) {
|
|
@@ -10976,6 +11394,7 @@ async function renderExistingMessages(state, isCurrent = () => true) {
|
|
|
10976
11394
|
const flushAccumulated = (isFinal = false) => {
|
|
10977
11395
|
if (accumulatedParts.length === 0 && !(isFinal && hasTerminalMetadata(message))) return;
|
|
10978
11396
|
const textComponent = new AssistantMessageComponent(buildAssistantSlice(message, accumulatedParts, { includeTerminalMetadata: isFinal }), state.hideThinkingBlock, getMarkdownTheme());
|
|
11397
|
+
textComponent.setQuietModeDisplay(state.quietMode ? "quiet" : "normal");
|
|
10979
11398
|
state.chatContainer.addChild(textComponent);
|
|
10980
11399
|
accumulatedParts = [];
|
|
10981
11400
|
};
|
|
@@ -11046,17 +11465,29 @@ async function renderExistingMessages(state, isCurrent = () => true) {
|
|
|
11046
11465
|
}
|
|
11047
11466
|
const toolComponent = new ToolExecutionComponentEnhanced(toolName, toolArgs, {
|
|
11048
11467
|
showImages: false,
|
|
11049
|
-
collapsedByDefault: !state.toolOutputExpanded
|
|
11468
|
+
collapsedByDefault: !state.toolOutputExpanded,
|
|
11469
|
+
projectRoot: state.projectInfo?.rootPath
|
|
11050
11470
|
}, state.ui);
|
|
11051
11471
|
const backgroundTaskId = (isBackgroundPlaceholder || part.backgroundTask?.status !== "running" ? part.backgroundTask?.taskId : void 0) ?? backgroundTasksByToolCallId.get(part.toolCallId);
|
|
11052
11472
|
if (backgroundTaskId) toolComponent.setBackgroundTaskId(backgroundTaskId);
|
|
11053
|
-
if (hasResult)
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11473
|
+
if (hasResult) {
|
|
11474
|
+
toolComponent.updateResult({
|
|
11475
|
+
content: [{
|
|
11476
|
+
type: "text",
|
|
11477
|
+
text: isBackgroundPlaceholder ? "Running in background…" : formatToolResult(resultValue)
|
|
11478
|
+
}],
|
|
11479
|
+
isError: resultIsError
|
|
11480
|
+
}, isBackgroundPlaceholder);
|
|
11481
|
+
if (!isBackgroundPlaceholder) {
|
|
11482
|
+
const exit = commandExits.get(part.toolCallId);
|
|
11483
|
+
if (exit) toolComponent.setCommandExit(exit);
|
|
11484
|
+
const runMs = exit?.executionTimeMs;
|
|
11485
|
+
if (runMs !== void 0) {
|
|
11486
|
+
const endedAt = part.endedAt ?? (part.startedAt ?? 0) + runMs;
|
|
11487
|
+
toolComponent.setRecordedTiming(endedAt - runMs, endedAt);
|
|
11488
|
+
} else toolComponent.setRecordedTiming(part.startedAt, part.endedAt);
|
|
11489
|
+
}
|
|
11490
|
+
} else toolComponent.stopLiveUpdates();
|
|
11060
11491
|
if (cancelledBackgroundToolCalls.has(part.toolCallId)) {
|
|
11061
11492
|
toolComponent.updateResult({
|
|
11062
11493
|
content: [{
|
|
@@ -12656,22 +13087,6 @@ function formatOMStatus(state) {
|
|
|
12656
13087
|
return chalk.default.hex(mastra.specialGray)("msg ") + value + buffered;
|
|
12657
13088
|
}
|
|
12658
13089
|
//#endregion
|
|
12659
|
-
//#region src/tui/status-duration.ts
|
|
12660
|
-
const MINUTE_MS$1 = 6e4;
|
|
12661
|
-
const HOUR_MS = 60 * MINUTE_MS$1;
|
|
12662
|
-
const DAY_MS = 24 * HOUR_MS;
|
|
12663
|
-
function formatStatusDuration(ms, opts = {}) {
|
|
12664
|
-
const safeMs = Math.max(0, ms);
|
|
12665
|
-
const days = Math.floor(safeMs / DAY_MS);
|
|
12666
|
-
const hours = Math.floor(safeMs % DAY_MS / HOUR_MS);
|
|
12667
|
-
const minutes = Math.floor(safeMs % HOUR_MS / MINUTE_MS$1);
|
|
12668
|
-
const seconds = Math.floor(safeMs % MINUTE_MS$1 / 1e3);
|
|
12669
|
-
if (days > 0) return `${days}d${hours > 0 ? `${hours}hr` : ""}${minutes > 0 ? `${minutes}m` : ""}`;
|
|
12670
|
-
if (hours > 0) return `${hours}hr${minutes > 0 ? `${minutes}m` : ""}`;
|
|
12671
|
-
if (opts.includeSeconds) return minutes > 0 ? `${minutes}m${seconds}s` : `${Math.max(1, seconds)}s`;
|
|
12672
|
-
return `${Math.max(1, minutes)}m`;
|
|
12673
|
-
}
|
|
12674
|
-
//#endregion
|
|
12675
13090
|
//#region src/tui/status-line.ts
|
|
12676
13091
|
/**
|
|
12677
13092
|
* Status line rendering — builds the bottom-of-screen status bar
|
|
@@ -15802,12 +16217,13 @@ var SettingsComponent = class extends _earendil_works_pi_tui.Box {
|
|
|
15802
16217
|
//#region src/tui/quiet-mode.ts
|
|
15803
16218
|
/**
|
|
15804
16219
|
* Pushes the current quiet-mode preference into every already-rendered component
|
|
15805
|
-
* that renders differently in quiet mode: tool executions,
|
|
15806
|
-
* notification summaries. Both the onboarding prompt and the
|
|
15807
|
-
* route through here so the two paths cannot drift.
|
|
16220
|
+
* that renders differently in quiet mode: tool executions, assistant messages,
|
|
16221
|
+
* notifications, and notification summaries. Both the onboarding prompt and the
|
|
16222
|
+
* `/settings` overlay route through here so the two paths cannot drift.
|
|
15808
16223
|
*/
|
|
15809
16224
|
function applyQuietModeToRenderedComponents(state, enabled, previewLineLimit, modeColor) {
|
|
15810
16225
|
const mode = enabled ? "quiet" : "normal";
|
|
16226
|
+
if (!enabled) state.idleCounter?.setThinking(false);
|
|
15811
16227
|
const tools = state.allToolComponents.filter((tool) => typeof tool.setQuietModeDisplay === "function");
|
|
15812
16228
|
for (const tool of tools) {
|
|
15813
16229
|
tool.setCompactToolModeColor?.(modeColor);
|
|
@@ -15818,6 +16234,9 @@ function applyQuietModeToRenderedComponents(state, enabled, previewLineLimit, mo
|
|
|
15818
16234
|
component.setQuietModeDisplay(mode);
|
|
15819
16235
|
component.setQuietPreviewLineLimit(previewLineLimit);
|
|
15820
16236
|
} else if (component instanceof NotificationSummaryComponent) component.setQuietModeDisplay(mode);
|
|
16237
|
+
if (!state.chatContainer) return;
|
|
16238
|
+
for (const component of state.chatContainer.children) if (component instanceof AssistantMessageComponent) component.setQuietModeDisplay(mode);
|
|
16239
|
+
reconcileChatBoundarySpacers(state.chatContainer);
|
|
15821
16240
|
}
|
|
15822
16241
|
//#endregion
|
|
15823
16242
|
//#region src/tui/commands/api-keys.ts
|
|
@@ -20434,6 +20853,8 @@ function handleAgentEnd(ctx) {
|
|
|
20434
20853
|
if (state.streamingComponent) finalizeStreamingAssistant(state);
|
|
20435
20854
|
state.activeGoalJudge = void 0;
|
|
20436
20855
|
state.followUpComponents = [];
|
|
20856
|
+
for (const tool of state.pendingTools.values()) tool.stopLiveUpdates?.();
|
|
20857
|
+
state.idleCounter?.setThinking(false);
|
|
20437
20858
|
state.pendingTools.clear();
|
|
20438
20859
|
state.pendingTaskToolIds?.clear();
|
|
20439
20860
|
pruneChatContainer(state);
|
|
@@ -20514,6 +20935,8 @@ function handleAgentAborted(ctx) {
|
|
|
20514
20935
|
state.pendingSlashCommands = [];
|
|
20515
20936
|
state.pendingSlashCommandMessageIds = [];
|
|
20516
20937
|
clearPendingUserMessages(state);
|
|
20938
|
+
for (const tool of state.pendingTools.values()) tool.stopLiveUpdates?.();
|
|
20939
|
+
state.idleCounter?.setThinking(false);
|
|
20517
20940
|
state.pendingTools.clear();
|
|
20518
20941
|
state.pendingTaskToolIds?.clear();
|
|
20519
20942
|
pruneChatContainer(state);
|
|
@@ -20534,6 +20957,8 @@ function handleAgentError(ctx) {
|
|
|
20534
20957
|
state.pendingSlashCommands = [];
|
|
20535
20958
|
state.pendingSlashCommandMessageIds = [];
|
|
20536
20959
|
clearPendingUserMessages(state);
|
|
20960
|
+
for (const tool of state.pendingTools.values()) tool.stopLiveUpdates?.();
|
|
20961
|
+
state.idleCounter?.setThinking(false);
|
|
20537
20962
|
state.pendingTools.clear();
|
|
20538
20963
|
state.pendingTaskToolIds?.clear();
|
|
20539
20964
|
pruneChatContainer(state);
|
|
@@ -20618,6 +21043,16 @@ function getContent(message) {
|
|
|
20618
21043
|
function getRawParts(message) {
|
|
20619
21044
|
return getContent(message)?.parts ?? [];
|
|
20620
21045
|
}
|
|
21046
|
+
/**
|
|
21047
|
+
* Quiet mode keeps "Thinking..." out of the chat and shows it in the status line above the input
|
|
21048
|
+
* while the streaming message's latest content is reasoning.
|
|
21049
|
+
*/
|
|
21050
|
+
function syncQuietThinkingStatus(ctx, message) {
|
|
21051
|
+
const { state } = ctx;
|
|
21052
|
+
const latest = message ? getRawParts(message).findLast((part) => part.type === "text" || part.type === "reasoning" || part.type === "tool-invocation") : void 0;
|
|
21053
|
+
const thinking = state.quietMode && state.hideThinkingBlock && latest?.type === "reasoning";
|
|
21054
|
+
state.idleCounter?.setThinking(thinking);
|
|
21055
|
+
}
|
|
20621
21056
|
function isToolPart(part) {
|
|
20622
21057
|
return part.type === "tool-invocation";
|
|
20623
21058
|
}
|
|
@@ -20697,6 +21132,7 @@ function handleMessageStart(ctx, message) {
|
|
|
20697
21132
|
return;
|
|
20698
21133
|
}
|
|
20699
21134
|
if (message.role === "assistant") {
|
|
21135
|
+
syncQuietThinkingStatus(ctx, message);
|
|
20700
21136
|
state.lastAskUserComponent = void 0;
|
|
20701
21137
|
state.lastSubmitPlanComponent = void 0;
|
|
20702
21138
|
state.streamingMessage = message;
|
|
@@ -20717,6 +21153,7 @@ function handleMessageUpdate(ctx, message) {
|
|
|
20717
21153
|
return;
|
|
20718
21154
|
}
|
|
20719
21155
|
if (message.role !== "assistant") return;
|
|
21156
|
+
syncQuietThinkingStatus(ctx, message);
|
|
20720
21157
|
const toolParts = getAssistantRenderParts(message).filter((part) => part.kind === "tool");
|
|
20721
21158
|
const trailingParts = getTrailingParts(message);
|
|
20722
21159
|
const hasToolCalls = toolParts.length > 0;
|
|
@@ -20744,7 +21181,8 @@ function handleMessageUpdate(ctx, message) {
|
|
|
20744
21181
|
}
|
|
20745
21182
|
const component = new ToolExecutionComponentEnhanced(tool.toolName, tool.args, {
|
|
20746
21183
|
showImages: false,
|
|
20747
|
-
collapsedByDefault: !state.toolOutputExpanded
|
|
21184
|
+
collapsedByDefault: !state.toolOutputExpanded,
|
|
21185
|
+
projectRoot: state.projectInfo?.rootPath
|
|
20748
21186
|
}, state.ui);
|
|
20749
21187
|
component.setExpanded(state.toolOutputExpanded);
|
|
20750
21188
|
if (state.quietMode) {
|
|
@@ -20772,6 +21210,7 @@ function handleMessageUpdate(ctx, message) {
|
|
|
20772
21210
|
function handleMessageEnd(ctx, message) {
|
|
20773
21211
|
const { state } = ctx;
|
|
20774
21212
|
if (message.role === "signal" || message.role === "user") return;
|
|
21213
|
+
syncQuietThinkingStatus(ctx);
|
|
20775
21214
|
if (state.streamingComponent && message.role === "assistant") {
|
|
20776
21215
|
state.streamingMessage = message;
|
|
20777
21216
|
const trailingParts = getTrailingParts(message);
|
|
@@ -23946,6 +24385,9 @@ async function dispatchEvent(event, ectx, state) {
|
|
|
23946
24385
|
state.agentRunLastStreamPartAt = Date.now();
|
|
23947
24386
|
handleShellOutput(ectx, event.toolCallId, event.output, event.stream);
|
|
23948
24387
|
break;
|
|
24388
|
+
case "command_exit":
|
|
24389
|
+
handleCommandExit(ectx, event.toolCallId, event.exitCode, event.success);
|
|
24390
|
+
break;
|
|
23949
24391
|
case "tool_input_start":
|
|
23950
24392
|
if (event.toolName === "ask_user" || event.toolName === "request_access" || event.toolName === "submit_plan") trackInteractivePrompt(ectx, event.toolName, {
|
|
23951
24393
|
toolName: event.toolName,
|
|
@@ -24729,6 +25171,7 @@ const MINUTE_MS = 6e4;
|
|
|
24729
25171
|
var IdleCounterComponent = class extends _earendil_works_pi_tui.Container {
|
|
24730
25172
|
timingState;
|
|
24731
25173
|
textChild;
|
|
25174
|
+
thinking = false;
|
|
24732
25175
|
constructor() {
|
|
24733
25176
|
super();
|
|
24734
25177
|
this.textChild = new _earendil_works_pi_tui.Text("", 0, 0);
|
|
@@ -24738,7 +25181,17 @@ var IdleCounterComponent = class extends _earendil_works_pi_tui.Container {
|
|
|
24738
25181
|
this.timingState = timingState;
|
|
24739
25182
|
this.update(now);
|
|
24740
25183
|
}
|
|
25184
|
+
/** Quiet mode shows the live "Thinking..." indicator here instead of in the chat. */
|
|
25185
|
+
setThinking(thinking) {
|
|
25186
|
+
if (this.thinking === thinking) return;
|
|
25187
|
+
this.thinking = thinking;
|
|
25188
|
+
this.update();
|
|
25189
|
+
}
|
|
24741
25190
|
update(now = Date.now()) {
|
|
25191
|
+
if (this.thinking) {
|
|
25192
|
+
this.textChild.setText(` ${theme.italic(theme.fg("thinkingText", "Thinking..."))}`);
|
|
25193
|
+
return;
|
|
25194
|
+
}
|
|
24742
25195
|
const segments = this.timingState ? formatIdleStatusTimingSegments(this.timingState, now) : null;
|
|
24743
25196
|
if (!segments) {
|
|
24744
25197
|
this.textChild.setText("");
|
|
@@ -24956,6 +25409,7 @@ function setupKeyboardShortcuts(state, callbacks) {
|
|
|
24956
25409
|
for (const sc of state.allSlashCommandComponents) sc.setExpanded(state.toolOutputExpanded);
|
|
24957
25410
|
for (const reminder of state.allSystemReminderComponents) reminder.setExpanded(state.toolOutputExpanded);
|
|
24958
25411
|
for (const shell of state.allShellComponents) shell.setExpanded(state.toolOutputExpanded);
|
|
25412
|
+
reconcileChatBoundarySpacers(state.chatContainer);
|
|
24959
25413
|
state.ui.requestRender();
|
|
24960
25414
|
});
|
|
24961
25415
|
if (callbacks.openBackgroundActivityCenter) state.editor.onAction("openBackgroundActivityCenter", callbacks.openBackgroundActivityCenter);
|
|
@@ -27214,4 +27668,4 @@ Object.defineProperty(exports, "theme", {
|
|
|
27214
27668
|
}
|
|
27215
27669
|
});
|
|
27216
27670
|
|
|
27217
|
-
//# sourceMappingURL=tui-
|
|
27671
|
+
//# sourceMappingURL=tui-DrOMn6H1.cjs.map
|