@blockrun/runcode 2.5.9 → 2.5.11
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/loop.js +2 -1
- package/dist/agent/streaming-executor.d.ts +5 -1
- package/dist/agent/streaming-executor.js +50 -11
- package/dist/agent/types.d.ts +7 -1
- package/dist/tools/bash.js +15 -0
- package/dist/ui/app.js +24 -4
- package/package.json +1 -1
package/dist/agent/loop.js
CHANGED
|
@@ -151,7 +151,8 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
151
151
|
handlers: capabilityMap,
|
|
152
152
|
scope: { workingDir: workDir, abortSignal: abort.signal },
|
|
153
153
|
permissions,
|
|
154
|
-
onStart: (id, name) => onEvent({ kind: 'capability_start', id, name }),
|
|
154
|
+
onStart: (id, name, preview) => onEvent({ kind: 'capability_start', id, name, preview }),
|
|
155
|
+
onProgress: (id, text) => onEvent({ kind: 'capability_progress', id, text }),
|
|
155
156
|
});
|
|
156
157
|
try {
|
|
157
158
|
const result = await client.complete({
|
|
@@ -10,12 +10,14 @@ export declare class StreamingExecutor {
|
|
|
10
10
|
private scope;
|
|
11
11
|
private permissions?;
|
|
12
12
|
private onStart;
|
|
13
|
+
private onProgress?;
|
|
13
14
|
private pending;
|
|
14
15
|
constructor(opts: {
|
|
15
16
|
handlers: Map<string, CapabilityHandler>;
|
|
16
17
|
scope: ExecutionScope;
|
|
17
18
|
permissions?: PermissionManager;
|
|
18
|
-
onStart: (id: string, name: string) => void;
|
|
19
|
+
onStart: (id: string, name: string, preview?: string) => void;
|
|
20
|
+
onProgress?: (id: string, text: string) => void;
|
|
19
21
|
});
|
|
20
22
|
/**
|
|
21
23
|
* Called when a tool_use block is fully received from the stream.
|
|
@@ -29,4 +31,6 @@ export declare class StreamingExecutor {
|
|
|
29
31
|
*/
|
|
30
32
|
collectResults(allInvocations: CapabilityInvocation[]): Promise<[CapabilityInvocation, CapabilityResult][]>;
|
|
31
33
|
private executeWithPermissions;
|
|
34
|
+
/** Extract a short preview string from a tool invocation's input. */
|
|
35
|
+
private inputPreview;
|
|
32
36
|
}
|
|
@@ -8,12 +8,14 @@ export class StreamingExecutor {
|
|
|
8
8
|
scope;
|
|
9
9
|
permissions;
|
|
10
10
|
onStart;
|
|
11
|
+
onProgress;
|
|
11
12
|
pending = [];
|
|
12
13
|
constructor(opts) {
|
|
13
14
|
this.handlers = opts.handlers;
|
|
14
15
|
this.scope = opts.scope;
|
|
15
16
|
this.permissions = opts.permissions;
|
|
16
17
|
this.onStart = opts.onStart;
|
|
18
|
+
this.onProgress = opts.onProgress;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Called when a tool_use block is fully received from the stream.
|
|
@@ -24,13 +26,13 @@ export class StreamingExecutor {
|
|
|
24
26
|
const handler = this.handlers.get(invocation.name);
|
|
25
27
|
const isConcurrent = handler?.concurrent ?? false;
|
|
26
28
|
if (isConcurrent) {
|
|
27
|
-
//
|
|
28
|
-
this.
|
|
29
|
-
|
|
29
|
+
// Concurrent tools are auto-allowed — start immediately and time from here
|
|
30
|
+
const preview = this.inputPreview(invocation);
|
|
31
|
+
this.onStart(invocation.id, invocation.name, preview);
|
|
32
|
+
const promise = this.executeWithPermissions(invocation, 1, false);
|
|
30
33
|
this.pending.push({ invocation, promise });
|
|
31
34
|
}
|
|
32
|
-
// Non-concurrent tools are NOT started here —
|
|
33
|
-
// via getRemainingResults after the model finishes streaming
|
|
35
|
+
// Non-concurrent tools are NOT started here — executed via collectResults
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* After the model finishes streaming, execute any non-concurrent tools
|
|
@@ -41,8 +43,8 @@ export class StreamingExecutor {
|
|
|
41
43
|
const alreadyStarted = new Set(this.pending.map(p => p.invocation.id));
|
|
42
44
|
const pendingSnapshot = [...this.pending];
|
|
43
45
|
this.pending = []; // Clear immediately so errors don't leave stale state
|
|
44
|
-
// Pre-count
|
|
45
|
-
//
|
|
46
|
+
// Pre-count pending sequential invocations per tool type.
|
|
47
|
+
// Shown in permission dialog: "N pending — press [a] to allow all".
|
|
46
48
|
const pendingCounts = new Map();
|
|
47
49
|
for (const inv of allInvocations) {
|
|
48
50
|
if (!alreadyStarted.has(inv.id)) {
|
|
@@ -62,8 +64,9 @@ export class StreamingExecutor {
|
|
|
62
64
|
continue;
|
|
63
65
|
const remaining = remainingCounts.get(inv.name) ?? 1;
|
|
64
66
|
remainingCounts.set(inv.name, remaining - 1);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
// NOTE: onStart is called INSIDE executeWithPermissions, AFTER permission is granted.
|
|
68
|
+
// This ensures elapsed time reflects actual execution time, not permission wait time.
|
|
69
|
+
const result = await this.executeWithPermissions(inv, remaining, true);
|
|
67
70
|
results.push([inv, result]);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -73,7 +76,8 @@ export class StreamingExecutor {
|
|
|
73
76
|
}
|
|
74
77
|
return results;
|
|
75
78
|
}
|
|
76
|
-
async executeWithPermissions(invocation, pendingCount = 1)
|
|
79
|
+
async executeWithPermissions(invocation, pendingCount = 1, callStart = true // false for concurrent tools (already called in onToolReceived)
|
|
80
|
+
) {
|
|
77
81
|
// Permission check
|
|
78
82
|
if (this.permissions) {
|
|
79
83
|
const decision = await this.permissions.check(invocation.name, invocation.input);
|
|
@@ -93,12 +97,24 @@ export class StreamingExecutor {
|
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
}
|
|
100
|
+
// Start timing AFTER permission is granted (accurate elapsed time)
|
|
101
|
+
if (callStart) {
|
|
102
|
+
const preview = this.inputPreview(invocation);
|
|
103
|
+
this.onStart(invocation.id, invocation.name, preview);
|
|
104
|
+
}
|
|
96
105
|
const handler = this.handlers.get(invocation.name);
|
|
97
106
|
if (!handler) {
|
|
98
107
|
return { output: `Unknown capability: ${invocation.name}`, isError: true };
|
|
99
108
|
}
|
|
109
|
+
// Wire per-invocation progress to onProgress callback
|
|
110
|
+
const progressScope = this.onProgress
|
|
111
|
+
? {
|
|
112
|
+
...this.scope,
|
|
113
|
+
onProgress: (text) => this.onProgress(invocation.id, text),
|
|
114
|
+
}
|
|
115
|
+
: this.scope;
|
|
100
116
|
try {
|
|
101
|
-
return await handler.execute(invocation.input,
|
|
117
|
+
return await handler.execute(invocation.input, progressScope);
|
|
102
118
|
}
|
|
103
119
|
catch (err) {
|
|
104
120
|
return {
|
|
@@ -107,4 +123,27 @@ export class StreamingExecutor {
|
|
|
107
123
|
};
|
|
108
124
|
}
|
|
109
125
|
}
|
|
126
|
+
/** Extract a short preview string from a tool invocation's input. */
|
|
127
|
+
inputPreview(invocation) {
|
|
128
|
+
const input = invocation.input;
|
|
129
|
+
switch (invocation.name) {
|
|
130
|
+
case 'Bash': {
|
|
131
|
+
const cmd = input.command || '';
|
|
132
|
+
return cmd.length > 80 ? cmd.slice(0, 80) + '…' : cmd;
|
|
133
|
+
}
|
|
134
|
+
case 'Write':
|
|
135
|
+
case 'Read':
|
|
136
|
+
case 'Edit':
|
|
137
|
+
return input.file_path || undefined;
|
|
138
|
+
case 'Grep':
|
|
139
|
+
return input.pattern || undefined;
|
|
140
|
+
case 'Glob':
|
|
141
|
+
return input.pattern || undefined;
|
|
142
|
+
case 'WebFetch':
|
|
143
|
+
case 'WebSearch':
|
|
144
|
+
return (input.url ?? input.query) || undefined;
|
|
145
|
+
default:
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
110
149
|
}
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -67,12 +67,18 @@ export interface StreamCapabilityStart {
|
|
|
67
67
|
kind: 'capability_start';
|
|
68
68
|
id: string;
|
|
69
69
|
name: string;
|
|
70
|
+
preview?: string;
|
|
70
71
|
}
|
|
71
72
|
export interface StreamCapabilityInputDelta {
|
|
72
73
|
kind: 'capability_input_delta';
|
|
73
74
|
id: string;
|
|
74
75
|
delta: string;
|
|
75
76
|
}
|
|
77
|
+
export interface StreamCapabilityProgress {
|
|
78
|
+
kind: 'capability_progress';
|
|
79
|
+
id: string;
|
|
80
|
+
text: string;
|
|
81
|
+
}
|
|
76
82
|
export interface StreamCapabilityDone {
|
|
77
83
|
kind: 'capability_done';
|
|
78
84
|
id: string;
|
|
@@ -89,7 +95,7 @@ export interface StreamUsageInfo {
|
|
|
89
95
|
outputTokens: number;
|
|
90
96
|
model: string;
|
|
91
97
|
}
|
|
92
|
-
export type StreamEvent = StreamTextDelta | StreamThinkingDelta | StreamCapabilityStart | StreamCapabilityInputDelta | StreamCapabilityDone | StreamTurnDone | StreamUsageInfo;
|
|
98
|
+
export type StreamEvent = StreamTextDelta | StreamThinkingDelta | StreamCapabilityStart | StreamCapabilityInputDelta | StreamCapabilityProgress | StreamCapabilityDone | StreamTurnDone | StreamUsageInfo;
|
|
93
99
|
export interface AgentConfig {
|
|
94
100
|
model: string;
|
|
95
101
|
apiUrl: string;
|
package/dist/tools/bash.js
CHANGED
|
@@ -50,6 +50,19 @@ async function execute(input, ctx) {
|
|
|
50
50
|
child.kill('SIGTERM');
|
|
51
51
|
};
|
|
52
52
|
ctx.abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
53
|
+
// Emit last non-empty line to UI progress (throttled to avoid flooding)
|
|
54
|
+
let lastProgressEmit = 0;
|
|
55
|
+
const emitProgress = (text) => {
|
|
56
|
+
if (!ctx.onProgress)
|
|
57
|
+
return;
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
if (now - lastProgressEmit < 500)
|
|
60
|
+
return; // max 2 updates/sec
|
|
61
|
+
lastProgressEmit = now;
|
|
62
|
+
const lastLine = text.split('\n').map(l => l.trim()).filter(Boolean).pop();
|
|
63
|
+
if (lastLine)
|
|
64
|
+
ctx.onProgress(lastLine.slice(0, 120));
|
|
65
|
+
};
|
|
53
66
|
child.stdout?.on('data', (chunk) => {
|
|
54
67
|
if (truncated)
|
|
55
68
|
return;
|
|
@@ -68,6 +81,7 @@ async function execute(input, ctx) {
|
|
|
68
81
|
outputBytes = MAX_OUTPUT_BYTES;
|
|
69
82
|
truncated = true;
|
|
70
83
|
}
|
|
84
|
+
emitProgress(text);
|
|
71
85
|
});
|
|
72
86
|
child.stderr?.on('data', (chunk) => {
|
|
73
87
|
if (truncated)
|
|
@@ -87,6 +101,7 @@ async function execute(input, ctx) {
|
|
|
87
101
|
outputBytes = MAX_OUTPUT_BYTES;
|
|
88
102
|
truncated = true;
|
|
89
103
|
}
|
|
104
|
+
emitProgress(text);
|
|
90
105
|
});
|
|
91
106
|
child.on('close', (code) => {
|
|
92
107
|
clearTimeout(timer);
|
package/dist/ui/app.js
CHANGED
|
@@ -252,6 +252,8 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
252
252
|
updateBalance: (bal) => setBalance(bal),
|
|
253
253
|
requestPermission: (toolName, description) => {
|
|
254
254
|
return new Promise((resolve) => {
|
|
255
|
+
// Ring the terminal bell — causes tab to show notification badge in iTerm2/Terminal.app
|
|
256
|
+
process.stderr.write('\x07');
|
|
255
257
|
setPermissionRequest({ toolName, description, resolve });
|
|
256
258
|
});
|
|
257
259
|
},
|
|
@@ -277,22 +279,40 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
277
279
|
const next = new Map(prev);
|
|
278
280
|
next.set(event.id, {
|
|
279
281
|
name: event.name, startTime: Date.now(),
|
|
280
|
-
done: false, error: false,
|
|
282
|
+
done: false, error: false,
|
|
283
|
+
preview: event.preview || '',
|
|
284
|
+
liveOutput: '',
|
|
285
|
+
elapsed: 0,
|
|
281
286
|
});
|
|
282
287
|
return next;
|
|
283
288
|
});
|
|
284
289
|
break;
|
|
290
|
+
case 'capability_progress':
|
|
291
|
+
setTools(prev => {
|
|
292
|
+
const t = prev.get(event.id);
|
|
293
|
+
if (!t || t.done)
|
|
294
|
+
return prev;
|
|
295
|
+
const next = new Map(prev);
|
|
296
|
+
next.set(event.id, { ...t, liveOutput: event.text });
|
|
297
|
+
return next;
|
|
298
|
+
});
|
|
299
|
+
break;
|
|
285
300
|
case 'capability_done': {
|
|
286
301
|
setTools(prev => {
|
|
287
302
|
const next = new Map(prev);
|
|
288
303
|
const t = next.get(event.id);
|
|
289
304
|
if (t) {
|
|
305
|
+
// On success: show input preview (command/path). On error: show error output.
|
|
306
|
+
const resultPreview = event.result.isError
|
|
307
|
+
? event.result.output.replace(/\n/g, ' ').slice(0, 150)
|
|
308
|
+
: (t.preview || event.result.output.replace(/\n/g, ' ').slice(0, 120));
|
|
290
309
|
const completed = {
|
|
291
310
|
...t,
|
|
292
311
|
key: event.id,
|
|
293
312
|
done: true,
|
|
294
313
|
error: !!event.result.isError,
|
|
295
|
-
preview:
|
|
314
|
+
preview: resultPreview,
|
|
315
|
+
liveOutput: '',
|
|
296
316
|
elapsed: Date.now() - t.startTime,
|
|
297
317
|
};
|
|
298
318
|
// Move to Static (permanent scrollback) — prevents re-render artifacts
|
|
@@ -333,8 +353,8 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
333
353
|
}
|
|
334
354
|
// ── Normal Mode ──
|
|
335
355
|
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 })] })] })), _jsx(Static, { items: completedTools, children: (tool) => (_jsx(Box, { marginLeft: 1, children: tool.error
|
|
336
|
-
? _jsxs(Text, { color: "red", children: [" \u2717 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms"] })] })
|
|
337
|
-
: _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms
|
|
356
|
+
? _jsxs(Text, { color: "red", children: [" \u2717 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms", tool.preview ? ` — ${tool.preview}` : ''] })] })
|
|
357
|
+
: _jsxs(Text, { color: "green", children: [" \u2713 ", tool.name, " ", _jsxs(Text, { dimColor: true, children: [tool.elapsed, "ms", tool.preview ? ` — ${tool.preview}` : ''] })] }) }, tool.key)) }), permissionRequest && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: "yellow", children: " \u256D\u2500 Permission required \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsxs(Text, { color: "yellow", children: [" \u2502 ", _jsx(Text, { bold: true, children: permissionRequest.toolName })] }), permissionRequest.description.split('\n').map((line, i) => (_jsxs(Text, { dimColor: true, children: [" ", line] }, i))), _jsx(Text, { color: "yellow", children: " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { children: [_jsx(Text, { bold: true, color: "green", children: "[y]" }), _jsx(Text, { dimColor: true, children: " yes " }), _jsx(Text, { bold: true, color: "red", children: "[n]" }), _jsx(Text, { dimColor: true, children: " no " }), _jsx(Text, { bold: true, color: "cyan", children: "[a]" }), _jsx(Text, { dimColor: true, children: " always allow this session" })] }) })] })), Array.from(tools.entries()).map(([id, tool]) => (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { color: "cyan", children: [' ', _jsx(Spinner, { type: "dots" }), ' ', tool.name, tool.preview ? _jsxs(Text, { dimColor: true, children: [": ", tool.preview] }) : null, _jsx(Text, { dimColor: true, children: (() => { const s = Math.round((Date.now() - tool.startTime) / 1000); return s > 0 ? ` ${s}s` : ''; })() })] }), tool.liveOutput ? (_jsxs(Text, { dimColor: true, children: [" \u2514 ", tool.liveOutput] })) : null] }, 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` : ''] }) })), _jsx(InputBox, { input: ready ? input : '', setInput: ready ? setInput : () => { }, onSubmit: ready ? handleSubmit : () => { }, model: currentModel, balance: balance, focused: ready && !permissionRequest })] }));
|
|
338
358
|
}
|
|
339
359
|
export function launchInkUI(opts) {
|
|
340
360
|
let resolveInput = null;
|