@garuda-agent/garuda 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/README.md +3 -2
- package/dist/{acpCommand-IUZPLPLI.js → acpCommand-JAYNMIMP.js} +17 -14
- package/dist/advise-HDKJGNBL.js +195 -0
- package/dist/brief-RWKESOPT.js +91 -0
- package/dist/buildCheck-HB46NWLX.js +19 -0
- package/dist/buildInfo-K73FFI5S.js +8 -0
- package/dist/chunk-2OYTNNUJ.js +216 -0
- package/dist/{chunk-OQ26W5L4.js → chunk-34WBDQBL.js} +134 -77
- package/dist/chunk-4QRTTRVK.js +88 -0
- package/dist/{chunk-NHWHG3SW.js → chunk-5UDYQ5MK.js} +354 -59
- package/dist/chunk-CWVXOSAA.js +894 -0
- package/dist/{chunk-FARS4R67.js → chunk-DQSCCE77.js} +18 -1
- package/dist/chunk-DUYLVGPY.js +821 -0
- package/dist/{chunk-Q4P3W657.js → chunk-GKU62MHQ.js} +10 -1124
- package/dist/chunk-H7LIBGZT.js +9 -0
- package/dist/{chunk-FOPBOB7W.js → chunk-IKPGHM54.js} +6 -4
- package/dist/chunk-IYGJWX65.js +9 -0
- package/dist/{chunk-65NUEOI7.js → chunk-JMH5W5DG.js} +1 -44
- package/dist/chunk-L55VUSX3.js +54 -0
- package/dist/{chunk-B4SJY7KT.js → chunk-LMKGSYJC.js} +77 -8
- package/dist/{chunk-23FXBNBB.js → chunk-MNE7HHX2.js} +2 -1
- package/dist/chunk-NH72AELP.js +48 -0
- package/dist/{chunk-UKIE4AAM.js → chunk-NLS7GMF7.js} +11 -105
- package/dist/{chunk-2FIV2VZP.js → chunk-QLBCM2YQ.js} +2 -2
- package/dist/{chunk-6XHON3Q6.js → chunk-QXTHEFKP.js} +3 -3
- package/dist/{chunk-4BVCYCQT.js → chunk-R7RCW4BQ.js} +9 -7
- package/dist/chunk-TLA2PDGM.js +156 -0
- package/dist/{chunk-5B5HDTUR.js → chunk-TWQ7V2L6.js} +2 -2
- package/dist/chunk-ULAJPMY7.js +107 -0
- package/dist/chunk-VISKX36R.js +306 -0
- package/dist/{chunk-55CFEEIN.js → chunk-WK5NE6RY.js} +2 -2
- package/dist/chunk-Y62J7PTD.js +90 -0
- package/dist/chunk-YAQ6QLKB.js +40 -0
- package/dist/{chunk-7TH3NHCQ.js → chunk-ZKT4P2PC.js} +2 -2
- package/dist/cli/index.js +252 -61
- package/dist/{create-W6BV75QX.js → create-OACSGJ37.js} +5 -5
- package/dist/{evalCommand-SKJQ6CW2.js → evalCommand-BLJJ4G76.js} +43 -33
- package/dist/{evalTune-CZRCPSZ3.js → evalTune-W53KAD4V.js} +25 -18
- package/dist/{export-SJ3DUP6W.js → export-E677HIQS.js} +27 -4
- package/dist/exportHtml-BMV73YRO.js +21 -0
- package/dist/{inkChat-WYYAFZXD.js → inkChat-EVLYDC3K.js} +258 -42
- package/dist/{install-TKWN5AYH.js → install-4ST5BNRG.js} +3 -3
- package/dist/{jobCommand-2ODQLHB3.js → jobCommand-ILOTYOX3.js} +8 -7
- package/dist/{launchd-3A3ONEW5.js → launchd-QG6R5RAK.js} +4 -4
- package/dist/{lspCommand-ZZ6R4PA2.js → lspCommand-X4FTTONR.js} +3 -3
- package/dist/{manager-TCYTA5XU.js → manager-L25IUXHW.js} +3 -3
- package/dist/{nightCommand-WJU6VZA6.js → nightCommand-XPWT6CQZ.js} +8 -7
- package/dist/receiptView-YFDHV253.js +19 -0
- package/dist/{remove-42WQNVPX.js → remove-STX5UO7X.js} +3 -3
- package/dist/{run-PTMRQCIU.js → run-W4TK52WT.js} +5 -5
- package/dist/{server-VEYIJHEU.js → server-ASM73QVY.js} +3 -3
- package/dist/{setupCommand-YMF6I3MD.js → setupCommand-OSOMFHDQ.js} +9 -8
- package/dist/tour-CEVMAC5S.js +18 -0
- package/dist/tune-VX2WQTHS.js +52 -0
- package/dist/uiState-YFJQ43D7.js +16 -0
- package/package.json +1 -1
- package/dist/chunk-4JSUUDFQ.js +0 -118
- package/dist/chunk-C57GHLKP.js +0 -9
- package/dist/chunk-DOCDCQVM.js +0 -484
- package/dist/tune-YVI2KWQF.js +0 -31
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
budgetSpent,
|
|
4
|
+
isCostIncomplete,
|
|
5
|
+
pendingChildTokens
|
|
6
|
+
} from "./chunk-NLS7GMF7.js";
|
|
7
|
+
import {
|
|
8
|
+
totalTokens
|
|
9
|
+
} from "./chunk-7KBOIPUT.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/report.ts
|
|
12
|
+
function formatTokens(n) {
|
|
13
|
+
if (n < 1e3) return String(n);
|
|
14
|
+
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
15
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
16
|
+
}
|
|
17
|
+
function formatUsd(usd, incomplete = false) {
|
|
18
|
+
if (usd === void 0) return "cost unknown";
|
|
19
|
+
const amount = usd < 0.01 ? `$${usd.toFixed(4)}` : `$${usd.toFixed(2)}`;
|
|
20
|
+
return incomplete ? `\u2265 ${amount} (incomplete)` : amount;
|
|
21
|
+
}
|
|
22
|
+
function budgetNote(session) {
|
|
23
|
+
const unreported = session.unreportedTokens;
|
|
24
|
+
const children = pendingChildTokens(session);
|
|
25
|
+
if (unreported <= 0 && children <= 0) return void 0;
|
|
26
|
+
const parts = [
|
|
27
|
+
...unreported > 0 ? [`${formatTokens(unreported)} reserved without reported usage`] : [],
|
|
28
|
+
...children > 0 ? [`${formatTokens(children)} from subagents with no report`] : []
|
|
29
|
+
];
|
|
30
|
+
return `budget ${formatTokens(budgetSpent(session))} tokens (${parts.join(", ")})`;
|
|
31
|
+
}
|
|
32
|
+
function usageLine(result, session, runCostUsd, contextWindow, runCostIncomplete = false) {
|
|
33
|
+
const u = result.usage;
|
|
34
|
+
const input = u.inputTokens + u.cacheReadTokens + u.cacheWriteTokens;
|
|
35
|
+
const percent = Math.round(session.contextTokens / contextWindow * 100);
|
|
36
|
+
return [
|
|
37
|
+
`${result.stopReason} \xB7 ${result.steps} step(s)`,
|
|
38
|
+
`${budgetNote(session) !== void 0 ? "reported " : ""}${formatTokens(input)} in (${formatTokens(u.cacheReadTokens)} cached) / ${formatTokens(u.outputTokens)} out`,
|
|
39
|
+
formatUsd(runCostUsd, runCostIncomplete),
|
|
40
|
+
`context ${percent}% of ${formatTokens(contextWindow)}`,
|
|
41
|
+
`session ${formatTokens(totalTokens(session.usage))} tokens, ${formatUsd(session.costUsd, isCostIncomplete(session))}`,
|
|
42
|
+
...budgetNote(session) === void 0 ? [] : [budgetNote(session)]
|
|
43
|
+
].join(" \xB7 ");
|
|
44
|
+
}
|
|
45
|
+
function stopMessage(reason, limits) {
|
|
46
|
+
switch (reason) {
|
|
47
|
+
case "done":
|
|
48
|
+
return void 0;
|
|
49
|
+
case "max_steps":
|
|
50
|
+
return `Stopped: the run reached the step limit (${limits.maxSteps}). Use --resume to continue, or raise limits.maxSteps in .garuda/settings.json.`;
|
|
51
|
+
case "token_budget":
|
|
52
|
+
return `Stopped: the session used its token budget (${formatTokens(limits.tokenBudget)} tokens). Raise limits.tokenBudget in .garuda/settings.json to continue.`;
|
|
53
|
+
case "repeated_calls":
|
|
54
|
+
return "Stopped: the agent made the same tool call 3 times in a row, so it may be stuck. Use --resume with a hint to continue.";
|
|
55
|
+
case "max_tokens":
|
|
56
|
+
return "Stopped: the response reached the output token limit.";
|
|
57
|
+
case "refusal":
|
|
58
|
+
return "Stopped: the model refused the request.";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function chatStopMessage(reason, limits) {
|
|
62
|
+
switch (reason) {
|
|
63
|
+
case "max_steps":
|
|
64
|
+
return `\u25A0 Stopped at the step limit (${limits.maxSteps}). The task is not finished.`;
|
|
65
|
+
case "repeated_calls":
|
|
66
|
+
return "\u25A0 Stopped: the agent made the same tool call 3 times in a row, so it may be stuck. Type a hint as your next message, and it goes on from there.";
|
|
67
|
+
default:
|
|
68
|
+
return stopMessage(reason, limits);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/cli/receiptView.ts
|
|
73
|
+
function screenReaderMode(env = process.env) {
|
|
74
|
+
return env.GARUDA_SCREEN_READER === "1";
|
|
75
|
+
}
|
|
76
|
+
var RECEIPT_BOX_MIN_COLUMNS = 60;
|
|
77
|
+
var RECEIPT_MAX_WIDTH = 76;
|
|
78
|
+
var seconds = (ms) => `${Math.max(0, Math.round(ms / 1e3))} s`;
|
|
79
|
+
var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
80
|
+
function cells(text) {
|
|
81
|
+
let n = 0;
|
|
82
|
+
for (const ch of text) {
|
|
83
|
+
const c = ch.codePointAt(0) ?? 0;
|
|
84
|
+
const wide = c >= 4352 && c <= 4447 || c >= 11904 && c <= 42191 || c >= 44032 && c <= 55203 || c >= 63744 && c <= 64255 || c >= 65072 && c <= 65103 || c >= 65280 && c <= 65376 || c >= 65504 && c <= 65510 || c >= 127744 && c <= 129791 || c >= 131072 && c <= 262141;
|
|
85
|
+
n += wide ? 2 : 1;
|
|
86
|
+
}
|
|
87
|
+
return n;
|
|
88
|
+
}
|
|
89
|
+
function cut(text, width) {
|
|
90
|
+
if (cells(text) <= width) return text;
|
|
91
|
+
let out = "";
|
|
92
|
+
for (const ch of text) {
|
|
93
|
+
if (cells(out + ch) > width - 1) break;
|
|
94
|
+
out += ch;
|
|
95
|
+
}
|
|
96
|
+
return `${out}\u2026`;
|
|
97
|
+
}
|
|
98
|
+
function filesParts(receipt, room) {
|
|
99
|
+
const files = receipt.files;
|
|
100
|
+
if (files === void 0) return [["undo is off: no file list", "dim"]];
|
|
101
|
+
if (files.length === 0) return [["no file changed", "dim"]];
|
|
102
|
+
const shown = [];
|
|
103
|
+
for (const [i, f] of files.entries()) {
|
|
104
|
+
const lines = f.added === void 0 ? " (binary)" : ` +${f.added} \u2212${f.removed ?? 0}`;
|
|
105
|
+
const item = `${f.path}${lines}`;
|
|
106
|
+
const rest = files.length - i - 1;
|
|
107
|
+
const tail = rest > 0 ? ` \xB7 ${rest} more` : "";
|
|
108
|
+
const next = [...shown, item].join(" \xB7 ");
|
|
109
|
+
if (shown.length > 0 && cells(next + tail) > room) {
|
|
110
|
+
shown.push(`${files.length - i} more`);
|
|
111
|
+
return [[shown.join(" \xB7 ")]];
|
|
112
|
+
}
|
|
113
|
+
shown.push(item);
|
|
114
|
+
}
|
|
115
|
+
return [[shown.join(" \xB7 ")]];
|
|
116
|
+
}
|
|
117
|
+
function testsParts(receipt) {
|
|
118
|
+
const t = receipt.tests;
|
|
119
|
+
if (t === void 0) return void 0;
|
|
120
|
+
const count = t.total === void 0 ? "" : t.passed ? ` (${plural(t.total, "test")}${t.previousTotal !== void 0 && t.previousTotal !== t.total ? `, was ${t.previousTotal}` : ""})` : (t.failed ?? 0) > 0 ? ` (${t.failed} of ${t.total})` : ` (${plural(t.total, "test")})`;
|
|
121
|
+
return t.passed ? [[`${t.command} `], ["\u2713 passed", "green"], [count]] : [[`${t.command} `], [t.timedOut === true ? "\u2717 timed out" : "\u2717 failed", "red"], [count]];
|
|
122
|
+
}
|
|
123
|
+
function sandboxParts(receipt) {
|
|
124
|
+
const c = receipt.commands;
|
|
125
|
+
if (c.sandboxed + c.outside === 0) return void 0;
|
|
126
|
+
const parts = [
|
|
127
|
+
[`\u26E8 ${plural(c.sandboxed, "command")} in the sandbox \xB7 `],
|
|
128
|
+
c.outside > 0 ? [`${c.outside} outside`, "yellow"] : [`${c.outside} outside`]
|
|
129
|
+
];
|
|
130
|
+
return parts;
|
|
131
|
+
}
|
|
132
|
+
function rows(receipt, room) {
|
|
133
|
+
const out = [{ label: "Files", parts: filesParts(receipt, room) }];
|
|
134
|
+
const tests = testsParts(receipt);
|
|
135
|
+
if (tests !== void 0) out.push({ label: "Tests", parts: tests });
|
|
136
|
+
const sandbox = sandboxParts(receipt);
|
|
137
|
+
if (sandbox !== void 0) out.push({ label: "Sandbox", parts: sandbox });
|
|
138
|
+
const hosts = receipt.commands.blockedHosts;
|
|
139
|
+
if (hosts.length > 0) {
|
|
140
|
+
out.push({ label: "Network", parts: [[`blocked ${hosts.join(", ")}`, "yellow"]] });
|
|
141
|
+
}
|
|
142
|
+
const changed = receipt.files?.length ?? 0;
|
|
143
|
+
if (changed > 0)
|
|
144
|
+
out.push({ label: "Undo", parts: [[`/undo puts back ${plural(changed, "file")}`]] });
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
function headText(receipt) {
|
|
148
|
+
return `Turn ${receipt.turn} \xB7 ${receipt.stopReason} \xB7 ${plural(receipt.steps, "step")} \xB7 ${seconds(receipt.durationMs)}`;
|
|
149
|
+
}
|
|
150
|
+
function footText(receipt) {
|
|
151
|
+
const parts = [`context ${receipt.contextPercent}%`];
|
|
152
|
+
if (receipt.sessionCostUsd !== void 0) {
|
|
153
|
+
parts.push(
|
|
154
|
+
`session ${formatUsd(receipt.sessionCostUsd, receipt.sessionCostIncomplete === true)}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const kept = receipt.keptChanges ?? 0;
|
|
158
|
+
if (kept > 0) {
|
|
159
|
+
parts.push(plural(kept, "kept change"));
|
|
160
|
+
if (receipt.sessionCostUsd !== void 0) {
|
|
161
|
+
const each = formatUsd(receipt.sessionCostUsd / kept, receipt.sessionCostIncomplete === true);
|
|
162
|
+
parts.push(`${each} each`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return parts.join(" \xB7 ");
|
|
166
|
+
}
|
|
167
|
+
function receiptText(receipt, options = {}) {
|
|
168
|
+
const paint = options.paint ?? ((_style, text) => text);
|
|
169
|
+
const draw = (parts) => parts.map(([text, style]) => style === void 0 ? text : paint(style, text)).join("");
|
|
170
|
+
const cost = formatUsd(receipt.costUsd, receipt.costIncomplete === true);
|
|
171
|
+
const columns = options.columns;
|
|
172
|
+
if (options.screenReader === true || columns === void 0 || columns < RECEIPT_BOX_MIN_COLUMNS) {
|
|
173
|
+
const lines = [`${headText(receipt)} \xB7 ${cost}`];
|
|
174
|
+
for (const row of rows(receipt, 200)) lines.push(`${row.label}: ${draw(row.parts)}`);
|
|
175
|
+
lines.push(footText(receipt));
|
|
176
|
+
return lines.join("\n");
|
|
177
|
+
}
|
|
178
|
+
const width = Math.min(columns, RECEIPT_MAX_WIDTH);
|
|
179
|
+
const label = 9;
|
|
180
|
+
const room = width - 4 - label;
|
|
181
|
+
const border = (text) => paint("yellow", text);
|
|
182
|
+
const edge = (left, text, right, end) => {
|
|
183
|
+
const tail = right === "" ? "" : ` ${right} \u2500`;
|
|
184
|
+
const head = cut(text, width - 4 - cells(tail) - cells(end) - 1);
|
|
185
|
+
const fill = width - 3 - cells(head) - 1 - cells(tail) - cells(end);
|
|
186
|
+
return `${border(`${left}\u2500 `)}${head} ${border("\u2500".repeat(Math.max(1, fill)))}${right === "" ? "" : ` ${right} ${border("\u2500")}`}${border(end)}`;
|
|
187
|
+
};
|
|
188
|
+
const out = [edge("\u256D", headText(receipt), cost, "\u256E")];
|
|
189
|
+
for (const row of rows(receipt, room)) {
|
|
190
|
+
const plain = row.parts.map(([text]) => text).join("");
|
|
191
|
+
let parts = row.parts;
|
|
192
|
+
if (cells(plain) > room) parts = [[cut(plain, room)]];
|
|
193
|
+
const shown = parts.map(([text]) => text).join("");
|
|
194
|
+
const pad = " ".repeat(Math.max(0, room - cells(shown)));
|
|
195
|
+
out.push(
|
|
196
|
+
`${border("\u2502")} ${paint("dim", row.label.padEnd(label))}${draw(parts)}${pad} ${border("\u2502")}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
out.push(edge("\u2570", footText(receipt), "", "\u256F"));
|
|
200
|
+
return out.join("\n");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {
|
|
204
|
+
formatTokens,
|
|
205
|
+
formatUsd,
|
|
206
|
+
budgetNote,
|
|
207
|
+
usageLine,
|
|
208
|
+
stopMessage,
|
|
209
|
+
chatStopMessage,
|
|
210
|
+
screenReaderMode,
|
|
211
|
+
RECEIPT_BOX_MIN_COLUMNS,
|
|
212
|
+
RECEIPT_MAX_WIDTH,
|
|
213
|
+
cells,
|
|
214
|
+
receiptText
|
|
215
|
+
};
|
|
216
|
+
//# sourceMappingURL=chunk-2OYTNNUJ.js.map
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
colorDiff
|
|
4
|
-
} from "./chunk-4JSUUDFQ.js";
|
|
5
2
|
import {
|
|
6
3
|
AuditDirError,
|
|
7
4
|
SPECIALIST_SPECS,
|
|
@@ -18,19 +15,35 @@ import {
|
|
|
18
15
|
repoMapText,
|
|
19
16
|
repoMapTool,
|
|
20
17
|
verifyAuditDir
|
|
21
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5UDYQ5MK.js";
|
|
19
|
+
import {
|
|
20
|
+
colorDiff
|
|
21
|
+
} from "./chunk-VISKX36R.js";
|
|
22
|
+
import {
|
|
23
|
+
hasReceipt
|
|
24
|
+
} from "./chunk-4QRTTRVK.js";
|
|
25
|
+
import {
|
|
26
|
+
budgetNote,
|
|
27
|
+
chatStopMessage,
|
|
28
|
+
formatTokens,
|
|
29
|
+
formatUsd,
|
|
30
|
+
stopMessage,
|
|
31
|
+
usageLine
|
|
32
|
+
} from "./chunk-2OYTNNUJ.js";
|
|
22
33
|
import {
|
|
23
|
-
budgetSpent,
|
|
24
34
|
isCostIncomplete,
|
|
25
35
|
pendingChildTokens
|
|
26
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-NLS7GMF7.js";
|
|
27
37
|
import {
|
|
28
38
|
WEB_SEARCH_USD,
|
|
29
39
|
totalTokens
|
|
30
40
|
} from "./chunk-7KBOIPUT.js";
|
|
41
|
+
import {
|
|
42
|
+
tourChoice
|
|
43
|
+
} from "./chunk-Y62J7PTD.js";
|
|
31
44
|
import {
|
|
32
45
|
BUILTIN_COMMANDS
|
|
33
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-MNE7HHX2.js";
|
|
34
47
|
import {
|
|
35
48
|
LSP_LANGUAGES
|
|
36
49
|
} from "./chunk-EU7PA45O.js";
|
|
@@ -76,70 +89,19 @@ function focusEvent(input) {
|
|
|
76
89
|
return void 0;
|
|
77
90
|
}
|
|
78
91
|
|
|
79
|
-
// src/cli/report.ts
|
|
80
|
-
function formatTokens(n) {
|
|
81
|
-
if (n < 1e3) return String(n);
|
|
82
|
-
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
83
|
-
return `${(n / 1e6).toFixed(1)}M`;
|
|
84
|
-
}
|
|
85
|
-
function formatUsd(usd, incomplete = false) {
|
|
86
|
-
if (usd === void 0) return "cost unknown";
|
|
87
|
-
const amount = usd < 0.01 ? `$${usd.toFixed(4)}` : `$${usd.toFixed(2)}`;
|
|
88
|
-
return incomplete ? `\u2265 ${amount} (incomplete)` : amount;
|
|
89
|
-
}
|
|
90
|
-
function budgetNote(session) {
|
|
91
|
-
const unreported = session.unreportedTokens;
|
|
92
|
-
const children = pendingChildTokens(session);
|
|
93
|
-
if (unreported <= 0 && children <= 0) return void 0;
|
|
94
|
-
const parts = [
|
|
95
|
-
...unreported > 0 ? [`${formatTokens(unreported)} reserved without reported usage`] : [],
|
|
96
|
-
...children > 0 ? [`${formatTokens(children)} from subagents with no report`] : []
|
|
97
|
-
];
|
|
98
|
-
return `budget ${formatTokens(budgetSpent(session))} tokens (${parts.join(", ")})`;
|
|
99
|
-
}
|
|
100
|
-
function usageLine(result, session, runCostUsd, contextWindow, runCostIncomplete = false) {
|
|
101
|
-
const u = result.usage;
|
|
102
|
-
const input = u.inputTokens + u.cacheReadTokens + u.cacheWriteTokens;
|
|
103
|
-
const percent = Math.round(session.contextTokens / contextWindow * 100);
|
|
104
|
-
return [
|
|
105
|
-
`${result.stopReason} \xB7 ${result.steps} step(s)`,
|
|
106
|
-
`${budgetNote(session) !== void 0 ? "reported " : ""}${formatTokens(input)} in (${formatTokens(u.cacheReadTokens)} cached) / ${formatTokens(u.outputTokens)} out`,
|
|
107
|
-
formatUsd(runCostUsd, runCostIncomplete),
|
|
108
|
-
`context ${percent}% of ${formatTokens(contextWindow)}`,
|
|
109
|
-
`session ${formatTokens(totalTokens(session.usage))} tokens, ${formatUsd(session.costUsd, isCostIncomplete(session))}`,
|
|
110
|
-
...budgetNote(session) === void 0 ? [] : [budgetNote(session)]
|
|
111
|
-
].join(" \xB7 ");
|
|
112
|
-
}
|
|
113
|
-
function stopMessage(reason, limits) {
|
|
114
|
-
switch (reason) {
|
|
115
|
-
case "done":
|
|
116
|
-
return void 0;
|
|
117
|
-
case "max_steps":
|
|
118
|
-
return `Stopped: the run reached the step limit (${limits.maxSteps}). Use --resume to continue, or raise limits.maxSteps in .garuda/settings.json.`;
|
|
119
|
-
case "token_budget":
|
|
120
|
-
return `Stopped: the session used its token budget (${formatTokens(limits.tokenBudget)} tokens). Raise limits.tokenBudget in .garuda/settings.json to continue.`;
|
|
121
|
-
case "repeated_calls":
|
|
122
|
-
return "Stopped: the agent made the same tool call 3 times in a row, so it may be stuck. Use --resume with a hint to continue.";
|
|
123
|
-
case "max_tokens":
|
|
124
|
-
return "Stopped: the response reached the output token limit.";
|
|
125
|
-
case "refusal":
|
|
126
|
-
return "Stopped: the model refused the request.";
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
92
|
// src/cli/turn.ts
|
|
131
|
-
async function runTurnInTerminal(runtime, approver, renderer, prompt, exitNow, watcher) {
|
|
93
|
+
async function runTurnInTerminal(runtime, approver, renderer, prompt, exitNow, watcher, options = {}) {
|
|
132
94
|
const started = performance.now();
|
|
133
95
|
watcher?.turnStarted();
|
|
134
96
|
let outcome;
|
|
135
97
|
try {
|
|
136
|
-
outcome = await runTurn(runtime, approver, renderer, prompt, exitNow);
|
|
98
|
+
outcome = await runTurn(runtime, approver, renderer, prompt, exitNow, options);
|
|
137
99
|
return outcome;
|
|
138
100
|
} finally {
|
|
139
101
|
watcher?.turnEnded(outcome, performance.now() - started);
|
|
140
102
|
}
|
|
141
103
|
}
|
|
142
|
-
async function runTurn(runtime, approver, renderer, prompt, exitNow) {
|
|
104
|
+
async function runTurn(runtime, approver, renderer, prompt, exitNow, options) {
|
|
143
105
|
const controller = new AbortController();
|
|
144
106
|
const stop = () => {
|
|
145
107
|
if (controller.signal.aborted) exitNow();
|
|
@@ -158,12 +120,20 @@ async function runTurn(runtime, approver, renderer, prompt, exitNow) {
|
|
|
158
120
|
if (session !== void 0) {
|
|
159
121
|
const runCost = session.costUsd === void 0 ? void 0 : session.costUsd - costBefore;
|
|
160
122
|
const runIncomplete = session.unreportedTokens > unreportedBefore || pendingChildTokens(session) > pendingBefore || session.costIncomplete && !incompleteBefore;
|
|
161
|
-
|
|
162
|
-
|
|
123
|
+
const receipt = runtime.lastReceipt;
|
|
124
|
+
if (receipt !== void 0 && hasReceipt(receipt) && renderer.receipt !== void 0) {
|
|
125
|
+
renderer.receipt(receipt);
|
|
126
|
+
} else {
|
|
127
|
+
renderer.info(
|
|
128
|
+
`
|
|
163
129
|
[${usageLine(result, session, runCost, runtime.limits.contextWindow, runIncomplete)}]`
|
|
164
|
-
|
|
130
|
+
);
|
|
131
|
+
}
|
|
165
132
|
}
|
|
166
|
-
const message =
|
|
133
|
+
const message = (options.chat === true ? chatStopMessage : stopMessage)(
|
|
134
|
+
result.stopReason,
|
|
135
|
+
runtime.limits
|
|
136
|
+
);
|
|
167
137
|
if (message !== void 0) renderer.warn(message);
|
|
168
138
|
return { kind: "done", result };
|
|
169
139
|
} catch (error) {
|
|
@@ -208,6 +178,34 @@ function modeText(runtime) {
|
|
|
208
178
|
return runtime.mode === "plan" ? "Plan mode: the agent reads and plans. It cannot change files; commands run read-only in the sandbox. /build (or Shift+Tab) switches back." : "Build mode: the agent can change files and run commands, with the usual approvals.";
|
|
209
179
|
}
|
|
210
180
|
|
|
181
|
+
// src/cli/chat/rewind.ts
|
|
182
|
+
function ago(at, now) {
|
|
183
|
+
if (at === void 0) return "";
|
|
184
|
+
const minutes = Math.floor(Math.max(0, now - at) / 6e4);
|
|
185
|
+
if (minutes < 1) return "<1 min";
|
|
186
|
+
if (minutes < 60) return `${minutes} min`;
|
|
187
|
+
const hours = Math.floor(minutes / 60);
|
|
188
|
+
return hours < 48 ? `${hours} h` : `${Math.floor(hours / 24)} d`;
|
|
189
|
+
}
|
|
190
|
+
function rewindList(points, now = Date.now()) {
|
|
191
|
+
if (points.length === 0) return "There is no turn to rewind to yet.";
|
|
192
|
+
const width = Math.min(40, Math.max(...points.map((p) => p.prompt.length + 2)));
|
|
193
|
+
const rows = points.map((p, i) => {
|
|
194
|
+
const prompt = `"${p.prompt}"`;
|
|
195
|
+
const shown = prompt.length > width ? `${prompt.slice(0, width - 2)}\u2026"` : prompt;
|
|
196
|
+
const files = p.changedFiles === void 0 ? "" : p.changedFiles === 0 ? "no files" : `${p.changedFiles} file${p.changedFiles === 1 ? "" : "s"}`;
|
|
197
|
+
const cost = p.costUsd === void 0 ? "" : formatUsd(p.costUsd);
|
|
198
|
+
const note = p.conversation ? "" : " (before a compaction: files only)";
|
|
199
|
+
return ` ${String(i + 1).padStart(3)} ${shown.padEnd(width)} ${files.padEnd(8)} ${cost.padEnd(8)} ${ago(p.at, now)}${note}`.trimEnd();
|
|
200
|
+
}).reverse();
|
|
201
|
+
return [
|
|
202
|
+
"Rewind to the start of a turn (newest first):",
|
|
203
|
+
...rows,
|
|
204
|
+
"/rewind <n> goes back to the start of turn n (/redo brings the turns back).",
|
|
205
|
+
"/rewind fork <n> starts a new session from there; this one stays as it is."
|
|
206
|
+
].join("\n");
|
|
207
|
+
}
|
|
208
|
+
|
|
211
209
|
// src/cli/chat/commands.ts
|
|
212
210
|
var HELP = [
|
|
213
211
|
"Type a task and press Enter. @path attaches a file or folder, !command runs a command, Tab completes.",
|
|
@@ -217,7 +215,7 @@ var HELP = [
|
|
|
217
215
|
" /session the session id and file",
|
|
218
216
|
" /sessions this project's sessions; /sessions <n|id> continues one; rename, delete",
|
|
219
217
|
" /models the models; /models <n|id|opus|sonnet|haiku> switches for this chat",
|
|
220
|
-
" /export write this conversation as Markdown; /export <file
|
|
218
|
+
" /export write this conversation as Markdown; /export <file>; /export --html [file] as one page",
|
|
221
219
|
" /compact summarise the older turns now; /compact <what to keep>",
|
|
222
220
|
" /diff file changes in this session; /diff last (last turn); /diff [last] <path>",
|
|
223
221
|
" /schedule make the last plan a job that runs later: /schedule [HH:MM], then garuda run <id>",
|
|
@@ -240,6 +238,7 @@ var HELP = [
|
|
|
240
238
|
" /build build mode: change files and run commands (the default); /build <task> runs it",
|
|
241
239
|
" /undo take back the last turn: its file changes and its messages",
|
|
242
240
|
" /redo bring back the last undone turn",
|
|
241
|
+
" /rewind the turns of this session; /rewind <n> goes back to turn n, /rewind fork <n> forks",
|
|
243
242
|
" /init set up this folder: AGENTS.md, and files from other agents (Claude Code, OpenCode \u2026)",
|
|
244
243
|
" /editor write the next prompt in $VISUAL or $EDITOR (also Ctrl-G)",
|
|
245
244
|
" /details show or hide the result line under each tool call; /details on|off",
|
|
@@ -362,6 +361,18 @@ async function runCommand(text, { runtime, renderer, sessionPath, output, signal
|
|
|
362
361
|
await expertsCommand(runtime, renderer);
|
|
363
362
|
} else if (command === "/audit") {
|
|
364
363
|
await auditCommand(runtime, renderer, text.slice(command.length).trim());
|
|
364
|
+
} else if (command === "/rewind") {
|
|
365
|
+
const args = text.slice(command.length).trim().split(/\s+/).filter((a) => a !== "");
|
|
366
|
+
const signal2 = new AbortController().signal;
|
|
367
|
+
const fork = args[0] === "fork";
|
|
368
|
+
const number = fork ? args[1] : args[0];
|
|
369
|
+
if (number === void 0 && !fork) renderer.info(rewindList(runtime.rewindPoints));
|
|
370
|
+
else if (number === void 0 || !/^\d+$/.test(number) || args.length > (fork ? 2 : 1)) {
|
|
371
|
+
renderer.info("Use: /rewind, /rewind <n> or /rewind fork <n>.");
|
|
372
|
+
} else {
|
|
373
|
+
const turn = Number(number);
|
|
374
|
+
renderer.info(await (fork ? runtime.fork(turn, signal2) : runtime.rewind(turn, signal2)));
|
|
375
|
+
}
|
|
365
376
|
} else if (command === "/undo" || command === "/redo") {
|
|
366
377
|
const signal2 = new AbortController().signal;
|
|
367
378
|
renderer.info(await (command === "/undo" ? runtime.undo(signal2) : runtime.redo(signal2)));
|
|
@@ -484,10 +495,10 @@ async function diffCommand(runtime, renderer, arg, output) {
|
|
|
484
495
|
`Changes${where} ${since} (${result.files.length} file${result.files.length === 1 ? "" : "s"}, +${plus} \u2212${minus}). Changes you made yourself count too.`,
|
|
485
496
|
...rows,
|
|
486
497
|
"",
|
|
487
|
-
colorDiff(lines.slice(0, DIFF_LINES).join("\n")),
|
|
498
|
+
colorDiff(lines.slice(0, DIFF_LINES).join("\n"), process.stdout.columns),
|
|
488
499
|
...cut ? [`\u2026 ${lines.length - DIFF_LINES} more lines. ${more}`] : []
|
|
489
500
|
].join("\n"),
|
|
490
|
-
cut ? { title: `diff ${since}${where}`, text: colorDiff(result.patch) } : void 0
|
|
501
|
+
cut ? { title: `diff ${since}${where}`, text: colorDiff(result.patch, process.stdout.columns) } : void 0
|
|
491
502
|
);
|
|
492
503
|
}
|
|
493
504
|
async function jobsCommand(runtime, renderer, id) {
|
|
@@ -513,7 +524,7 @@ async function jobsCommand(runtime, renderer, id) {
|
|
|
513
524
|
return;
|
|
514
525
|
}
|
|
515
526
|
const jobs = await listJobs2(runtime.root);
|
|
516
|
-
const { defaultAgentEnv, nightAgentTime } = await import("./launchd-
|
|
527
|
+
const { defaultAgentEnv, nightAgentTime } = await import("./launchd-QG6R5RAK.js");
|
|
517
528
|
const night = nightAgentTime(runtime.root, defaultAgentEnv());
|
|
518
529
|
const nightLine = night === void 0 ? [] : [
|
|
519
530
|
`Night shift: launchd starts the queue at ${night.toTimeString().slice(0, 5)} on ${night.toDateString()} (/jobs cancel night removes it).`
|
|
@@ -551,8 +562,11 @@ async function exportCommand(runtime, renderer, file) {
|
|
|
551
562
|
renderer.info("There is no conversation to export yet.");
|
|
552
563
|
return;
|
|
553
564
|
}
|
|
554
|
-
const { sessionMarkdown, writeExport } = await import("./export-
|
|
555
|
-
const
|
|
565
|
+
const { sessionMarkdown, writeExport } = await import("./export-E677HIQS.js");
|
|
566
|
+
const html = /^--html(\s|$)/.test(file);
|
|
567
|
+
const name = html ? file.slice("--html".length).trim() : file;
|
|
568
|
+
const text = html ? (await import("./exportHtml-BMV73YRO.js")).sessionHtml(records, id) : sessionMarkdown(records, id);
|
|
569
|
+
const result = await writeExport(runtime.root, id, text, name, html ? ".html" : ".md");
|
|
556
570
|
if ("problem" in result) renderer.warn(result.problem);
|
|
557
571
|
else
|
|
558
572
|
renderer.info(
|
|
@@ -896,6 +910,7 @@ var RUN_AT_ONCE = /* @__PURE__ */ new Set([
|
|
|
896
910
|
"build",
|
|
897
911
|
"undo",
|
|
898
912
|
"redo",
|
|
913
|
+
"rewind",
|
|
899
914
|
"init",
|
|
900
915
|
"editor",
|
|
901
916
|
"details",
|
|
@@ -1097,8 +1112,30 @@ function rootFiles(root, now = Date.now) {
|
|
|
1097
1112
|
};
|
|
1098
1113
|
}
|
|
1099
1114
|
|
|
1115
|
+
// src/cli/chat/stop.ts
|
|
1116
|
+
var CONTINUE_PROMPT = "Go on with the task from where you stopped. You reached the step limit; the conversation above has your work so far.";
|
|
1117
|
+
var SUMMARY_PROMPT = "Stop the work here and do not call tools. Say what is done, what is left, and the next step.";
|
|
1118
|
+
async function stopHandoff(approver, maxSteps, isolation, signal = new AbortController().signal) {
|
|
1119
|
+
const request = {
|
|
1120
|
+
tool: "step_limit",
|
|
1121
|
+
target: { kind: "input", json: "{}" },
|
|
1122
|
+
preview: `The turn used its ${maxSteps} steps. The next turn gets ${maxSteps} more.`,
|
|
1123
|
+
isolation,
|
|
1124
|
+
title: "The task is not finished.",
|
|
1125
|
+
question: "Go on?",
|
|
1126
|
+
labels: {
|
|
1127
|
+
once: `Go on for ${maxSteps} more steps`,
|
|
1128
|
+
session: "Summarise what is done and what is left",
|
|
1129
|
+
deny: "Stop here"
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
const choice = await approver.ask(request, signal);
|
|
1133
|
+
return choice === "once" ? "more" : choice === "session" ? "summary" : "stop";
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1100
1136
|
// src/cli/chat/controller.ts
|
|
1101
|
-
async function runChat(runtime, store, sessionPath, exitNow, notifier) {
|
|
1137
|
+
async function runChat(runtime, store, sessionPath, exitNow, notifier, tour) {
|
|
1138
|
+
let tourTasks = tour;
|
|
1102
1139
|
store.onToggleMode = () => {
|
|
1103
1140
|
runtime.setMode(runtime.mode === "plan" ? "build" : "plan");
|
|
1104
1141
|
return statusOf(runtime);
|
|
@@ -1117,7 +1154,8 @@ async function runChat(runtime, store, sessionPath, exitNow, notifier) {
|
|
|
1117
1154
|
void args.refresh();
|
|
1118
1155
|
const input = await store.nextInput();
|
|
1119
1156
|
if (input === void 0) return;
|
|
1120
|
-
const text = input.trim();
|
|
1157
|
+
const text = tourChoice(input.trim(), tourTasks);
|
|
1158
|
+
tourTasks = void 0;
|
|
1121
1159
|
let prompt = text;
|
|
1122
1160
|
if (text.startsWith("!") && text.length > 1) {
|
|
1123
1161
|
await runShell(runtime, store, text.slice(1).trim());
|
|
@@ -1167,10 +1205,20 @@ async function runChat(runtime, store, sessionPath, exitNow, notifier) {
|
|
|
1167
1205
|
store.begin(display, prompt === display);
|
|
1168
1206
|
let outcome;
|
|
1169
1207
|
try {
|
|
1170
|
-
outcome = await runTurnInTerminal(runtime, store, store, prompt, exitNow, notifier
|
|
1208
|
+
outcome = await runTurnInTerminal(runtime, store, store, prompt, exitNow, notifier, {
|
|
1209
|
+
chat: true
|
|
1210
|
+
});
|
|
1171
1211
|
} finally {
|
|
1172
1212
|
store.end(statusOf(runtime));
|
|
1173
1213
|
}
|
|
1214
|
+
if (outcome.kind === "done" && outcome.result.stopReason === "max_steps") {
|
|
1215
|
+
const next2 = await stopHandoff(store, runtime.limits.maxSteps, runtime.executor.isolation);
|
|
1216
|
+
store.refreshStatus(statusOf(runtime));
|
|
1217
|
+
if (next2 === "stop") break;
|
|
1218
|
+
prompt = next2 === "more" ? CONTINUE_PROMPT : SUMMARY_PROMPT;
|
|
1219
|
+
display = prompt;
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1174
1222
|
if (!planning || outcome.kind !== "done" || outcome.result.stopReason !== "done") break;
|
|
1175
1223
|
const next = await planHandoff(runtime, store);
|
|
1176
1224
|
store.refreshStatus(statusOf(runtime));
|
|
@@ -1252,6 +1300,14 @@ var CommandArgs = class {
|
|
|
1252
1300
|
return (first === "cancel" || first === "delete") && second === void 0 ? this.jobs : [];
|
|
1253
1301
|
case "diff":
|
|
1254
1302
|
return first === void 0 ? words("last") : [];
|
|
1303
|
+
case "rewind": {
|
|
1304
|
+
const turns = this.runtime.rewindPoints.map((p, i) => ({
|
|
1305
|
+
value: String(i + 1),
|
|
1306
|
+
hint: p.prompt
|
|
1307
|
+
}));
|
|
1308
|
+
if (first === void 0) return [...words("fork"), ...turns];
|
|
1309
|
+
return first === "fork" && second === void 0 ? turns : [];
|
|
1310
|
+
}
|
|
1255
1311
|
case "mcp":
|
|
1256
1312
|
if (first === void 0) return words("logout");
|
|
1257
1313
|
return first === "logout" && second === void 0 ? this.runtime.mcpStatus().map((s) => ({ value: s.name })) : [];
|
|
@@ -1278,8 +1334,6 @@ export {
|
|
|
1278
1334
|
reportsFocus,
|
|
1279
1335
|
FocusTracker,
|
|
1280
1336
|
focusEvent,
|
|
1281
|
-
formatTokens,
|
|
1282
|
-
stopMessage,
|
|
1283
1337
|
BUILD_PROMPT,
|
|
1284
1338
|
planHandoff,
|
|
1285
1339
|
runCommand,
|
|
@@ -1288,9 +1342,12 @@ export {
|
|
|
1288
1342
|
fuzzyFiles,
|
|
1289
1343
|
rootFiles,
|
|
1290
1344
|
runTurnInTerminal,
|
|
1345
|
+
CONTINUE_PROMPT,
|
|
1346
|
+
SUMMARY_PROMPT,
|
|
1347
|
+
stopHandoff,
|
|
1291
1348
|
runChat,
|
|
1292
1349
|
commandNames,
|
|
1293
1350
|
statusOf,
|
|
1294
1351
|
CommandArgs
|
|
1295
1352
|
};
|
|
1296
|
-
//# sourceMappingURL=chunk-
|
|
1353
|
+
//# sourceMappingURL=chunk-34WBDQBL.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/app/receipt.ts
|
|
4
|
+
function hasReceipt(receipt) {
|
|
5
|
+
return (receipt.files?.length ?? 0) > 0 || receipt.tests !== void 0 || receipt.commands.outside > 0 || receipt.commands.blockedHosts.length > 0;
|
|
6
|
+
}
|
|
7
|
+
var RUNNERS = /(^|[\s;&|(/])(vitest|jest|pytest|py\.test|mvn|mvnw|gradle|gradlew|go\s+test|node\s+--test|(npm|pnpm|yarn|bun)\s+(run\s+)?test|cargo\s+test|tox|nox)\b/;
|
|
8
|
+
function isTestCommand(command, projectTest) {
|
|
9
|
+
const text = command.trim();
|
|
10
|
+
if (projectTest !== void 0 && projectTest !== "" && text.includes(projectTest)) return true;
|
|
11
|
+
if (/(^|[\s;&|])(mvn|mvnw|gradle|gradlew)\b/.test(text) && !/\b(test|verify|check)\b/.test(text)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return RUNNERS.test(text);
|
|
15
|
+
}
|
|
16
|
+
function parseTestCount(output) {
|
|
17
|
+
const num = (s) => s === void 0 ? 0 : Number(s);
|
|
18
|
+
const vitest = /^\s*Tests\s+(.*\b(?:passed|failed|skipped)\b.*?)\s+\((\d+)\)\s*$/m.exec(output);
|
|
19
|
+
if (vitest !== null) {
|
|
20
|
+
return { total: num(vitest[2]), failed: num(/(\d+) failed/.exec(vitest[1] ?? "")?.[1]) };
|
|
21
|
+
}
|
|
22
|
+
const jest = /^Tests:\s+(?:(\d+) failed, )?.*?(\d+) total\s*$/m.exec(output);
|
|
23
|
+
if (jest !== null) return { total: num(jest[2]), failed: num(jest[1]) };
|
|
24
|
+
const nodeTotal = /^(?:#|ℹ) tests (\d+)\s*$/m.exec(output);
|
|
25
|
+
if (nodeTotal !== null) {
|
|
26
|
+
return { total: num(nodeTotal[1]), failed: num(/^(?:#|ℹ) fail (\d+)\s*$/m.exec(output)?.[1]) };
|
|
27
|
+
}
|
|
28
|
+
const pytest = /^=+ (.*\b(?:passed|failed)\b.*) in [\d.]+s(?: \([^)]*\))? =+\s*$/m.exec(output);
|
|
29
|
+
if (pytest !== null) {
|
|
30
|
+
const part = (word) => num(new RegExp(`(\\d+) ${word}\\b`).exec(pytest[1] ?? "")?.[1]);
|
|
31
|
+
const failed = part("failed") + part("errors?");
|
|
32
|
+
return { total: part("passed") + failed, failed };
|
|
33
|
+
}
|
|
34
|
+
const maven = [...output.matchAll(/Tests run: (\d+), Failures: (\d+), Errors: (\d+)/g)].at(-1);
|
|
35
|
+
if (maven !== void 0) {
|
|
36
|
+
return { total: num(maven[1]), failed: num(maven[2]) + num(maven[3]) };
|
|
37
|
+
}
|
|
38
|
+
const passes = output.match(/^\s*--- PASS: /gm)?.length ?? 0;
|
|
39
|
+
const fails = output.match(/^\s*--- FAIL: /gm)?.length ?? 0;
|
|
40
|
+
if (passes + fails > 0) return { total: passes + fails, failed: fails };
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
var ReceiptCollector = class {
|
|
44
|
+
constructor(projectTest) {
|
|
45
|
+
this.projectTest = projectTest;
|
|
46
|
+
}
|
|
47
|
+
projectTest;
|
|
48
|
+
calls = /* @__PURE__ */ new Map();
|
|
49
|
+
sandboxed = 0;
|
|
50
|
+
outside = 0;
|
|
51
|
+
blockedHosts = /* @__PURE__ */ new Set();
|
|
52
|
+
tests;
|
|
53
|
+
onEvent(event) {
|
|
54
|
+
if (event.type === "tool_call") {
|
|
55
|
+
const command = event.call.input.command;
|
|
56
|
+
if (typeof command === "string") this.calls.set(event.call.id, command);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (event.type !== "tool_result" || event.outcome.command === void 0) return;
|
|
60
|
+
this.add(this.calls.get(event.call.id) ?? "", event.outcome.command, event.outcome.content);
|
|
61
|
+
}
|
|
62
|
+
/** One command's facts; `output` is the text that the model got (for the test count). */
|
|
63
|
+
add(command, facts, output) {
|
|
64
|
+
if (facts.sandboxed) this.sandboxed++;
|
|
65
|
+
else this.outside++;
|
|
66
|
+
for (const host of facts.blockedHosts) this.blockedHosts.add(host);
|
|
67
|
+
if (!isTestCommand(command, this.projectTest)) return;
|
|
68
|
+
const count = parseTestCount(output);
|
|
69
|
+
const passed = facts.exitCode === 0 && !facts.timedOut;
|
|
70
|
+
this.tests = {
|
|
71
|
+
command: shortCommand(command),
|
|
72
|
+
passed,
|
|
73
|
+
...facts.timedOut ? { timedOut: true } : {},
|
|
74
|
+
...count === void 0 ? {} : { total: count.total, failed: count.failed }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function shortCommand(command) {
|
|
79
|
+
const line = command.trim().split("\n")[0] ?? "";
|
|
80
|
+
return line.length > 40 ? `${line.slice(0, 39)}\u2026` : line;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
hasReceipt,
|
|
85
|
+
parseTestCount,
|
|
86
|
+
ReceiptCollector
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=chunk-4QRTTRVK.js.map
|