@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.
@@ -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
- // Start executing immediately
28
- this.onStart(invocation.id, invocation.name);
29
- const promise = this.executeWithPermissions(invocation);
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 — they'll be executed
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 how many sequential invocations of each tool type are pending.
45
- // Passed to promptUser so the dialog can show "N pending — press [a] to allow all".
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
- this.onStart(inv.id, inv.name);
66
- const result = await this.executeWithPermissions(inv, remaining);
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, this.scope);
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
  }
@@ -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;
@@ -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, preview: '', elapsed: 0,
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: event.result.output.replace(/\n/g, ' ').slice(0, 200),
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 \u2014 ", tool.preview.slice(0, 120), tool.preview.length > 120 ? '...' : ''] })] }) }, 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]) => (_jsx(Box, { marginLeft: 1, children: _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` : ''] }) })), _jsx(InputBox, { input: ready ? input : '', setInput: ready ? setInput : () => { }, onSubmit: ready ? handleSubmit : () => { }, model: currentModel, balance: balance, focused: ready && !permissionRequest })] }));
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/runcode",
3
- "version": "2.5.9",
3
+ "version": "2.5.11",
4
4
  "description": "RunCode — AI coding agent powered by 41+ models. Pay per use with USDC.",
5
5
  "type": "module",
6
6
  "bin": {