@blockrun/runcode 2.5.3 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/commands.js +14 -9
- package/dist/agent/compact.js +17 -0
- package/dist/tools/askuser.js +10 -2
- package/dist/ui/app.js +2 -2
- package/dist/ui/terminal.d.ts +1 -0
- package/dist/ui/terminal.js +11 -2
- package/package.json +1 -1
package/dist/agent/commands.js
CHANGED
|
@@ -295,17 +295,18 @@ const DIRECT_COMMANDS = {
|
|
|
295
295
|
try {
|
|
296
296
|
let address;
|
|
297
297
|
let balance;
|
|
298
|
+
const fetchTimeout = (ms) => new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), ms));
|
|
298
299
|
if (chain === 'solana') {
|
|
299
300
|
const { getOrCreateSolanaWallet, setupAgentSolanaWallet } = await import('@blockrun/llm');
|
|
300
301
|
const w = await getOrCreateSolanaWallet();
|
|
301
302
|
address = w.address;
|
|
302
303
|
try {
|
|
303
304
|
const client = await setupAgentSolanaWallet({ silent: true });
|
|
304
|
-
const bal = await client.getBalance();
|
|
305
|
+
const bal = await Promise.race([client.getBalance(), fetchTimeout(5000)]);
|
|
305
306
|
balance = `$${bal.toFixed(2)} USDC`;
|
|
306
307
|
}
|
|
307
308
|
catch {
|
|
308
|
-
balance = '(
|
|
309
|
+
balance = '(unavailable)';
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
else {
|
|
@@ -314,11 +315,11 @@ const DIRECT_COMMANDS = {
|
|
|
314
315
|
address = w.address;
|
|
315
316
|
try {
|
|
316
317
|
const client = setupAgentWallet({ silent: true });
|
|
317
|
-
const bal = await client.getBalance();
|
|
318
|
+
const bal = await Promise.race([client.getBalance(), fetchTimeout(5000)]);
|
|
318
319
|
balance = `$${bal.toFixed(2)} USDC`;
|
|
319
320
|
}
|
|
320
321
|
catch {
|
|
321
|
-
balance = '(
|
|
322
|
+
balance = '(unavailable)';
|
|
322
323
|
}
|
|
323
324
|
}
|
|
324
325
|
ctx.onEvent({ kind: 'text_delta', text: `**Wallet**\n` +
|
|
@@ -345,12 +346,16 @@ const DIRECT_COMMANDS = {
|
|
|
345
346
|
ctx.history.length = 0;
|
|
346
347
|
ctx.history.push(...compacted);
|
|
347
348
|
resetTokenAnchor();
|
|
349
|
+
const afterTokens = estimateHistoryTokens(ctx.history);
|
|
350
|
+
const saved = beforeTokens - afterTokens;
|
|
351
|
+
const pct = Math.round((saved / beforeTokens) * 100);
|
|
352
|
+
ctx.onEvent({ kind: 'text_delta', text: `Compacted: ~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens (saved ${pct}%)\n`
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
ctx.onEvent({ kind: 'text_delta', text: `Nothing to compact — history is already minimal (${beforeTokens.toLocaleString()} tokens, ${ctx.history.length} messages).\n`
|
|
357
|
+
});
|
|
348
358
|
}
|
|
349
|
-
const afterTokens = estimateHistoryTokens(ctx.history);
|
|
350
|
-
ctx.onEvent({ kind: 'text_delta', text: didCompact
|
|
351
|
-
? `Compacted: ~${beforeTokens.toLocaleString()} → ~${afterTokens.toLocaleString()} tokens\n`
|
|
352
|
-
: `History too short to compact (${beforeTokens.toLocaleString()} tokens, ${ctx.history.length} messages).\n`
|
|
353
|
-
});
|
|
354
359
|
emitDone(ctx);
|
|
355
360
|
},
|
|
356
361
|
};
|
package/dist/agent/compact.js
CHANGED
|
@@ -38,8 +38,16 @@ export async function autoCompactIfNeeded(history, model, client, debug) {
|
|
|
38
38
|
if (debug) {
|
|
39
39
|
console.error(`[runcode] Auto-compacting: ~${currentTokens} tokens, threshold=${threshold}`);
|
|
40
40
|
}
|
|
41
|
+
const beforeTokens = estimateHistoryTokens(history);
|
|
41
42
|
try {
|
|
42
43
|
const compacted = await compactHistory(history, model, client, debug);
|
|
44
|
+
const afterTokens = estimateHistoryTokens(compacted);
|
|
45
|
+
if (afterTokens >= beforeTokens) {
|
|
46
|
+
if (debug) {
|
|
47
|
+
console.error(`[runcode] Auto-compaction grew history (${beforeTokens} → ${afterTokens}) — skipping`);
|
|
48
|
+
}
|
|
49
|
+
return { history, compacted: false };
|
|
50
|
+
}
|
|
43
51
|
return { history: compacted, compacted: true };
|
|
44
52
|
}
|
|
45
53
|
catch (err) {
|
|
@@ -58,8 +66,17 @@ export async function forceCompact(history, model, client, debug) {
|
|
|
58
66
|
if (history.length <= 4) {
|
|
59
67
|
return { history, compacted: false };
|
|
60
68
|
}
|
|
69
|
+
const beforeTokens = estimateHistoryTokens(history);
|
|
61
70
|
try {
|
|
62
71
|
const compacted = await compactHistory(history, model, client, debug);
|
|
72
|
+
const afterTokens = estimateHistoryTokens(compacted);
|
|
73
|
+
// Only accept compaction if it actually reduces tokens
|
|
74
|
+
if (afterTokens >= beforeTokens) {
|
|
75
|
+
if (debug) {
|
|
76
|
+
console.error(`[runcode] Compaction produced larger history (${beforeTokens} → ${afterTokens}) — reverting`);
|
|
77
|
+
}
|
|
78
|
+
return { history, compacted: false };
|
|
79
|
+
}
|
|
63
80
|
return { history: compacted, compacted: true };
|
|
64
81
|
}
|
|
65
82
|
catch (err) {
|
package/dist/tools/askuser.js
CHANGED
|
@@ -9,6 +9,14 @@ async function execute(input, _ctx) {
|
|
|
9
9
|
if (!question) {
|
|
10
10
|
return { output: 'Error: question is required', isError: true };
|
|
11
11
|
}
|
|
12
|
+
// In non-TTY (piped/scripted) mode, creating a new readline would conflict with
|
|
13
|
+
// the TerminalUI's existing readline. Return a hint for the model to proceed.
|
|
14
|
+
if (!process.stdin.isTTY) {
|
|
15
|
+
return {
|
|
16
|
+
output: `[Non-interactive mode] Cannot prompt user. Proceed with a reasonable assumption. Question was: ${question}`,
|
|
17
|
+
isError: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
12
20
|
console.error('');
|
|
13
21
|
console.error(chalk.yellow(' ╭─ Question ────────────────────────────'));
|
|
14
22
|
console.error(chalk.yellow(` │ ${question}`));
|
|
@@ -21,7 +29,7 @@ async function execute(input, _ctx) {
|
|
|
21
29
|
const rl = readline.createInterface({
|
|
22
30
|
input: process.stdin,
|
|
23
31
|
output: process.stderr,
|
|
24
|
-
terminal:
|
|
32
|
+
terminal: true,
|
|
25
33
|
});
|
|
26
34
|
return new Promise((resolve) => {
|
|
27
35
|
let answered = false;
|
|
@@ -32,7 +40,7 @@ async function execute(input, _ctx) {
|
|
|
32
40
|
});
|
|
33
41
|
rl.on('close', () => {
|
|
34
42
|
if (!answered)
|
|
35
|
-
resolve({ output: 'User
|
|
43
|
+
resolve({ output: 'User closed input without responding.', isError: false });
|
|
36
44
|
});
|
|
37
45
|
});
|
|
38
46
|
}
|
package/dist/ui/app.js
CHANGED
|
@@ -295,9 +295,9 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
295
295
|
}), _jsx(Text, { children: " " })] }));
|
|
296
296
|
}
|
|
297
297
|
// ── Normal Mode ──
|
|
298
|
-
return (_jsxs(Box, { flexDirection: "column", children: [statusMsg && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "green", children: statusMsg }) })), showHelp && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Commands" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/model" }), " [name] Switch model (picker if no name)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/wallet" }), " Show wallet address & balance"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/cost" }), " Session cost & savings"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/retry" }), " Retry the last prompt"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/compact" }), " Compress conversation history"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Coding \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/test" }), " Run tests"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/fix" }), " Fix last error"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/review" }), " Code review"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/explain" }), " file Explain code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/search" }), " query Search codebase"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/refactor" }), " desc Refactor code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/scaffold" }), " desc Generate boilerplate"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Git \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/commit" }), " Commit changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/push" }), " Push to remote"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/pr" }), " Create pull request"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/status" }), " Git status"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/diff" }), " Git diff"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/log" }), " Git log"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/branch" }), " [name] Branches"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/stash" }), " Stash changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/undo" }), " Undo last commit"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Analysis \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/security" }), " Security audit"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/lint" }), " Quality check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/optimize" }), " Performance check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/todo" }), " Find TODOs"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/deps" }), " Dependencies"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clean" }), " Dead code removal"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/context" }), " Session info (model, tokens, mode)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/plan" }), " Enter plan mode (read-only tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/execute" }), " Exit plan mode (enable all tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/sessions" }), " List saved sessions"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/resume" }), " id Resume a saved session"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clear" }), " Clear conversation display"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/doctor" }), " Diagnose setup issues"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/help" }), " This help"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/exit" }), " Quit"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Shortcuts: sonnet, opus, gpt, gemini, deepseek, flash, free, r1, o4, nano, mini, haiku" })] })), showWallet && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Wallet" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" Chain: ", _jsx(Text, { color: "magenta", children: chain })] }), _jsxs(Text, { children: [" Address: ", _jsx(Text, { color: "cyan", children: walletAddress })] }), _jsxs(Text, { children: [" Balance: ", _jsx(Text, { color: "green", children: balance })] })] })), Array.from(tools.
|
|
298
|
+
return (_jsxs(Box, { flexDirection: "column", children: [statusMsg && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "green", children: statusMsg }) })), showHelp && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Commands" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/model" }), " [name] Switch model (picker if no name)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/wallet" }), " Show wallet address & balance"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/cost" }), " Session cost & savings"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/retry" }), " Retry the last prompt"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/compact" }), " Compress conversation history"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Coding \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/test" }), " Run tests"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/fix" }), " Fix last error"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/review" }), " Code review"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/explain" }), " file Explain code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/search" }), " query Search codebase"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/refactor" }), " desc Refactor code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/scaffold" }), " desc Generate boilerplate"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Git \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/commit" }), " Commit changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/push" }), " Push to remote"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/pr" }), " Create pull request"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/status" }), " Git status"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/diff" }), " Git diff"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/log" }), " Git log"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/branch" }), " [name] Branches"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/stash" }), " Stash changes"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/undo" }), " Undo last commit"] }), _jsx(Text, { dimColor: true, children: " \u2500\u2500 Analysis \u2500\u2500" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/security" }), " Security audit"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/lint" }), " Quality check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/optimize" }), " Performance check"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/todo" }), " Find TODOs"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/deps" }), " Dependencies"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clean" }), " Dead code removal"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/context" }), " Session info (model, tokens, mode)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/plan" }), " Enter plan mode (read-only tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/execute" }), " Exit plan mode (enable all tools)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/sessions" }), " List saved sessions"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/resume" }), " id Resume a saved session"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/clear" }), " Clear conversation display"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/doctor" }), " Diagnose setup issues"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/help" }), " This help"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/exit" }), " Quit"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Shortcuts: sonnet, opus, gpt, gemini, deepseek, flash, free, r1, o4, nano, mini, haiku" })] })), showWallet && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Wallet" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" Chain: ", _jsx(Text, { color: "magenta", children: chain })] }), _jsxs(Text, { children: [" Address: ", _jsx(Text, { color: "cyan", children: walletAddress })] }), _jsxs(Text, { children: [" Balance: ", _jsx(Text, { color: "green", children: balance })] })] })), Array.from(tools.entries()).map(([id, tool]) => (_jsx(Box, { marginLeft: 1, children: tool.done ? (tool.error
|
|
299
299
|
? _jsxs(Text, { color: "red", children: [" \u2717 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms"] })] })
|
|
300
|
-
: _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms \u2014 ", tool.preview.slice(0, 200), tool.preview.length > 200 ? '...' : ''] })] })) : (_jsxs(Text, { color: "cyan", children: [" ", _jsx(Spinner, { type: "dots" }), " ", tool.name, "... ", _jsx(Text, { dimColor: true, children: (() => { const s = Math.round((Date.now() - tool.startTime) / 1000); return s > 0 ? `${s}s` : ''; })() })] })) },
|
|
300
|
+
: _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms \u2014 ", tool.preview.slice(0, 200), tool.preview.length > 200 ? '...' : ''] })] })) : (_jsxs(Text, { color: "cyan", children: [" ", _jsx(Spinner, { type: "dots" }), " ", tool.name, "... ", _jsx(Text, { dimColor: true, children: (() => { const s = Math.round((Date.now() - tool.startTime) / 1000); return s > 0 ? `${s}s` : ''; })() })] })) }, id))), thinking && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { color: "magenta", children: [" ", _jsx(Spinner, { type: "dots" }), " thinking..."] }), thinkingText && (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [" ", thinkingText.split('\n').pop()?.slice(0, 80)] }))] })), waiting && !thinking && tools.size === 0 && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: "yellow", children: [" ", _jsx(Spinner, { type: "dots" }), " ", _jsx(Text, { dimColor: true, children: currentModel })] }) })), streamText && (_jsx(Box, { marginTop: 0, marginBottom: 0, children: _jsx(Text, { children: streamText }) })), ready && (turnTokens.input > 0 || turnTokens.output > 0) && streamText && (_jsx(Box, { marginLeft: 1, marginTop: 0, children: _jsxs(Text, { dimColor: true, children: [turnTokens.input.toLocaleString(), " in / ", turnTokens.output.toLocaleString(), " out", totalCost > 0 ? ` · $${totalCost.toFixed(4)} session` : ''] }) })), ready && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: balance, focused: mode === 'input' }))] }));
|
|
301
301
|
}
|
|
302
302
|
export function launchInkUI(opts) {
|
|
303
303
|
let resolveInput = null;
|
package/dist/ui/terminal.d.ts
CHANGED
package/dist/ui/terminal.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import readline from 'node:readline';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
+
import { estimateCost } from '../pricing.js';
|
|
8
9
|
// ─── Spinner ───────────────────────────────────────────────────────────────
|
|
9
10
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
10
11
|
class Spinner {
|
|
@@ -130,6 +131,7 @@ export class TerminalUI {
|
|
|
130
131
|
activeCapabilities = new Map();
|
|
131
132
|
totalInputTokens = 0;
|
|
132
133
|
totalOutputTokens = 0;
|
|
134
|
+
sessionModel = '';
|
|
133
135
|
mdRenderer = new MarkdownRenderer();
|
|
134
136
|
// Line queue for piped (non-TTY) input — buffers all stdin lines eagerly
|
|
135
137
|
lineQueue = [];
|
|
@@ -269,6 +271,8 @@ export class TerminalUI {
|
|
|
269
271
|
case 'usage':
|
|
270
272
|
this.totalInputTokens += event.inputTokens;
|
|
271
273
|
this.totalOutputTokens += event.outputTokens;
|
|
274
|
+
if (event.model)
|
|
275
|
+
this.sessionModel = event.model;
|
|
272
276
|
break;
|
|
273
277
|
case 'turn_done': {
|
|
274
278
|
this.spinner.stop();
|
|
@@ -295,9 +299,14 @@ export class TerminalUI {
|
|
|
295
299
|
const cmd = parts[0].toLowerCase();
|
|
296
300
|
switch (cmd) {
|
|
297
301
|
case '/cost':
|
|
298
|
-
case '/usage':
|
|
299
|
-
|
|
302
|
+
case '/usage': {
|
|
303
|
+
const cost = this.sessionModel
|
|
304
|
+
? estimateCost(this.sessionModel, this.totalInputTokens, this.totalOutputTokens)
|
|
305
|
+
: 0;
|
|
306
|
+
const costStr = cost > 0 ? ` · $${cost.toFixed(4)} USDC` : '';
|
|
307
|
+
console.error(chalk.dim(`\n Tokens: ${this.totalInputTokens.toLocaleString()} in / ${this.totalOutputTokens.toLocaleString()} out${costStr}\n`));
|
|
300
308
|
return true;
|
|
309
|
+
}
|
|
301
310
|
default:
|
|
302
311
|
// All other slash commands pass through to the agent loop (commands.ts handles them)
|
|
303
312
|
return false;
|