@mragentix/cli 4.2.37 → 4.2.39

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.
Files changed (195) hide show
  1. package/dist/cli.js +179 -8
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +64 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +13 -1
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/branding.d.ts +11 -0
  10. package/dist/core/branding.d.ts.map +1 -0
  11. package/dist/core/branding.js +11 -0
  12. package/dist/core/branding.js.map +1 -0
  13. package/dist/core/compaction/compactor.d.ts +1 -0
  14. package/dist/core/compaction/compactor.d.ts.map +1 -1
  15. package/dist/core/compaction/compactor.js +8 -1
  16. package/dist/core/compaction/compactor.js.map +1 -1
  17. package/dist/core/index.d.ts +2 -0
  18. package/dist/core/index.d.ts.map +1 -1
  19. package/dist/core/index.js +2 -0
  20. package/dist/core/index.js.map +1 -1
  21. package/dist/core/skills.d.ts +2 -1
  22. package/dist/core/skills.d.ts.map +1 -1
  23. package/dist/core/skills.js +8 -8
  24. package/dist/core/skills.js.map +1 -1
  25. package/dist/core/slash-commands.d.ts.map +1 -1
  26. package/dist/core/slash-commands.js +97 -1
  27. package/dist/core/slash-commands.js.map +1 -1
  28. package/dist/core/update.d.ts +34 -0
  29. package/dist/core/update.d.ts.map +1 -0
  30. package/dist/core/update.js +231 -0
  31. package/dist/core/update.js.map +1 -0
  32. package/dist/interactive.d.ts.map +1 -1
  33. package/dist/interactive.js +17 -5
  34. package/dist/interactive.js.map +1 -1
  35. package/dist/modes/json-mode.d.ts.map +1 -1
  36. package/dist/modes/json-mode.js +2 -1
  37. package/dist/modes/json-mode.js.map +1 -1
  38. package/dist/modes/print-mode.d.ts.map +1 -1
  39. package/dist/modes/print-mode.js +2 -1
  40. package/dist/modes/print-mode.js.map +1 -1
  41. package/dist/modes/rpc-mode.d.ts.map +1 -1
  42. package/dist/modes/rpc-mode.js +2 -1
  43. package/dist/modes/rpc-mode.js.map +1 -1
  44. package/dist/modes/serve-mode.d.ts.map +1 -1
  45. package/dist/modes/serve-mode.js +7 -6
  46. package/dist/modes/serve-mode.js.map +1 -1
  47. package/dist/system-prompt.d.ts +1 -1
  48. package/dist/system-prompt.d.ts.map +1 -1
  49. package/dist/system-prompt.js +40 -2
  50. package/dist/system-prompt.js.map +1 -1
  51. package/dist/tools/bash.d.ts +3 -1
  52. package/dist/tools/bash.d.ts.map +1 -1
  53. package/dist/tools/bash.js +4 -1
  54. package/dist/tools/bash.js.map +1 -1
  55. package/dist/tools/edit-diff.test.d.ts +2 -0
  56. package/dist/tools/edit-diff.test.d.ts.map +1 -0
  57. package/dist/tools/edit-diff.test.js +67 -0
  58. package/dist/tools/edit-diff.test.js.map +1 -0
  59. package/dist/tools/edit.d.ts +3 -1
  60. package/dist/tools/edit.d.ts.map +1 -1
  61. package/dist/tools/edit.js +4 -1
  62. package/dist/tools/edit.js.map +1 -1
  63. package/dist/tools/edit.test.d.ts +2 -0
  64. package/dist/tools/edit.test.d.ts.map +1 -0
  65. package/dist/tools/edit.test.js +93 -0
  66. package/dist/tools/edit.test.js.map +1 -0
  67. package/dist/tools/enter-plan.d.ts +8 -0
  68. package/dist/tools/enter-plan.d.ts.map +1 -0
  69. package/dist/tools/enter-plan.js +28 -0
  70. package/dist/tools/enter-plan.js.map +1 -0
  71. package/dist/tools/exit-plan.d.ts +8 -0
  72. package/dist/tools/exit-plan.d.ts.map +1 -0
  73. package/dist/tools/exit-plan.js +36 -0
  74. package/dist/tools/exit-plan.js.map +1 -0
  75. package/dist/tools/index.d.ts +10 -0
  76. package/dist/tools/index.d.ts.map +1 -1
  77. package/dist/tools/index.js +20 -4
  78. package/dist/tools/index.js.map +1 -1
  79. package/dist/tools/path-utils.test.d.ts +2 -0
  80. package/dist/tools/path-utils.test.d.ts.map +1 -0
  81. package/dist/tools/path-utils.test.js +49 -0
  82. package/dist/tools/path-utils.test.js.map +1 -0
  83. package/dist/tools/plan-mode.test.d.ts +2 -0
  84. package/dist/tools/plan-mode.test.d.ts.map +1 -0
  85. package/dist/tools/plan-mode.test.js +214 -0
  86. package/dist/tools/plan-mode.test.js.map +1 -0
  87. package/dist/tools/read.test.d.ts +2 -0
  88. package/dist/tools/read.test.d.ts.map +1 -0
  89. package/dist/tools/read.test.js +81 -0
  90. package/dist/tools/read.test.js.map +1 -0
  91. package/dist/tools/skill.d.ts +10 -0
  92. package/dist/tools/skill.d.ts.map +1 -0
  93. package/dist/tools/skill.js +36 -0
  94. package/dist/tools/skill.js.map +1 -0
  95. package/dist/tools/subagent.d.ts +3 -1
  96. package/dist/tools/subagent.d.ts.map +1 -1
  97. package/dist/tools/subagent.js +4 -1
  98. package/dist/tools/subagent.js.map +1 -1
  99. package/dist/tools/truncate-utils.test.d.ts +2 -0
  100. package/dist/tools/truncate-utils.test.d.ts.map +1 -0
  101. package/dist/tools/truncate-utils.test.js +93 -0
  102. package/dist/tools/truncate-utils.test.js.map +1 -0
  103. package/dist/tools/web-fetch.js +1 -1
  104. package/dist/tools/web-fetch.js.map +1 -1
  105. package/dist/tools/write.d.ts +3 -1
  106. package/dist/tools/write.d.ts.map +1 -1
  107. package/dist/tools/write.js +11 -1
  108. package/dist/tools/write.js.map +1 -1
  109. package/dist/tools/write.test.js +65 -52
  110. package/dist/tools/write.test.js.map +1 -1
  111. package/dist/ui/App.d.ts +18 -1
  112. package/dist/ui/App.d.ts.map +1 -1
  113. package/dist/ui/App.js +343 -31
  114. package/dist/ui/App.js.map +1 -1
  115. package/dist/ui/components/ActivityIndicator.d.ts +2 -1
  116. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  117. package/dist/ui/components/ActivityIndicator.js +25 -4
  118. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  119. package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
  120. package/dist/ui/components/AssistantMessage.js +6 -1
  121. package/dist/ui/components/AssistantMessage.js.map +1 -1
  122. package/dist/ui/components/Banner.d.ts.map +1 -1
  123. package/dist/ui/components/Banner.js +12 -6
  124. package/dist/ui/components/Banner.js.map +1 -1
  125. package/dist/ui/components/Footer.d.ts +2 -1
  126. package/dist/ui/components/Footer.d.ts.map +1 -1
  127. package/dist/ui/components/Footer.js +30 -16
  128. package/dist/ui/components/Footer.js.map +1 -1
  129. package/dist/ui/components/InputArea.d.ts +3 -1
  130. package/dist/ui/components/InputArea.d.ts.map +1 -1
  131. package/dist/ui/components/InputArea.js +27 -17
  132. package/dist/ui/components/InputArea.js.map +1 -1
  133. package/dist/ui/components/Markdown.d.ts +5 -1
  134. package/dist/ui/components/Markdown.d.ts.map +1 -1
  135. package/dist/ui/components/Markdown.js +14 -6
  136. package/dist/ui/components/Markdown.js.map +1 -1
  137. package/dist/ui/components/PlanApproval.d.ts +8 -0
  138. package/dist/ui/components/PlanApproval.d.ts.map +1 -0
  139. package/dist/ui/components/PlanApproval.js +58 -0
  140. package/dist/ui/components/PlanApproval.js.map +1 -0
  141. package/dist/ui/components/PlanBanner.d.ts +6 -0
  142. package/dist/ui/components/PlanBanner.d.ts.map +1 -0
  143. package/dist/ui/components/PlanBanner.js +28 -0
  144. package/dist/ui/components/PlanBanner.js.map +1 -0
  145. package/dist/ui/components/PlanOverlay.d.ts +11 -0
  146. package/dist/ui/components/PlanOverlay.d.ts.map +1 -0
  147. package/dist/ui/components/PlanOverlay.js +267 -0
  148. package/dist/ui/components/PlanOverlay.js.map +1 -0
  149. package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
  150. package/dist/ui/components/ServerToolExecution.js +11 -3
  151. package/dist/ui/components/ServerToolExecution.js.map +1 -1
  152. package/dist/ui/components/SkillsOverlay.d.ts +7 -0
  153. package/dist/ui/components/SkillsOverlay.d.ts.map +1 -0
  154. package/dist/ui/components/SkillsOverlay.js +158 -0
  155. package/dist/ui/components/SkillsOverlay.js.map +1 -0
  156. package/dist/ui/components/Spinner.js +1 -1
  157. package/dist/ui/components/Spinner.js.map +1 -1
  158. package/dist/ui/components/StreamingArea.d.ts +2 -1
  159. package/dist/ui/components/StreamingArea.d.ts.map +1 -1
  160. package/dist/ui/components/StreamingArea.js +7 -2
  161. package/dist/ui/components/StreamingArea.js.map +1 -1
  162. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
  163. package/dist/ui/components/SubAgentPanel.js +21 -11
  164. package/dist/ui/components/SubAgentPanel.js.map +1 -1
  165. package/dist/ui/components/TaskOverlay.d.ts.map +1 -1
  166. package/dist/ui/components/TaskOverlay.js +8 -8
  167. package/dist/ui/components/TaskOverlay.js.map +1 -1
  168. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  169. package/dist/ui/components/ToolExecution.js +33 -13
  170. package/dist/ui/components/ToolExecution.js.map +1 -1
  171. package/dist/ui/components/ToolGroupExecution.js +1 -1
  172. package/dist/ui/components/ToolGroupExecution.js.map +1 -1
  173. package/dist/ui/hooks/useAgentLoop.d.ts +8 -0
  174. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  175. package/dist/ui/hooks/useAgentLoop.js +318 -236
  176. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  177. package/dist/ui/hooks/useTerminalSize.d.ts +18 -16
  178. package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -1
  179. package/dist/ui/hooks/useTerminalSize.js +31 -24
  180. package/dist/ui/hooks/useTerminalSize.js.map +1 -1
  181. package/dist/ui/login.d.ts.map +1 -1
  182. package/dist/ui/login.js +1 -5
  183. package/dist/ui/login.js.map +1 -1
  184. package/dist/ui/render.d.ts +11 -0
  185. package/dist/ui/render.d.ts.map +1 -1
  186. package/dist/ui/render.js +7 -2
  187. package/dist/ui/render.js.map +1 -1
  188. package/dist/ui/sessions.d.ts.map +1 -1
  189. package/dist/ui/sessions.js +1 -5
  190. package/dist/ui/sessions.js.map +1 -1
  191. package/dist/ui/theme/dark.json +3 -1
  192. package/dist/ui/theme/light.json +3 -1
  193. package/dist/ui/theme/theme.d.ts +2 -0
  194. package/dist/ui/theme/theme.d.ts.map +1 -1
  195. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"useAgentLoop.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useAgentLoop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAgentLoop.d.ts","sourceRoot":"","sources":["../../../src/ui/hooks/useAgentLoop.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { useState, useRef, useCallback, useEffect } from "react";
2
- import { agentLoop } from "@mragentix/agent";
2
+ import { agentLoop, isAbortError } from "@mragentix/agent";
3
3
  /** Rough token estimate from message content (~4 chars per token). */
4
4
  function estimateTokens(msgs) {
5
5
  let chars = 0;
@@ -22,6 +22,31 @@ function estimateTokens(msgs) {
22
22
  }
23
23
  return Math.round(chars / 4);
24
24
  }
25
+ /**
26
+ * Merge multiple UserContent items into a single one.
27
+ * Text-only items are joined with newlines. Mixed content (text + images)
28
+ * is flattened into a content array preserving all parts.
29
+ */
30
+ function mergeUserContent(items) {
31
+ if (items.length === 1)
32
+ return items[0];
33
+ const hasArrayContent = items.some((c) => Array.isArray(c));
34
+ if (!hasArrayContent) {
35
+ // All items are strings — join with newlines
36
+ return items.join("\n");
37
+ }
38
+ // Flatten into a single content array
39
+ const parts = [];
40
+ for (const item of items) {
41
+ if (typeof item === "string") {
42
+ parts.push({ type: "text", text: item });
43
+ }
44
+ else {
45
+ parts.push(...item);
46
+ }
47
+ }
48
+ return parts;
49
+ }
25
50
  export function useAgentLoop(messages, options, callbacks) {
26
51
  const onComplete = callbacks?.onComplete;
27
52
  const onTurnText = callbacks?.onTurnText;
@@ -33,6 +58,7 @@ export function useAgentLoop(messages, options, callbacks) {
33
58
  const onTurnEnd = callbacks?.onTurnEnd;
34
59
  const onDone = callbacks?.onDone;
35
60
  const onAborted = callbacks?.onAborted;
61
+ const onQueuedStart = callbacks?.onQueuedStart;
36
62
  const [isRunning, setIsRunning] = useState(false);
37
63
  const [streamingText, setStreamingText] = useState("");
38
64
  const [streamingThinking, setStreamingThinking] = useState("");
@@ -46,6 +72,8 @@ export function useAgentLoop(messages, options, callbacks) {
46
72
  const [isThinking, setIsThinking] = useState(false);
47
73
  const [streamedTokenEstimate, setStreamedTokenEstimate] = useState(0);
48
74
  const abortRef = useRef(null);
75
+ const queueRef = useRef([]);
76
+ const [queuedCount, setQueuedCount] = useState(0);
49
77
  const activeToolCallsRef = useRef([]);
50
78
  const textPendingRef = useRef("");
51
79
  const textVisibleRef = useRef("");
@@ -182,254 +210,304 @@ export function useAgentLoop(messages, options, callbacks) {
182
210
  setThinkingMs(0);
183
211
  setIsThinking(false);
184
212
  setStreamedTokenEstimate(0);
213
+ queueRef.current = [];
214
+ setQueuedCount(0);
215
+ }, []);
216
+ const queueMessage = useCallback((content) => {
217
+ queueRef.current.push(content);
218
+ setQueuedCount(queueRef.current.length);
219
+ }, []);
220
+ const clearQueue = useCallback(() => {
221
+ queueRef.current = [];
222
+ setQueuedCount(0);
185
223
  }, []);
186
224
  const run = useCallback(async (userContent) => {
187
- const ac = new AbortController();
188
- abortRef.current = ac;
189
- let wasAborted = false;
190
- // Reset state
191
- doneCalledRef.current = false;
192
- textPendingRef.current = "";
193
- textVisibleRef.current = "";
194
- thinkingBufferRef.current = "";
195
- thinkingPendingRef.current = "";
196
- thinkingVisibleRef.current = "";
197
- runStartRef.current = Date.now();
198
- toolsUsedRef.current = new Set();
199
- charCountRef.current = 0;
200
- realTokensAccumRef.current = 0;
201
- thinkingAccumRef.current = 0;
202
- thinkingStartRef.current = null;
203
- phaseRef.current = "waiting";
204
- setStreamingText("");
205
- setStreamingThinking("");
206
- setActiveToolCalls([]);
207
- setActivityPhase("waiting");
208
- setElapsedMs(0);
209
- setThinkingMs(0);
210
- setIsThinking(false);
211
- setStreamedTokenEstimate(0);
212
- setIsRunning(true);
213
- // Start elapsed timer (ticks every 1000ms — less frequent to reduce
214
- // Ink re-renders which cause live-area flickering and viewport snapping)
215
- if (elapsedTimerRef.current)
216
- clearInterval(elapsedTimerRef.current);
217
- const timerStart = Date.now();
218
- elapsedTimerRef.current = setInterval(() => {
219
- const now = Date.now();
220
- setElapsedMs(now - timerStart);
221
- // Update live thinking time if currently thinking
222
- if (thinkingStartRef.current !== null) {
223
- setThinkingMs(thinkingAccumRef.current + (now - thinkingStartRef.current));
224
- }
225
- // Update token estimate
226
- setStreamedTokenEstimate(realTokensAccumRef.current + Math.ceil(charCountRef.current / 4));
227
- }, 1000);
228
- /** Freeze thinking time if currently in thinking phase */
229
- const freezeThinking = () => {
230
- if (thinkingStartRef.current !== null) {
231
- thinkingAccumRef.current += Date.now() - thinkingStartRef.current;
232
- thinkingStartRef.current = null;
233
- setThinkingMs(thinkingAccumRef.current);
234
- setIsThinking(false);
235
- }
236
- };
237
- // Push user message
238
- const userMsg = { role: "user", content: userContent };
239
- messages.current.push(userMsg);
240
- const startIndex = messages.current.length;
241
- try {
242
- // Resolve fresh credentials (handles OAuth token refresh)
243
- let apiKey = options.apiKey;
244
- let accountId = options.accountId;
245
- if (options.resolveCredentials) {
246
- const creds = await options.resolveCredentials();
247
- apiKey = creds.apiKey;
248
- accountId = creds.accountId;
249
- }
250
- const generator = agentLoop(messages.current, {
251
- provider: options.provider,
252
- model: options.model,
253
- tools: options.tools,
254
- webSearch: options.webSearch,
255
- maxTokens: options.maxTokens,
256
- thinking: options.thinking,
257
- apiKey,
258
- baseUrl: options.baseUrl,
259
- accountId,
260
- signal: ac.signal,
261
- transformContext: options.transformContext,
262
- });
263
- for await (const event of generator) {
264
- switch (event.type) {
265
- case "text_delta":
266
- textPendingRef.current += event.text;
267
- charCountRef.current += event.text.length;
268
- startReveal();
269
- if (phaseRef.current !== "generating") {
225
+ /** Run a single user message through the agent loop. Returns true if aborted. */
226
+ const runSingle = async (content) => {
227
+ const ac = new AbortController();
228
+ abortRef.current = ac;
229
+ let wasAborted = false;
230
+ // Reset state
231
+ doneCalledRef.current = false;
232
+ textPendingRef.current = "";
233
+ textVisibleRef.current = "";
234
+ thinkingBufferRef.current = "";
235
+ thinkingPendingRef.current = "";
236
+ thinkingVisibleRef.current = "";
237
+ runStartRef.current = Date.now();
238
+ toolsUsedRef.current = new Set();
239
+ charCountRef.current = 0;
240
+ realTokensAccumRef.current = 0;
241
+ thinkingAccumRef.current = 0;
242
+ thinkingStartRef.current = null;
243
+ phaseRef.current = "waiting";
244
+ setStreamingText("");
245
+ setStreamingThinking("");
246
+ setActiveToolCalls([]);
247
+ setActivityPhase("waiting");
248
+ setElapsedMs(0);
249
+ setThinkingMs(0);
250
+ setIsThinking(false);
251
+ setStreamedTokenEstimate(0);
252
+ setIsRunning(true);
253
+ // Start elapsed timer (ticks every 1000ms — less frequent to reduce
254
+ // Ink re-renders which cause live-area flickering and viewport snapping)
255
+ if (elapsedTimerRef.current)
256
+ clearInterval(elapsedTimerRef.current);
257
+ const timerStart = Date.now();
258
+ elapsedTimerRef.current = setInterval(() => {
259
+ const now = Date.now();
260
+ setElapsedMs(now - timerStart);
261
+ // Update live thinking time if currently thinking
262
+ if (thinkingStartRef.current !== null) {
263
+ setThinkingMs(thinkingAccumRef.current + (now - thinkingStartRef.current));
264
+ }
265
+ // Update token estimate
266
+ setStreamedTokenEstimate(realTokensAccumRef.current + Math.ceil(charCountRef.current / 4));
267
+ }, 1000);
268
+ /** Freeze thinking time if currently in thinking phase */
269
+ const freezeThinking = () => {
270
+ if (thinkingStartRef.current !== null) {
271
+ thinkingAccumRef.current += Date.now() - thinkingStartRef.current;
272
+ thinkingStartRef.current = null;
273
+ setThinkingMs(thinkingAccumRef.current);
274
+ setIsThinking(false);
275
+ }
276
+ };
277
+ // Push user message
278
+ const userMsg = { role: "user", content: content };
279
+ messages.current.push(userMsg);
280
+ const startIndex = messages.current.length;
281
+ try {
282
+ // Resolve fresh credentials (handles OAuth token refresh)
283
+ let apiKey = options.apiKey;
284
+ let accountId = options.accountId;
285
+ if (options.resolveCredentials) {
286
+ const creds = await options.resolveCredentials();
287
+ apiKey = creds.apiKey;
288
+ accountId = creds.accountId;
289
+ }
290
+ const generator = agentLoop(messages.current, {
291
+ provider: options.provider,
292
+ model: options.model,
293
+ tools: options.tools,
294
+ webSearch: options.webSearch,
295
+ maxTokens: options.maxTokens,
296
+ thinking: options.thinking,
297
+ apiKey,
298
+ baseUrl: options.baseUrl,
299
+ accountId,
300
+ signal: ac.signal,
301
+ transformContext: options.transformContext,
302
+ // Drain queued messages as steering — injected between tool calls
303
+ // and before the agent would stop, so the LLM sees user guidance
304
+ // within the same run instead of waiting for a new one.
305
+ getSteeringMessages: () => {
306
+ if (queueRef.current.length === 0)
307
+ return null;
308
+ const batch = queueRef.current.splice(0);
309
+ setQueuedCount(0);
310
+ const merged = mergeUserContent(batch);
311
+ onQueuedStart?.(merged);
312
+ return [{ role: "user", content: merged }];
313
+ },
314
+ });
315
+ for await (const event of generator) {
316
+ switch (event.type) {
317
+ case "text_delta":
318
+ textPendingRef.current += event.text;
319
+ charCountRef.current += event.text.length;
320
+ startReveal();
321
+ if (phaseRef.current !== "generating") {
322
+ freezeThinking();
323
+ phaseRef.current = "generating";
324
+ setActivityPhase("generating");
325
+ }
326
+ break;
327
+ case "thinking_delta":
328
+ thinkingBufferRef.current += event.text;
329
+ thinkingPendingRef.current += event.text;
330
+ charCountRef.current += event.text.length;
331
+ startThinkingReveal();
332
+ if (phaseRef.current !== "thinking") {
333
+ thinkingStartRef.current = Date.now();
334
+ setIsThinking(true);
335
+ phaseRef.current = "thinking";
336
+ setActivityPhase("thinking");
337
+ }
338
+ break;
339
+ case "tool_call_start": {
270
340
  freezeThinking();
271
- phaseRef.current = "generating";
272
- setActivityPhase("generating");
341
+ if (phaseRef.current !== "tools") {
342
+ phaseRef.current = "tools";
343
+ setActivityPhase("tools");
344
+ }
345
+ const newTc = {
346
+ toolCallId: event.toolCallId,
347
+ name: event.name,
348
+ args: event.args,
349
+ startTime: Date.now(),
350
+ updates: [],
351
+ };
352
+ onToolStart?.(event.toolCallId, event.name, event.args);
353
+ toolsUsedRef.current.add(event.name);
354
+ activeToolCallsRef.current = [...activeToolCallsRef.current, newTc];
355
+ setActiveToolCalls(activeToolCallsRef.current);
356
+ break;
273
357
  }
274
- break;
275
- case "thinking_delta":
276
- thinkingBufferRef.current += event.text;
277
- thinkingPendingRef.current += event.text;
278
- charCountRef.current += event.text.length;
279
- startThinkingReveal();
280
- if (phaseRef.current !== "thinking") {
281
- thinkingStartRef.current = Date.now();
282
- setIsThinking(true);
283
- phaseRef.current = "thinking";
284
- setActivityPhase("thinking");
358
+ case "tool_call_update": {
359
+ onToolUpdate?.(event.toolCallId, event.update);
360
+ // Mutate the matching tool call in-place to avoid allocating
361
+ // a new array + new objects on every update event. Over a 5h
362
+ // session with thousands of tool calls this prevents significant
363
+ // GC pressure from spread-copy churn.
364
+ const target = activeToolCallsRef.current.find((tc) => tc.toolCallId === event.toolCallId);
365
+ if (target) {
366
+ if (target.updates.length >= 20) {
367
+ target.updates.shift();
368
+ }
369
+ target.updates.push(event.update);
370
+ }
371
+ // Spread once to create a new array reference for React state
372
+ setActiveToolCalls([...activeToolCallsRef.current]);
373
+ break;
285
374
  }
286
- break;
287
- case "tool_call_start": {
288
- freezeThinking();
289
- if (phaseRef.current !== "tools") {
290
- phaseRef.current = "tools";
291
- setActivityPhase("tools");
375
+ case "tool_call_end": {
376
+ const tc = activeToolCallsRef.current.find((t) => t.toolCallId === event.toolCallId);
377
+ const toolName = tc?.name ?? "unknown";
378
+ const durationMs = tc ? Date.now() - tc.startTime : 0;
379
+ onToolEnd?.(event.toolCallId, toolName, event.result, event.isError, durationMs, event.details);
380
+ activeToolCallsRef.current = activeToolCallsRef.current.filter((t) => t.toolCallId !== event.toolCallId);
381
+ setActiveToolCalls(activeToolCallsRef.current);
382
+ break;
292
383
  }
293
- const newTc = {
294
- toolCallId: event.toolCallId,
295
- name: event.name,
296
- args: event.args,
297
- startTime: Date.now(),
298
- updates: [],
299
- };
300
- onToolStart?.(event.toolCallId, event.name, event.args);
301
- toolsUsedRef.current.add(event.name);
302
- activeToolCallsRef.current = [...activeToolCallsRef.current, newTc];
303
- setActiveToolCalls(activeToolCallsRef.current);
304
- break;
305
- }
306
- case "tool_call_update": {
307
- onToolUpdate?.(event.toolCallId, event.update);
308
- // Mutate the matching tool call in-place to avoid allocating
309
- // a new array + new objects on every update event. Over a 5h
310
- // session with thousands of tool calls this prevents significant
311
- // GC pressure from spread-copy churn.
312
- const target = activeToolCallsRef.current.find((tc) => tc.toolCallId === event.toolCallId);
313
- if (target) {
314
- if (target.updates.length >= 20) {
315
- target.updates.shift();
384
+ case "server_tool_call":
385
+ onServerToolCall?.(event.id, event.name, event.input);
386
+ break;
387
+ case "server_tool_result":
388
+ onServerToolResult?.(event.toolUseId, event.resultType, event.data);
389
+ break;
390
+ case "steering_message":
391
+ // Steering message was injected — UI already notified via
392
+ // onQueuedStart inside getSteeringMessages callback.
393
+ break;
394
+ case "turn_end":
395
+ onTurnEnd?.(event.turn, event.stopReason, event.usage);
396
+ setCurrentTurn(event.turn);
397
+ setTotalTokens((prev) => ({
398
+ input: prev.input + event.usage.inputTokens,
399
+ output: prev.output + event.usage.outputTokens,
400
+ }));
401
+ // Latest turn's input tokens = current context window fill
402
+ // With prompt caching, input_tokens only counts non-cached tokens.
403
+ // Total context = input + cache_read + cache_write.
404
+ setContextUsed(event.usage.inputTokens +
405
+ (event.usage.cacheRead ?? 0) +
406
+ (event.usage.cacheWrite ?? 0));
407
+ // Replace char-based estimate with real output tokens
408
+ realTokensAccumRef.current += event.usage.outputTokens;
409
+ charCountRef.current = 0;
410
+ setStreamedTokenEstimate(realTokensAccumRef.current);
411
+ // Reset phase for next turn
412
+ phaseRef.current = "waiting";
413
+ setActivityPhase("waiting");
414
+ // Flush all pending text before completing turn
415
+ flushAllText();
416
+ if (textVisibleRef.current) {
417
+ onTurnText?.(textVisibleRef.current, thinkingBufferRef.current, thinkingAccumRef.current);
316
418
  }
317
- target.updates.push(event.update);
318
- }
319
- // Spread once to create a new array reference for React state
320
- setActiveToolCalls([...activeToolCallsRef.current]);
321
- break;
419
+ // Reset streaming buffers for next turn
420
+ textPendingRef.current = "";
421
+ textVisibleRef.current = "";
422
+ thinkingBufferRef.current = "";
423
+ thinkingPendingRef.current = "";
424
+ thinkingVisibleRef.current = "";
425
+ setStreamingText("");
426
+ setStreamingThinking("");
427
+ break;
428
+ case "agent_done":
429
+ flushAllText();
430
+ // Batch ALL completion state into a single render so Ink
431
+ // processes the live-area change atomically. Previously
432
+ // isRunning, activityPhase, and onDone landed in separate
433
+ // render batches, causing multiple live-area height changes
434
+ // that confused Ink's cursor math and clipped content.
435
+ setIsRunning(false);
436
+ phaseRef.current = "idle";
437
+ setActivityPhase("idle");
438
+ // Call onDone HERE (not in finally) so its state updates
439
+ // (doneStatus, flushing items to Static) are batched too.
440
+ onDone?.(Date.now() - runStartRef.current, [...toolsUsedRef.current]);
441
+ doneCalledRef.current = true;
442
+ break;
322
443
  }
323
- case "tool_call_end": {
324
- const tc = activeToolCallsRef.current.find((t) => t.toolCallId === event.toolCallId);
325
- const toolName = tc?.name ?? "unknown";
326
- const durationMs = tc ? Date.now() - tc.startTime : 0;
327
- onToolEnd?.(event.toolCallId, toolName, event.result, event.isError, durationMs, event.details);
328
- activeToolCallsRef.current = activeToolCallsRef.current.filter((t) => t.toolCallId !== event.toolCallId);
329
- setActiveToolCalls(activeToolCallsRef.current);
330
- break;
331
- }
332
- case "server_tool_call":
333
- onServerToolCall?.(event.id, event.name, event.input);
334
- break;
335
- case "server_tool_result":
336
- onServerToolResult?.(event.toolUseId, event.resultType, event.data);
337
- break;
338
- case "turn_end":
339
- onTurnEnd?.(event.turn, event.stopReason, event.usage);
340
- setCurrentTurn(event.turn);
341
- setTotalTokens((prev) => ({
342
- input: prev.input + event.usage.inputTokens,
343
- output: prev.output + event.usage.outputTokens,
344
- }));
345
- // Latest turn's input tokens = current context window fill
346
- // With prompt caching, input_tokens only counts non-cached tokens.
347
- // Total context = input + cache_read + cache_write.
348
- setContextUsed(event.usage.inputTokens +
349
- (event.usage.cacheRead ?? 0) +
350
- (event.usage.cacheWrite ?? 0));
351
- // Replace char-based estimate with real output tokens
352
- realTokensAccumRef.current += event.usage.outputTokens;
353
- charCountRef.current = 0;
354
- setStreamedTokenEstimate(realTokensAccumRef.current);
355
- // Reset phase for next turn
356
- phaseRef.current = "waiting";
357
- setActivityPhase("waiting");
358
- // Flush all pending text before completing turn
359
- flushAllText();
360
- if (textVisibleRef.current) {
361
- onTurnText?.(textVisibleRef.current, thinkingBufferRef.current, thinkingAccumRef.current);
362
- }
363
- // Reset streaming buffers for next turn
364
- textPendingRef.current = "";
365
- textVisibleRef.current = "";
366
- thinkingBufferRef.current = "";
367
- thinkingPendingRef.current = "";
368
- thinkingVisibleRef.current = "";
369
- setStreamingText("");
370
- setStreamingThinking("");
371
- break;
372
- case "agent_done":
373
- flushAllText();
374
- // Batch ALL completion state into a single render so Ink
375
- // processes the live-area change atomically. Previously
376
- // isRunning, activityPhase, and onDone landed in separate
377
- // render batches, causing multiple live-area height changes
378
- // that confused Ink's cursor math and clipped content.
379
- setIsRunning(false);
380
- phaseRef.current = "idle";
381
- setActivityPhase("idle");
382
- // Call onDone HERE (not in finally) so its state updates
383
- // (doneStatus, flushing items to Static) are batched too.
384
- onDone?.(Date.now() - runStartRef.current, [...toolsUsedRef.current]);
385
- doneCalledRef.current = true;
386
- break;
387
444
  }
388
445
  }
389
- }
390
- catch (err) {
391
- const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("aborted"));
392
- if (!isAbort) {
393
- throw err;
394
- }
395
- wasAborted = true;
396
- }
397
- finally {
398
- setIsRunning(false);
399
- abortRef.current = null;
400
- stopReveal();
401
- stopThinkingReveal();
402
- if (elapsedTimerRef.current) {
403
- clearInterval(elapsedTimerRef.current);
404
- elapsedTimerRef.current = null;
405
- }
406
- phaseRef.current = "idle";
407
- setActivityPhase("idle");
408
- if (wasAborted) {
409
- // Flush any visible streaming text so onAborted (which adds
410
- // "Request was stopped.") lands AFTER the agent's partial text
411
- // in liveItems — not above it.
412
- flushAllText();
413
- if (textVisibleRef.current) {
414
- onTurnText?.(textVisibleRef.current, thinkingBufferRef.current, thinkingAccumRef.current);
446
+ catch (err) {
447
+ if (!isAbortError(err)) {
448
+ throw err;
415
449
  }
416
- textPendingRef.current = "";
417
- textVisibleRef.current = "";
418
- thinkingBufferRef.current = "";
419
- thinkingPendingRef.current = "";
420
- thinkingVisibleRef.current = "";
421
- setStreamingText("");
422
- setStreamingThinking("");
423
- onAborted?.();
450
+ wasAborted = true;
424
451
  }
425
- else if (!doneCalledRef.current) {
426
- // Safety fallback normally agent_done calls onDone in-band
427
- const durationMs = Date.now() - runStartRef.current;
428
- onDone?.(durationMs, [...toolsUsedRef.current]);
452
+ finally {
453
+ // If the signal was aborted but the loop exited normally (e.g.
454
+ // agent_done fired right before the abort), treat it as aborted so
455
+ // the user sees "Request was stopped." instead of a duration verb.
456
+ if (!wasAborted && ac.signal.aborted) {
457
+ wasAborted = true;
458
+ }
459
+ setIsRunning(false);
460
+ abortRef.current = null;
461
+ stopReveal();
462
+ stopThinkingReveal();
463
+ if (elapsedTimerRef.current) {
464
+ clearInterval(elapsedTimerRef.current);
465
+ elapsedTimerRef.current = null;
466
+ }
467
+ phaseRef.current = "idle";
468
+ setActivityPhase("idle");
469
+ if (wasAborted) {
470
+ // Flush any visible streaming text so onAborted (which adds
471
+ // "Request was stopped.") lands AFTER the agent's partial text
472
+ // in liveItems — not above it.
473
+ flushAllText();
474
+ if (textVisibleRef.current) {
475
+ onTurnText?.(textVisibleRef.current, thinkingBufferRef.current, thinkingAccumRef.current);
476
+ }
477
+ textPendingRef.current = "";
478
+ textVisibleRef.current = "";
479
+ thinkingBufferRef.current = "";
480
+ thinkingPendingRef.current = "";
481
+ thinkingVisibleRef.current = "";
482
+ setStreamingText("");
483
+ setStreamingThinking("");
484
+ onAborted?.();
485
+ }
486
+ else if (!doneCalledRef.current) {
487
+ // Safety fallback — normally agent_done calls onDone in-band
488
+ const durationMs = Date.now() - runStartRef.current;
489
+ onDone?.(durationMs, [...toolsUsedRef.current]);
490
+ }
491
+ // Notify parent of new messages
492
+ const newMsgs = messages.current.slice(startIndex);
493
+ onComplete?.(newMsgs);
429
494
  }
430
- // Notify parent of new messages
431
- const newMsgs = messages.current.slice(startIndex);
432
- onComplete?.(newMsgs);
495
+ return wasAborted;
496
+ }; // end runSingle
497
+ // Run the initial message
498
+ const aborted = await runSingle(userContent);
499
+ // Drain the queue: process follow-up messages that arrived after agent_done.
500
+ // Most queued messages are consumed mid-run via getSteeringMessages, but
501
+ // messages that arrive after the agent finishes (no more tool calls to
502
+ // trigger steering) land here. Batch all remaining into a single run.
503
+ if (!aborted && queueRef.current.length > 0) {
504
+ const batch = queueRef.current.splice(0);
505
+ setQueuedCount(0);
506
+ const merged = mergeUserContent(batch);
507
+ // Let React process the onDone state updates before starting next run
508
+ await new Promise((r) => setTimeout(r, 100));
509
+ onQueuedStart?.(merged);
510
+ await runSingle(merged);
433
511
  }
434
512
  }, [
435
513
  messages,
@@ -444,6 +522,7 @@ export function useAgentLoop(messages, options, callbacks) {
444
522
  onTurnEnd,
445
523
  onDone,
446
524
  onAborted,
525
+ onQueuedStart,
447
526
  startReveal,
448
527
  stopReveal,
449
528
  startThinkingReveal,
@@ -466,6 +545,9 @@ export function useAgentLoop(messages, options, callbacks) {
466
545
  run,
467
546
  abort,
468
547
  reset,
548
+ queueMessage,
549
+ queuedCount,
550
+ clearQueue,
469
551
  isRunning,
470
552
  streamingText,
471
553
  streamingThinking,