@prestyj/agent 4.3.15 → 4.3.54

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/index.cjs CHANGED
@@ -51,18 +51,20 @@ function isAbortError(err) {
51
51
  }
52
52
  function isContextOverflow(err) {
53
53
  if (!(err instanceof Error)) return false;
54
+ if (isBillingError(err)) return false;
54
55
  const msg = err.message.toLowerCase();
55
56
  return msg.includes("prompt is too long") || msg.includes("context_length_exceeded") || msg.includes("maximum context length") || msg.includes("token") && msg.includes("exceed");
56
57
  }
57
58
  function isBillingError(err) {
58
59
  if (!(err instanceof Error)) return false;
59
60
  const msg = err.message.toLowerCase();
60
- return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge") || msg.includes("subscription plan") || msg.includes("does not yet include access");
61
+ return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge") || msg.includes("subscription plan") || msg.includes("does not yet include access") || msg.includes("token quota") || msg.includes("exceeded_current_quota_error") || msg.includes("check your account balance");
61
62
  }
62
63
  function isToolPairingError(err) {
63
64
  if (!(err instanceof Error)) return false;
64
65
  const msg = err.message.toLowerCase();
65
- return msg.includes("tool_use") && msg.includes("tool_result") || msg.includes("unexpected `tool_use_id`") || msg.includes("tool_use ids found without");
66
+ return msg.includes("tool_use") && msg.includes("tool_result") || msg.includes("unexpected `tool_use_id`") || msg.includes("tool_use ids found without") || // Moonshot/OpenAI-compatible: "tool call id <id> is not found"
67
+ msg.includes("tool call id") && msg.includes("is not found");
66
68
  }
67
69
  function isOverloaded(err) {
68
70
  if (!(err instanceof Error)) return false;
@@ -70,6 +72,31 @@ function isOverloaded(err) {
70
72
  const msg = err.message.toLowerCase();
71
73
  return msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("529");
72
74
  }
75
+ function isMalformedStream(err) {
76
+ if (!(err instanceof Error)) return false;
77
+ if (err.name === "SyntaxError") return true;
78
+ const cause = err.cause;
79
+ if (cause instanceof Error && cause.name === "SyntaxError") return true;
80
+ const msg = err.message;
81
+ return /\bin JSON at position \d+/i.test(msg);
82
+ }
83
+ function abortableSleep(ms, signal) {
84
+ if (signal?.aborted) {
85
+ return Promise.reject(new DOMException("Aborted", "AbortError"));
86
+ }
87
+ return new Promise((resolve, reject) => {
88
+ let onAbort = null;
89
+ const timer = setTimeout(() => {
90
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
91
+ resolve();
92
+ }, ms);
93
+ onAbort = () => {
94
+ clearTimeout(timer);
95
+ reject(new DOMException("Aborted", "AbortError"));
96
+ };
97
+ signal?.addEventListener("abort", onAbort, { once: true });
98
+ });
99
+ }
73
100
  async function* agentLoop(messages, options) {
74
101
  const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
75
102
  const maxContinuations = options.maxContinuations ?? 5;
@@ -79,14 +106,14 @@ async function* agentLoop(messages, options) {
79
106
  let firstTurn = true;
80
107
  let consecutivePauses = 0;
81
108
  let toolPairingRepaired = false;
82
- let overflowRetries = 0;
83
109
  let overloadRetries = 0;
84
110
  let emptyResponseRetries = 0;
85
111
  let stallRetries = 0;
86
- const MAX_OVERFLOW_RETRIES = 3;
112
+ let useNonStreamingFallback = false;
87
113
  const MAX_OVERLOAD_RETRIES = 10;
88
114
  const MAX_EMPTY_RESPONSE_RETRIES = 2;
89
115
  const MAX_STALL_RETRIES = 5;
116
+ const STALL_RETRIES_BEFORE_NON_STREAMING = 2;
90
117
  const STALL_DELAY_MS = 1e3;
91
118
  const OVERLOAD_BASE_DELAY_MS = 2e3;
92
119
  const OVERLOAD_MAX_DELAY_MS = 3e4;
@@ -96,6 +123,7 @@ async function* agentLoop(messages, options) {
96
123
  const STREAM_OUTPUT_HARD_TIMEOUT_MS = 3e5;
97
124
  const STREAM_THINKING_IDLE_TIMEOUT_MS = 3e5;
98
125
  const STREAM_THINKING_HARD_TIMEOUT_MS = 6e5;
126
+ const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
99
127
  try {
100
128
  while (turn < maxTurns) {
101
129
  options.signal?.throwIfAborted();
@@ -158,6 +186,7 @@ async function* agentLoop(messages, options) {
158
186
  let hasReceivedEvent = false;
159
187
  let hasReceivedThinking = false;
160
188
  const resetIdleTimer = () => {
189
+ if (useNonStreamingFallback) return;
161
190
  if (idleTimer) clearTimeout(idleTimer);
162
191
  const timeoutMs = hasReceivedEvent ? STREAM_IDLE_TIMEOUT_MS : hasReceivedThinking ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
163
192
  idleTimer = setTimeout(() => {
@@ -173,16 +202,17 @@ async function* agentLoop(messages, options) {
173
202
  streamController.abort();
174
203
  }, timeoutMs);
175
204
  };
176
- let hardTimeoutMs = STREAM_HARD_TIMEOUT_MS;
205
+ let hardTimeoutMs = useNonStreamingFallback ? NON_STREAMING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
177
206
  hardTimer = setTimeout(() => {
178
207
  diag("hard_timeout_fired", {
179
- events: typeof streamEventCount !== "undefined" ? streamEventCount : 0
208
+ events: typeof streamEventCount !== "undefined" ? streamEventCount : 0,
209
+ nonStreaming: useNonStreamingFallback
180
210
  });
181
211
  idleTimedOut = true;
182
212
  streamController.abort();
183
213
  }, hardTimeoutMs);
184
214
  try {
185
- diag("stream_call");
215
+ diag("stream_call", { nonStreaming: useNonStreamingFallback });
186
216
  streamCallStart = Date.now();
187
217
  const result = (0, import_ai.stream)({
188
218
  provider: options.provider,
@@ -199,8 +229,11 @@ async function* agentLoop(messages, options) {
199
229
  signal: streamController.signal,
200
230
  accountId: options.accountId,
201
231
  cacheRetention: options.cacheRetention,
232
+ supportsImages: options.supportsImages,
202
233
  compaction: options.compaction,
203
- clearToolUses: options.clearToolUses
234
+ clearToolUses: options.clearToolUses,
235
+ // Flip to non-streaming fallback after repeated stream stalls.
236
+ ...useNonStreamingFallback ? { streaming: false } : {}
204
237
  });
205
238
  diag("stream_created", { setupMs: Date.now() - streamCallStart });
206
239
  result.response.catch(() => {
@@ -298,27 +331,9 @@ async function* agentLoop(messages, options) {
298
331
  provider: options.provider,
299
332
  model: options.model
300
333
  });
301
- if (overflowRetries < MAX_OVERFLOW_RETRIES && isContextOverflow(err) && options.transformContext) {
302
- overflowRetries++;
303
- diag("retry", {
304
- reason: "context_overflow",
305
- attempt: overflowRetries,
306
- maxAttempts: MAX_OVERFLOW_RETRIES
307
- });
308
- yield {
309
- type: "retry",
310
- reason: "context_overflow",
311
- attempt: overflowRetries,
312
- maxAttempts: MAX_OVERFLOW_RETRIES,
313
- delayMs: 0
314
- };
315
- const transformed = await options.transformContext(messages, { force: true });
316
- if (transformed !== messages) {
317
- messages.length = 0;
318
- messages.push(...transformed);
319
- }
320
- turn--;
321
- continue;
334
+ if (isContextOverflow(err)) {
335
+ yield { type: "error", error: err instanceof Error ? err : new Error(errMsg) };
336
+ throw err;
322
337
  }
323
338
  if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {
324
339
  overloadRetries++;
@@ -339,19 +354,31 @@ async function* agentLoop(messages, options) {
339
354
  maxAttempts: MAX_OVERLOAD_RETRIES,
340
355
  delayMs
341
356
  };
342
- await new Promise((r) => setTimeout(r, delayMs));
357
+ await abortableSleep(delayMs, options.signal);
343
358
  turn--;
344
359
  continue;
345
360
  }
346
- if (idleTimedOut && !options.signal?.aborted && stallRetries < MAX_STALL_RETRIES) {
361
+ const malformed = isMalformedStream(err);
362
+ const transportFailure = (idleTimedOut || malformed) && !options.signal?.aborted;
363
+ if (transportFailure && stallRetries < MAX_STALL_RETRIES) {
347
364
  stallRetries++;
365
+ if (!useNonStreamingFallback && stallRetries >= STALL_RETRIES_BEFORE_NON_STREAMING) {
366
+ useNonStreamingFallback = true;
367
+ diag("non_streaming_fallback_enabled", {
368
+ stallRetries,
369
+ provider: options.provider,
370
+ model: options.model,
371
+ cause: malformed ? "malformed_stream" : "stream_stall"
372
+ });
373
+ }
348
374
  const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8e3);
349
375
  diag("retry", {
350
- reason: "stream_stall",
376
+ reason: malformed ? "malformed_stream" : "stream_stall",
351
377
  attempt: stallRetries,
352
378
  maxAttempts: MAX_STALL_RETRIES,
353
379
  delayMs,
354
- events: streamEventCount
380
+ events: streamEventCount,
381
+ nonStreaming: useNonStreamingFallback
355
382
  });
356
383
  yield {
357
384
  type: "retry",
@@ -361,11 +388,11 @@ async function* agentLoop(messages, options) {
361
388
  delayMs,
362
389
  silent: stallRetries <= 2
363
390
  };
364
- await new Promise((r) => setTimeout(r, delayMs));
391
+ await abortableSleep(delayMs, options.signal);
365
392
  turn--;
366
393
  continue;
367
394
  }
368
- if (idleTimedOut && !options.signal?.aborted) {
395
+ if (transportFailure) {
369
396
  diag("stall_exhausted", {
370
397
  stallRetries: MAX_STALL_RETRIES,
371
398
  provider: options.provider,
@@ -402,7 +429,6 @@ async function* agentLoop(messages, options) {
402
429
  if (hardTimer) clearTimeout(hardTimer);
403
430
  options.signal?.removeEventListener("abort", forwardAbort);
404
431
  }
405
- overflowRetries = 0;
406
432
  overloadRetries = 0;
407
433
  stallRetries = 0;
408
434
  const contentArr = Array.isArray(response.message.content) ? response.message.content : null;
@@ -432,6 +458,7 @@ async function* agentLoop(messages, options) {
432
458
  }
433
459
  }
434
460
  emptyResponseRetries = 0;
461
+ useNonStreamingFallback = false;
435
462
  totalUsage.inputTokens += response.usage.inputTokens;
436
463
  totalUsage.outputTokens += response.usage.outputTokens;
437
464
  if (response.usage.cacheRead) {
@@ -544,7 +571,7 @@ async function* agentLoop(messages, options) {
544
571
  eventStream.push({
545
572
  type: "tool_call_end",
546
573
  toolCallId: toolCall.id,
547
- result: resultContent,
574
+ result: toolResultPreview(resultContent),
548
575
  details,
549
576
  isError,
550
577
  durationMs
@@ -597,7 +624,7 @@ async function* agentLoop(messages, options) {
597
624
  const HARD_MAX = 4e5;
598
625
  const max = Math.min(options.maxToolResultChars, HARD_MAX);
599
626
  for (const tr of toolResults) {
600
- if (tr.content.length > max) {
627
+ if (typeof tr.content === "string" && tr.content.length > max) {
601
628
  const headChars = Math.floor(max * 0.7);
602
629
  const tailChars = max - headChars;
603
630
  const head = tr.content.slice(0, headChars);
@@ -648,6 +675,10 @@ async function* agentLoop(messages, options) {
648
675
  function normalizeToolResult(raw) {
649
676
  return typeof raw === "string" ? { content: raw } : raw;
650
677
  }
678
+ function toolResultPreview(content) {
679
+ if (typeof content === "string") return content;
680
+ return content.map((block) => block.type === "text" ? block.text : `[image ${block.mediaType}]`).join("\n");
681
+ }
651
682
  function extractToolCalls(content) {
652
683
  if (typeof content === "string") return [];
653
684
  return content.filter((part) => part.type === "tool_call");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["// Core\nexport { Agent, AgentStream } from \"./agent.js\";\nexport {\n agentLoop,\n isAbortError,\n isContextOverflow,\n isBillingError,\n setStreamDiagnostic,\n} from \"./agent-loop.js\";\nexport type { StreamDiagnosticFn } from \"./agent-loop.js\";\n\n// Types\nexport type {\n StructuredToolResult,\n ToolExecuteResult,\n ToolContext,\n AgentTool,\n AgentTextDeltaEvent,\n AgentThinkingDeltaEvent,\n AgentToolCallStartEvent,\n AgentToolCallUpdateEvent,\n AgentToolCallEndEvent,\n AgentToolCallDeltaEvent,\n AgentServerToolCallEvent,\n AgentServerToolResultEvent,\n AgentSteeringMessageEvent,\n AgentFollowUpMessageEvent,\n AgentRetryEvent,\n AgentTurnEndEvent,\n AgentDoneEvent,\n AgentErrorEvent,\n AgentEvent,\n AgentOptions,\n AgentResult,\n} from \"./types.js\";\n","import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n private steeringQueue: Message[] = [];\n private followUpQueue: Message[] = [];\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Queue a steering message for injection after current tool execution completes. */\n steer(msg: Message): void {\n this.steeringQueue.push(msg);\n }\n\n /** Queue a follow-up message for injection when the agent would otherwise stop. */\n followUp(msg: Message): void {\n this.followUpQueue.push(msg);\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const optionsWithQueues: AgentOptions = {\n ...this.options,\n getSteeringMessages: async () => {\n const callerResult = (await this.options.getSteeringMessages?.()) ?? [];\n const queued = this.steeringQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n getFollowUpMessages: async () => {\n const callerResult = (await this.options.getFollowUpMessages?.()) ?? [];\n const queued = this.followUpQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n };\n\n const generator = agentLoop(this.messages, optionsWithQueues);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 200;\n\n/**\n * Lightweight stream diagnostic callback. When set, the agent loop calls this\n * at every phase boundary with timing and state info. This lets the hosting\n * app (ezcoder, come-alive, etc.) log stall diagnostics without the agent\n * package needing fs/process dependencies.\n */\nexport type StreamDiagnosticFn = (phase: string, data?: Record<string, unknown>) => void;\n\n/** Global diagnostic hook — set by the hosting app before calling agentLoop. */\nlet _diagFn: StreamDiagnosticFn | null = null;\n\n/** Register a diagnostic callback for stream stall tracing. */\nexport function setStreamDiagnostic(fn: StreamDiagnosticFn | null): void {\n _diagFn = fn;\n}\n\nfunction diag(phase: string, data?: Record<string, unknown>): void {\n _diagFn?.(phase, data);\n}\n\n/**\n * Detect abort errors — user-initiated cancellation or AbortSignal.\n * These should be caught and handled gracefully, not re-thrown.\n */\nexport function isAbortError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"AbortError\") return true;\n const msg = err.message.toLowerCase();\n return msg.includes(\"aborted\") || msg.includes(\"abort\");\n}\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect billing/quota errors — these should NOT be retried.\n * GLM returns HTTP 429 with \"Insufficient balance\" for quota exhaustion.\n */\nexport function isBillingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"no resource package\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\") ||\n msg.includes(\"recharge\") ||\n msg.includes(\"subscription plan\") ||\n msg.includes(\"does not yet include access\")\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n * Excludes billing/quota errors which won't resolve with a retry.\n */\n/**\n * Detect tool pairing errors — orphaned tool_use or tool_result blocks.\n * These are 400 errors that can be recovered by repairing the message history.\n */\nexport function isToolPairingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n (msg.includes(\"tool_use\") && msg.includes(\"tool_result\")) ||\n msg.includes(\"unexpected `tool_use_id`\") ||\n msg.includes(\"tool_use ids found without\")\n );\n}\n\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let firstTurn = true;\n let consecutivePauses = 0;\n let toolPairingRepaired = false;\n let overflowRetries = 0;\n let overloadRetries = 0;\n let emptyResponseRetries = 0;\n let stallRetries = 0;\n const MAX_OVERFLOW_RETRIES = 3;\n const MAX_OVERLOAD_RETRIES = 10;\n const MAX_EMPTY_RESPONSE_RETRIES = 2;\n const MAX_STALL_RETRIES = 5;\n const STALL_DELAY_MS = 1_000; // Brief pause before retry — just enough to avoid tight loops\n const OVERLOAD_BASE_DELAY_MS = 2_000;\n const OVERLOAD_MAX_DELAY_MS = 30_000;\n const STREAM_FIRST_EVENT_TIMEOUT_MS = 45_000; // 45s to get first event (Opus thinks long)\n const STREAM_IDLE_TIMEOUT_MS = 30_000; // 30s between events once streaming starts\n // Anthropic models can pause 10-20s mid-stream while computing the next chunk\n // (e.g. generating tool call args for a large write). 10s was too aggressive\n // and caused false \"stream stalled\" errors, especially in plan mode.\n const STREAM_HARD_TIMEOUT_MS = 90_000; // 90s absolute cap before output starts\n // Once output events (text_delta) are actively streaming, extend the hard\n // timeout — long responses (plan mode, detailed explanations) can legitimately\n // take 2-3+ minutes while events flow continuously.\n const STREAM_OUTPUT_HARD_TIMEOUT_MS = 300_000; // 5min hard cap once output is flowing\n // Reasoning models (MiMo) can pause 3-5 minutes between thinking and output\n // generation. Once we've seen thinking events, extend timeouts significantly.\n const STREAM_THINKING_IDLE_TIMEOUT_MS = 300_000; // 5min idle after thinking\n const STREAM_THINKING_HARD_TIMEOUT_MS = 600_000; // 10min hard cap with thinking\n\n try {\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // Estimate message payload size for diagnostics\n let msgChars = 0;\n for (const m of messages) {\n if (typeof m.content === \"string\") msgChars += m.content.length;\n else if (Array.isArray(m.content)) {\n for (const p of m.content) {\n if (\"text\" in p && typeof p.text === \"string\") msgChars += p.text.length;\n if (\"content\" in p && typeof p.content === \"string\") msgChars += p.content.length;\n }\n }\n }\n diag(\"turn_start\", {\n turn,\n messages: messages.length,\n chars: msgChars,\n provider: options.provider,\n model: options.model,\n });\n\n // ── Initial steering poll: catch messages queued before the first LLM call ──\n if (firstTurn && options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n firstTurn = false;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n diag(\"transform_start\");\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n diag(\"transform_compacted\", {\n before: messages.length,\n after: transformed.length,\n });\n messages.length = 0;\n messages.push(...transformed);\n }\n diag(\"transform_end\");\n }\n\n // ── Repair tool pairing: ensure every tool_use has an adjacent tool_result ──\n repairToolPairingAdjacent(messages);\n\n // ── Call LLM with overflow recovery ──\n let response;\n // Per-attempt abort controller: allows idle timeout to abort the stream\n // without affecting the caller's signal. The caller's abort is forwarded.\n const streamController = new AbortController();\n let idleTimer: ReturnType<typeof setTimeout> | null = null;\n let hardTimer: ReturnType<typeof setTimeout> | null = null;\n let idleTimedOut = false;\n\n // Stream event counters — declared here so timeout callbacks can access them\n let streamEventCount = 0;\n let lastEventTime = Date.now();\n let streamCallStart = Date.now();\n // Track event types for diagnostics — shows what arrived before a stall\n const eventTypeCounts: Record<string, number> = {};\n let lastEventType = \"\";\n // Track consumer processing time — helps distinguish \"API stopped sending\"\n // from \"our consumer was slow to pull the next event\"\n let lastYieldEndTime = Date.now();\n let maxConsumerLagMs = 0;\n\n // Forward caller abort to the per-attempt controller\n const forwardAbort = () => streamController.abort();\n options.signal?.addEventListener(\"abort\", forwardAbort, { once: true });\n\n // Three-phase idle timeout:\n // - Before first event: STREAM_FIRST_EVENT_TIMEOUT_MS (45s) — Opus can\n // take 30s+ to start on large contexts, that's not a stall.\n // - After output event (text_delta, server_toolcall): STREAM_IDLE_TIMEOUT_MS\n // (10s) — once output is streaming, 10s of silence is dead. Retry fast.\n // - After thinking events only: STREAM_THINKING_IDLE_TIMEOUT_MS (5min) —\n // reasoning models (MiMo) can pause minutes between thinking and output.\n let hasReceivedEvent = false;\n let hasReceivedThinking = false;\n const resetIdleTimer = () => {\n if (idleTimer) clearTimeout(idleTimer);\n const timeoutMs = hasReceivedEvent\n ? STREAM_IDLE_TIMEOUT_MS\n : hasReceivedThinking\n ? STREAM_THINKING_IDLE_TIMEOUT_MS\n : STREAM_FIRST_EVENT_TIMEOUT_MS;\n idleTimer = setTimeout(() => {\n diag(\"idle_timeout_fired\", {\n events: streamEventCount,\n sinceLastEventMs: Date.now() - lastEventTime,\n lastEventType,\n maxConsumerLagMs,\n phase: hasReceivedEvent\n ? \"mid_stream\"\n : hasReceivedThinking\n ? \"post_thinking\"\n : \"first_event\",\n eventTypes: eventTypeCounts,\n });\n idleTimedOut = true;\n streamController.abort();\n }, timeoutMs);\n };\n\n // Hard timeout: absolute cap per LLM call. Safety net for streams that\n // keep sending sparse events (e.g. keep-alive pings) but never complete.\n // Extended dynamically when thinking events arrive (see thinking_delta handler).\n let hardTimeoutMs = STREAM_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", {\n events: typeof streamEventCount !== \"undefined\" ? streamEventCount : 0,\n });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n\n try {\n diag(\"stream_call\");\n streamCallStart = Date.now();\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: streamController.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n compaction: options.compaction,\n clearToolUses: options.clearToolUses,\n });\n diag(\"stream_created\", { setupMs: Date.now() - streamCallStart });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas — reset idle timer on each event\n streamEventCount = 0;\n hasReceivedEvent = false;\n lastEventTime = Date.now();\n streamCallStart = Date.now();\n resetIdleTimer();\n for await (const event of result) {\n // Measure consumer lag: time between finishing previous yield and\n // receiving this event. High lag means React rendering is starving\n // the stream consumer. Low lag means the API was slow to send.\n const pullTime = Date.now();\n const consumerLag = pullTime - lastYieldEndTime;\n if (consumerLag > maxConsumerLagMs) maxConsumerLagMs = consumerLag;\n\n streamEventCount++;\n eventTypeCounts[event.type] = (eventTypeCounts[event.type] ?? 0) + 1;\n lastEventType = event.type;\n\n // Flip to mid-stream timeout on confirmed output events — text\n // deltas, completed tool calls, and tool call deltas (large file\n // writes can stream toolcall_delta for minutes without any text_delta).\n // Reasoning models (MiMo) are handled separately below — they can\n // stream hundreds of thinking events then pause minutes before output.\n if (\n (event.type === \"text_delta\" ||\n event.type === \"server_toolcall\" ||\n event.type === \"toolcall_delta\") &&\n !hasReceivedEvent\n ) {\n hasReceivedEvent = true;\n // Extend hard timeout now that output is actively streaming.\n // Long responses (plan mode, detailed code) can exceed 90s while\n // events flow continuously — the idle timeout (10s) catches real stalls.\n if (hardTimer && hardTimeoutMs < STREAM_OUTPUT_HARD_TIMEOUT_MS) {\n clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_OUTPUT_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n }\n // Track thinking events — extends idle timeout and hard timeout\n // so reasoning models aren't killed during thinking→output transition.\n if (event.type === \"thinking_delta\" && !hasReceivedThinking) {\n hasReceivedThinking = true;\n // Extend the hard timeout now that we know the model is reasoning\n if (hardTimer) clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_THINKING_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n\n const now = Date.now();\n const gap = now - lastEventTime;\n // Log first event and any suspiciously long gaps\n if (streamEventCount === 1) {\n diag(\"first_event\", { type: event.type, ttfMs: now - streamCallStart });\n } else if (gap > 3000) {\n diag(\"slow_gap\", {\n type: event.type,\n gapMs: gap,\n eventNum: streamEventCount,\n sinceStartMs: now - streamCallStart,\n });\n }\n lastEventTime = now;\n resetIdleTimer();\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n } else if (event.type === \"toolcall_delta\") {\n yield {\n type: \"toolcall_delta\" as const,\n chars: event.argsJson?.length ?? 0,\n };\n }\n lastYieldEndTime = Date.now();\n }\n\n diag(\"stream_done\", {\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n maxConsumerLagMs,\n eventTypes: eventTypeCounts,\n });\n response = await result.response;\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n diag(\"stream_error\", {\n error: errMsg.slice(0, 200),\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n idleTimedOut,\n aborted: !!options.signal?.aborted,\n eventTypes: eventTypeCounts,\n provider: options.provider,\n model: options.model,\n });\n // Context overflow: force-compact via transformContext and retry (up to 3 times)\n if (\n overflowRetries < MAX_OVERFLOW_RETRIES &&\n isContextOverflow(err) &&\n options.transformContext\n ) {\n overflowRetries++;\n diag(\"retry\", {\n reason: \"context_overflow\",\n attempt: overflowRetries,\n maxAttempts: MAX_OVERFLOW_RETRIES,\n });\n yield {\n type: \"retry\" as const,\n reason: \"context_overflow\" as const,\n attempt: overflowRetries,\n maxAttempts: MAX_OVERFLOW_RETRIES,\n delayMs: 0,\n };\n const transformed = await options.transformContext(messages, { force: true });\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n turn--; // Don't count the failed turn\n continue;\n }\n // Overloaded / rate-limited: exponential backoff, retry up to 10 times\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n const delayMs = Math.min(\n OVERLOAD_BASE_DELAY_MS * 2 ** (overloadRetries - 1),\n OVERLOAD_MAX_DELAY_MS,\n );\n diag(\"retry\", {\n reason: \"overloaded\",\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n });\n yield {\n type: \"retry\" as const,\n reason: \"overloaded\" as const,\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n };\n await new Promise((r) => setTimeout(r, delayMs));\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall: the API connection hung without closing.\n // Retry with exponential backoff — most stalls are transient server-side\n // issues, not payload-dependent. First 2 retries are silent (hidden retries).\n if (idleTimedOut && !options.signal?.aborted && stallRetries < MAX_STALL_RETRIES) {\n stallRetries++;\n const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8_000);\n diag(\"retry\", {\n reason: \"stream_stall\",\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n events: streamEventCount,\n });\n yield {\n type: \"retry\" as const,\n reason: \"stream_stall\" as const,\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n silent: stallRetries <= 2,\n };\n await new Promise((r) => setTimeout(r, delayMs));\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall retries exhausted — surface a clear error so the UI\n // can distinguish \"gave up after stalls\" from \"completed normally\".\n if (idleTimedOut && !options.signal?.aborted) {\n diag(\"stall_exhausted\", {\n stallRetries: MAX_STALL_RETRIES,\n provider: options.provider,\n model: options.model,\n });\n yield {\n type: \"error\" as const,\n error: new Error(\n `The API provider's stream stalled ${MAX_STALL_RETRIES} times — the provider may be experiencing capacity issues. ` +\n `Your conversation is preserved. Send another message to retry.`,\n ),\n };\n break;\n }\n // Tool pairing 400: orphaned tool_result or tool_use in message history.\n // Run repair and retry once — if repair can't fix it, surface the error.\n if (isToolPairingError(err) && !toolPairingRepaired) {\n toolPairingRepaired = true;\n diag(\"tool_pairing_repair\", { error: errMsg.slice(0, 200) });\n repairToolPairingAdjacent(messages);\n turn--;\n continue;\n }\n // Abort errors (user cancellation) — exit loop cleanly instead of\n // crashing the process with an unhandled rejection.\n if (isAbortError(err) || options.signal?.aborted) {\n diag(\"aborted\", { turn, provider: options.provider, model: options.model });\n break;\n }\n // Unhandled error — log before throwing so the crash is traceable\n diag(\"unhandled_error\", {\n error: errMsg.slice(0, 500),\n turn,\n provider: options.provider,\n model: options.model,\n });\n throw err;\n } finally {\n if (idleTimer) clearTimeout(idleTimer);\n if (hardTimer) clearTimeout(hardTimer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n // Reset retry counters after successful call\n overflowRetries = 0;\n overloadRetries = 0;\n stallRetries = 0;\n\n // Detect empty/degenerate responses — the API occasionally returns 0 tokens\n // with no content, or \"thinks\" without producing actionable output.\n // Reasoning models (MiMo, DeepSeek) may report outputTokens > 0 from\n // thinking alone while producing no text or tool calls — still a dud.\n const contentArr = Array.isArray(response.message.content) ? response.message.content : null;\n const hasActionableContent =\n response.message.content !== \"\" &&\n contentArr !== null &&\n contentArr.some(\n (p) => p.type === \"text\" || p.type === \"tool_call\" || p.type === \"server_tool_call\",\n );\n if (!hasActionableContent) {\n if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {\n emptyResponseRetries++;\n diag(\"retry\", {\n reason: \"empty_response\",\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n provider: options.provider,\n model: options.model,\n contentTypes: contentArr?.map((p) => p.type).join(\",\") ?? \"empty\",\n });\n yield {\n type: \"retry\" as const,\n reason: \"empty_response\" as const,\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n delayMs: 0,\n };\n turn--; // Don't count the failed turn\n continue;\n }\n // Exhausted retries — fall through and let the agent finish\n }\n emptyResponseRetries = 0;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n\n // If no tool calls to execute, check for steering messages before stopping.\n // Check content (not just stopReason) because some providers (e.g. GLM)\n // return finish_reason=\"stop\" even when tool calls are present.\n if (response.stopReason !== \"tool_use\" && allToolCalls.length === 0) {\n // Check for queued steering messages — if present, inject and continue\n // the loop instead of returning (follow-up pattern).\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue; // Next iteration will call LLM with injected messages\n }\n }\n // Follow-up: lower priority than steering — only when agent would otherwise stop.\n if (options.getFollowUpMessages) {\n const followUp = await options.getFollowUpMessages();\n if (followUp && followUp.length > 0) {\n for (const msg of followUp) {\n yield { type: \"follow_up_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue;\n }\n }\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: string;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: resultContent,\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n const resultsMap = new Map(results.map((r) => [r.toolCallId, r]));\n for (const tc of toolCalls) {\n const r = resultsMap.get(tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n let toolsAborted = false;\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } catch (err) {\n // Tool event stream aborted (Ctrl+C) — don't propagate, just mark\n // so the finally block can clean up and the loop can exit.\n if (isAbortError(err) || options.signal?.aborted) {\n toolsAborted = true;\n } else {\n throw err;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n const resolvedIds = new Set(toolResults.map((r) => r.toolCallId));\n for (const tc of toolCalls) {\n if (!resolvedIds.has(tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n // Guard: cap oversized tool results before they enter conversation history.\n // Uses head+tail strategy to preserve error messages / closing structure at the end.\n if (options.maxToolResultChars) {\n const HARD_MAX = 400_000; // absolute ceiling regardless of context window\n const max = Math.min(options.maxToolResultChars, HARD_MAX);\n for (const tr of toolResults) {\n if (tr.content.length > max) {\n // Keep 70% head + 30% tail to preserve errors/diagnostics at the end\n const headChars = Math.floor(max * 0.7);\n const tailChars = max - headChars;\n const head = tr.content.slice(0, headChars);\n const tail = tr.content.slice(-tailChars);\n const omitted = tr.content.length - headChars - tailChars;\n tr.content = head + `\\n\\n[... ${omitted} characters omitted ...]\\n\\n` + tail;\n }\n }\n }\n\n messages.push({ role: \"tool\", content: toolResults });\n }\n\n // Exit loop after cleaning up aborted tools\n if (toolsAborted) break;\n\n // ── Steering messages: inject user messages queued during tool execution ──\n // Polled after tools complete so the next LLM call sees them in context.\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n }\n } finally {\n // Sanitize orphaned server_tool_use blocks on abort.\n // When a stream is aborted mid-server-tool (e.g. web_search), the\n // assistant message containing the server_tool_use may already be in\n // the messages array, but the corresponding web_search_tool_result\n // never arrived. The API rejects the next request with a 400 if it\n // finds an unmatched server_tool_use, so we strip it here.\n sanitizeOrphanedServerTools(messages);\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n\n/**\n * Remove orphaned server_tool_use blocks from the last assistant message.\n * When a stream is aborted mid-server-tool (e.g. web_search), the assistant\n * message may contain a server_tool_call without a matching server_tool_result.\n * The API rejects the next request if these are unmatched.\n */\nfunction sanitizeOrphanedServerTools(messages: Message[]): void {\n // Find the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) break;\n\n // Collect server_tool_call ids and matched server_tool_result ids\n const serverToolIds = new Set<string>();\n const resultToolIds = new Set<string>();\n for (const part of msg.content) {\n if (part.type === \"server_tool_call\") serverToolIds.add(part.id);\n if (part.type === \"server_tool_result\") resultToolIds.add(part.toolUseId);\n }\n\n // Find unmatched server_tool_call blocks\n const orphanedIds = new Set<string>();\n for (const id of serverToolIds) {\n if (!resultToolIds.has(id)) orphanedIds.add(id);\n }\n\n if (orphanedIds.size === 0) break;\n\n // Strip orphaned server_tool_call blocks from the content\n const filtered = msg.content.filter(\n (part) => !(part.type === \"server_tool_call\" && orphanedIds.has(part.id)),\n );\n\n if (filtered.length === 0) {\n // Nothing left — remove the entire message\n messages.splice(i, 1);\n } else {\n (msg as { content: ContentPart[] }).content = filtered;\n }\n break;\n }\n}\n\n/**\n * Ensure every assistant message with tool_call blocks is immediately followed\n * by a tool message with matching tool_result entries. This prevents Anthropic\n * API 400 errors (\"tool_use ids found without tool_result blocks immediately\n * after\") that can occur after compaction, session restore, or abort recovery.\n *\n * Repairs in-place by inserting synthetic tool_result messages where needed.\n */\nfunction repairToolPairingAdjacent(messages: Message[]): void {\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) continue;\n\n const toolCallIds = (msg.content as ContentPart[])\n .filter((p) => p.type === \"tool_call\")\n .map((p) => (p as ContentPart & { type: \"tool_call\"; id: string }).id);\n if (toolCallIds.length === 0) continue;\n\n const next = messages[i + 1];\n if (next?.role === \"tool\" && Array.isArray(next.content)) {\n // Tool message exists — check for missing results\n const existingIds = new Set((next.content as ToolResult[]).map((r) => r.toolCallId));\n const missing = toolCallIds.filter((id) => !existingIds.has(id));\n if (missing.length > 0) {\n for (const id of missing) {\n (next.content as ToolResult[]).push({\n type: \"tool_result\",\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n });\n }\n }\n } else {\n // No tool message follows — insert a synthetic one\n messages.splice(i + 1, 0, {\n role: \"tool\" as const,\n content: toolCallIds.map((id) => ({\n type: \"tool_result\" as const,\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n })),\n });\n }\n }\n\n // Reverse repair: strip tool_result entries whose tool_use_id has no matching\n // tool_call in the preceding assistant message. This can happen when compaction\n // or stall recovery removes an assistant message but leaves its tool_result behind.\n const toolCallIdSet = new Set<string>();\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n for (const p of msg.content as ContentPart[]) {\n if (p.type === \"tool_call\") toolCallIdSet.add((p as ToolCall).id);\n }\n }\n if (msg.role === \"tool\" && Array.isArray(msg.content)) {\n const results = msg.content as ToolResult[];\n const filtered = results.filter((r) => toolCallIdSet.has(r.toolCallId));\n if (filtered.length === 0) {\n // Entire tool message is orphaned — remove it\n messages.splice(i, 1);\n i--;\n } else if (filtered.length < results.length) {\n (msg as { content: ToolResult[] }).content = filtered;\n }\n }\n }\n}\n"],"mappings":"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","names":["import_ai"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["// Core\nexport { Agent, AgentStream } from \"./agent.js\";\nexport {\n agentLoop,\n isAbortError,\n isContextOverflow,\n isBillingError,\n setStreamDiagnostic,\n} from \"./agent-loop.js\";\nexport type { StreamDiagnosticFn } from \"./agent-loop.js\";\n\n// Types\nexport type {\n StructuredToolResult,\n ToolExecuteResult,\n ToolContext,\n AgentTool,\n AgentTextDeltaEvent,\n AgentThinkingDeltaEvent,\n AgentToolCallStartEvent,\n AgentToolCallUpdateEvent,\n AgentToolCallEndEvent,\n AgentToolCallDeltaEvent,\n AgentServerToolCallEvent,\n AgentServerToolResultEvent,\n AgentSteeringMessageEvent,\n AgentFollowUpMessageEvent,\n AgentRetryEvent,\n AgentTurnEndEvent,\n AgentDoneEvent,\n AgentErrorEvent,\n AgentEvent,\n AgentOptions,\n AgentResult,\n} from \"./types.js\";\n","import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n private steeringQueue: Message[] = [];\n private followUpQueue: Message[] = [];\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Queue a steering message for injection after current tool execution completes. */\n steer(msg: Message): void {\n this.steeringQueue.push(msg);\n }\n\n /** Queue a follow-up message for injection when the agent would otherwise stop. */\n followUp(msg: Message): void {\n this.followUpQueue.push(msg);\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const optionsWithQueues: AgentOptions = {\n ...this.options,\n getSteeringMessages: async () => {\n const callerResult = (await this.options.getSteeringMessages?.()) ?? [];\n const queued = this.steeringQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n getFollowUpMessages: async () => {\n const callerResult = (await this.options.getFollowUpMessages?.()) ?? [];\n const queued = this.followUpQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n };\n\n const generator = agentLoop(this.messages, optionsWithQueues);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type ToolResultContent,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 200;\n\n/**\n * Lightweight stream diagnostic callback. When set, the agent loop calls this\n * at every phase boundary with timing and state info. This lets the hosting\n * app (ezcoder, come-alive, etc.) log stall diagnostics without the agent\n * package needing fs/process dependencies.\n */\nexport type StreamDiagnosticFn = (phase: string, data?: Record<string, unknown>) => void;\n\n/** Global diagnostic hook — set by the hosting app before calling agentLoop. */\nlet _diagFn: StreamDiagnosticFn | null = null;\n\n/** Register a diagnostic callback for stream stall tracing. */\nexport function setStreamDiagnostic(fn: StreamDiagnosticFn | null): void {\n _diagFn = fn;\n}\n\nfunction diag(phase: string, data?: Record<string, unknown>): void {\n _diagFn?.(phase, data);\n}\n\n/**\n * Detect abort errors — user-initiated cancellation or AbortSignal.\n * These should be caught and handled gracefully, not re-thrown.\n */\nexport function isAbortError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"AbortError\") return true;\n const msg = err.message.toLowerCase();\n return msg.includes(\"aborted\") || msg.includes(\"abort\");\n}\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect billing/quota errors — these should NOT be retried.\n * GLM returns HTTP 429 with \"Insufficient balance\" for quota exhaustion.\n */\nexport function isBillingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"no resource package\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\") ||\n msg.includes(\"recharge\") ||\n msg.includes(\"subscription plan\") ||\n msg.includes(\"does not yet include access\") ||\n msg.includes(\"token quota\") ||\n msg.includes(\"exceeded_current_quota_error\") ||\n msg.includes(\"check your account balance\")\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n * Excludes billing/quota errors which won't resolve with a retry.\n */\n/**\n * Detect tool pairing errors — orphaned tool_use or tool_result blocks.\n * These are 400 errors that can be recovered by repairing the message history.\n */\nexport function isToolPairingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n (msg.includes(\"tool_use\") && msg.includes(\"tool_result\")) ||\n msg.includes(\"unexpected `tool_use_id`\") ||\n msg.includes(\"tool_use ids found without\") ||\n // Moonshot/OpenAI-compatible: \"tool call id <id> is not found\"\n (msg.includes(\"tool call id\") && msg.includes(\"is not found\"))\n );\n}\n\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\n/**\n * Detect malformed-stream errors — the SDK's SSE decoder threw a JSON parse\n * error mid-stream, typically because a chunk was truncated or corrupted by\n * an intermediary (CDN, proxy). Same class of transport failure as a stall:\n * replaying the request — and ideally flipping to non-streaming — recovers.\n */\nexport function isMalformedStream(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"SyntaxError\") return true;\n const cause = (err as { cause?: unknown }).cause;\n if (cause instanceof Error && cause.name === \"SyntaxError\") return true;\n const msg = err.message;\n // V8 JSON.parse error messages: \"Expected ... in JSON at position N\"\n // and \"Unexpected token ... in JSON at position N\"\n return /\\bin JSON at position \\d+/i.test(msg);\n}\n\n/**\n * Promise-returning sleep that rejects with AbortError if `signal` fires.\n * Used by retry backoffs so ESC/Ctrl+C cancel immediately instead of having\n * to wait out the full delay (up to 30s per overload retry × 10 retries).\n */\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n return new Promise<void>((resolve, reject) => {\n let onAbort: (() => void) | null = null;\n const timer = setTimeout(() => {\n if (onAbort) signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n onAbort = () => {\n clearTimeout(timer);\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let firstTurn = true;\n let consecutivePauses = 0;\n let toolPairingRepaired = false;\n let overloadRetries = 0;\n let emptyResponseRetries = 0;\n let stallRetries = 0;\n // Non-streaming fallback mode. After repeated stream stalls, flip to a\n // plain non-streaming request/response -- often survives broken SSE\n // connections (transient CDN / proxy issues) that streaming retries cannot.\n let useNonStreamingFallback = false;\n const MAX_OVERLOAD_RETRIES = 10;\n const MAX_EMPTY_RESPONSE_RETRIES = 2;\n const MAX_STALL_RETRIES = 5;\n // After this many streaming stalls in a row, switch to non-streaming mode\n // for the remaining stall retries. Keeps the first two retries fast (the\n // cheap \"transient glitch\" case) before paying for a full response round-trip.\n const STALL_RETRIES_BEFORE_NON_STREAMING = 2;\n const STALL_DELAY_MS = 1_000; // Brief pause before retry -- just enough to avoid tight loops\n const OVERLOAD_BASE_DELAY_MS = 2_000;\n const OVERLOAD_MAX_DELAY_MS = 30_000;\n const STREAM_FIRST_EVENT_TIMEOUT_MS = 45_000; // 45s to get first event (Opus thinks long)\n const STREAM_IDLE_TIMEOUT_MS = 30_000; // 30s between events once streaming starts\n // Anthropic models can pause 10-20s mid-stream while computing the next chunk\n // (e.g. generating tool call args for a large write). 10s was too aggressive\n // and caused false \"stream stalled\" errors, especially in plan mode.\n const STREAM_HARD_TIMEOUT_MS = 90_000; // 90s absolute cap before output starts\n // Once output events (text_delta) are actively streaming, extend the hard\n // timeout -- long responses (plan mode, detailed explanations) can legitimately\n // take 2-3+ minutes while events flow continuously.\n const STREAM_OUTPUT_HARD_TIMEOUT_MS = 300_000; // 5min hard cap once output is flowing\n // Reasoning models (MiMo) can pause 3-5 minutes between thinking and output\n // generation. Once we've seen thinking events, extend timeouts significantly.\n const STREAM_THINKING_IDLE_TIMEOUT_MS = 300_000; // 5min idle after thinking\n const STREAM_THINKING_HARD_TIMEOUT_MS = 600_000; // 10min hard cap with thinking\n // Non-streaming mode has no per-event idle -- the entire response arrives in\n // one HTTP round-trip. Use a single generous hard cap instead. This matches\n // Claude Code's v2.1.110/111 behaviour: cap non-streaming retries so API\n // unreachability doesn't cause multi-minute hangs, but not so aggressively\n // that slow-but-healthy backends get killed.\n const NON_STREAMING_HARD_TIMEOUT_MS = 300_000; // 5min for full non-streaming response\n\n try {\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // Estimate message payload size for diagnostics\n let msgChars = 0;\n for (const m of messages) {\n if (typeof m.content === \"string\") msgChars += m.content.length;\n else if (Array.isArray(m.content)) {\n for (const p of m.content) {\n if (\"text\" in p && typeof p.text === \"string\") msgChars += p.text.length;\n if (\"content\" in p && typeof p.content === \"string\") msgChars += p.content.length;\n }\n }\n }\n diag(\"turn_start\", {\n turn,\n messages: messages.length,\n chars: msgChars,\n provider: options.provider,\n model: options.model,\n });\n\n // ── Initial steering poll: catch messages queued before the first LLM call ──\n if (firstTurn && options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n firstTurn = false;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n diag(\"transform_start\");\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n diag(\"transform_compacted\", {\n before: messages.length,\n after: transformed.length,\n });\n messages.length = 0;\n messages.push(...transformed);\n }\n diag(\"transform_end\");\n }\n\n // ── Repair tool pairing: ensure every tool_use has an adjacent tool_result ──\n repairToolPairingAdjacent(messages);\n\n // ── Call LLM with overflow recovery ──\n let response;\n // Per-attempt abort controller: allows idle timeout to abort the stream\n // without affecting the caller's signal. The caller's abort is forwarded.\n const streamController = new AbortController();\n let idleTimer: ReturnType<typeof setTimeout> | null = null;\n let hardTimer: ReturnType<typeof setTimeout> | null = null;\n let idleTimedOut = false;\n\n // Stream event counters — declared here so timeout callbacks can access them\n let streamEventCount = 0;\n let lastEventTime = Date.now();\n let streamCallStart = Date.now();\n // Track event types for diagnostics — shows what arrived before a stall\n const eventTypeCounts: Record<string, number> = {};\n let lastEventType = \"\";\n // Track consumer processing time — helps distinguish \"API stopped sending\"\n // from \"our consumer was slow to pull the next event\"\n let lastYieldEndTime = Date.now();\n let maxConsumerLagMs = 0;\n\n // Forward caller abort to the per-attempt controller\n const forwardAbort = () => streamController.abort();\n options.signal?.addEventListener(\"abort\", forwardAbort, { once: true });\n\n // Three-phase idle timeout:\n // - Before first event: STREAM_FIRST_EVENT_TIMEOUT_MS (45s) -- Opus can\n // take 30s+ to start on large contexts, that's not a stall.\n // - After output event (text_delta, server_toolcall): STREAM_IDLE_TIMEOUT_MS\n // (10s) -- once output is streaming, 10s of silence is dead. Retry fast.\n // - After thinking events only: STREAM_THINKING_IDLE_TIMEOUT_MS (5min) --\n // reasoning models (MiMo) can pause minutes between thinking and output.\n //\n // In non-streaming fallback mode the entire response arrives in a single\n // HTTP round-trip, so the idle timer is disabled -- only the hard timeout\n // applies. Synthesized events all arrive at once when the response returns.\n let hasReceivedEvent = false;\n let hasReceivedThinking = false;\n const resetIdleTimer = () => {\n if (useNonStreamingFallback) return; // no inter-event idle in non-streaming mode\n if (idleTimer) clearTimeout(idleTimer);\n const timeoutMs = hasReceivedEvent\n ? STREAM_IDLE_TIMEOUT_MS\n : hasReceivedThinking\n ? STREAM_THINKING_IDLE_TIMEOUT_MS\n : STREAM_FIRST_EVENT_TIMEOUT_MS;\n idleTimer = setTimeout(() => {\n diag(\"idle_timeout_fired\", {\n events: streamEventCount,\n sinceLastEventMs: Date.now() - lastEventTime,\n lastEventType,\n maxConsumerLagMs,\n phase: hasReceivedEvent\n ? \"mid_stream\"\n : hasReceivedThinking\n ? \"post_thinking\"\n : \"first_event\",\n eventTypes: eventTypeCounts,\n });\n idleTimedOut = true;\n streamController.abort();\n }, timeoutMs);\n };\n\n // Hard timeout: absolute cap per LLM call. Safety net for streams that\n // keep sending sparse events (e.g. keep-alive pings) but never complete.\n // Extended dynamically when thinking events arrive (see thinking_delta handler).\n // Non-streaming fallback uses a single larger cap since there's no stream\n // to observe -- just wait for the full response up to the cap.\n let hardTimeoutMs = useNonStreamingFallback\n ? NON_STREAMING_HARD_TIMEOUT_MS\n : STREAM_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", {\n events: typeof streamEventCount !== \"undefined\" ? streamEventCount : 0,\n nonStreaming: useNonStreamingFallback,\n });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n\n try {\n diag(\"stream_call\", { nonStreaming: useNonStreamingFallback });\n streamCallStart = Date.now();\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: streamController.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n supportsImages: options.supportsImages,\n compaction: options.compaction,\n clearToolUses: options.clearToolUses,\n // Flip to non-streaming fallback after repeated stream stalls.\n ...(useNonStreamingFallback ? { streaming: false } : {}),\n });\n diag(\"stream_created\", { setupMs: Date.now() - streamCallStart });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas — reset idle timer on each event\n streamEventCount = 0;\n hasReceivedEvent = false;\n lastEventTime = Date.now();\n streamCallStart = Date.now();\n resetIdleTimer();\n for await (const event of result) {\n // Measure consumer lag: time between finishing previous yield and\n // receiving this event. High lag means React rendering is starving\n // the stream consumer. Low lag means the API was slow to send.\n const pullTime = Date.now();\n const consumerLag = pullTime - lastYieldEndTime;\n if (consumerLag > maxConsumerLagMs) maxConsumerLagMs = consumerLag;\n\n streamEventCount++;\n eventTypeCounts[event.type] = (eventTypeCounts[event.type] ?? 0) + 1;\n lastEventType = event.type;\n\n // Flip to mid-stream timeout on confirmed output events — text\n // deltas, completed tool calls, and tool call deltas (large file\n // writes can stream toolcall_delta for minutes without any text_delta).\n // Reasoning models (MiMo) are handled separately below — they can\n // stream hundreds of thinking events then pause minutes before output.\n if (\n (event.type === \"text_delta\" ||\n event.type === \"server_toolcall\" ||\n event.type === \"toolcall_delta\") &&\n !hasReceivedEvent\n ) {\n hasReceivedEvent = true;\n // Extend hard timeout now that output is actively streaming.\n // Long responses (plan mode, detailed code) can exceed 90s while\n // events flow continuously — the idle timeout (10s) catches real stalls.\n if (hardTimer && hardTimeoutMs < STREAM_OUTPUT_HARD_TIMEOUT_MS) {\n clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_OUTPUT_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n }\n // Track thinking events — extends idle timeout and hard timeout\n // so reasoning models aren't killed during thinking→output transition.\n if (event.type === \"thinking_delta\" && !hasReceivedThinking) {\n hasReceivedThinking = true;\n // Extend the hard timeout now that we know the model is reasoning\n if (hardTimer) clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_THINKING_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n\n const now = Date.now();\n const gap = now - lastEventTime;\n // Log first event and any suspiciously long gaps\n if (streamEventCount === 1) {\n diag(\"first_event\", { type: event.type, ttfMs: now - streamCallStart });\n } else if (gap > 3000) {\n diag(\"slow_gap\", {\n type: event.type,\n gapMs: gap,\n eventNum: streamEventCount,\n sinceStartMs: now - streamCallStart,\n });\n }\n lastEventTime = now;\n resetIdleTimer();\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n } else if (event.type === \"toolcall_delta\") {\n yield {\n type: \"toolcall_delta\" as const,\n chars: event.argsJson?.length ?? 0,\n };\n }\n lastYieldEndTime = Date.now();\n }\n\n diag(\"stream_done\", {\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n maxConsumerLagMs,\n eventTypes: eventTypeCounts,\n });\n response = await result.response;\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n diag(\"stream_error\", {\n error: errMsg.slice(0, 200),\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n idleTimedOut,\n aborted: !!options.signal?.aborted,\n eventTypes: eventTypeCounts,\n provider: options.provider,\n model: options.model,\n });\n // Context overflow: surface immediately as an error.\n // The pre-turn transformContext check should prevent overflow proactively.\n // Compacting mid-retry is unreliable (calls the same provider, may not\n // reduce enough) and was removed along with stall-compaction.\n if (isContextOverflow(err)) {\n yield { type: \"error\" as const, error: err instanceof Error ? err : new Error(errMsg) };\n throw err;\n }\n // Overloaded / rate-limited: exponential backoff, retry up to 10 times\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n const delayMs = Math.min(\n OVERLOAD_BASE_DELAY_MS * 2 ** (overloadRetries - 1),\n OVERLOAD_MAX_DELAY_MS,\n );\n diag(\"retry\", {\n reason: \"overloaded\",\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n });\n yield {\n type: \"retry\" as const,\n reason: \"overloaded\" as const,\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n };\n await abortableSleep(delayMs, options.signal);\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall: the API connection hung without closing.\n // Malformed stream: the SDK's SSE decoder hit truncated/corrupted JSON.\n // Both are transport failures — retry with exponential backoff and flip\n // to non-streaming mode after STALL_RETRIES_BEFORE_NON_STREAMING attempts,\n // since broken SSE often recovers when replayed as plain HTTP.\n const malformed = isMalformedStream(err);\n const transportFailure = (idleTimedOut || malformed) && !options.signal?.aborted;\n if (transportFailure && stallRetries < MAX_STALL_RETRIES) {\n stallRetries++;\n if (!useNonStreamingFallback && stallRetries >= STALL_RETRIES_BEFORE_NON_STREAMING) {\n useNonStreamingFallback = true;\n diag(\"non_streaming_fallback_enabled\", {\n stallRetries,\n provider: options.provider,\n model: options.model,\n cause: malformed ? \"malformed_stream\" : \"stream_stall\",\n });\n }\n const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8_000);\n diag(\"retry\", {\n reason: malformed ? \"malformed_stream\" : \"stream_stall\",\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n events: streamEventCount,\n nonStreaming: useNonStreamingFallback,\n });\n yield {\n type: \"retry\" as const,\n reason: \"stream_stall\" as const,\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n silent: stallRetries <= 2,\n };\n await abortableSleep(delayMs, options.signal);\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall retries exhausted — surface a clear error so the UI\n // can distinguish \"gave up after stalls\" from \"completed normally\".\n if (transportFailure) {\n diag(\"stall_exhausted\", {\n stallRetries: MAX_STALL_RETRIES,\n provider: options.provider,\n model: options.model,\n });\n yield {\n type: \"error\" as const,\n error: new Error(\n `The API provider's stream stalled ${MAX_STALL_RETRIES} times — the provider may be experiencing capacity issues. ` +\n `Your conversation is preserved. Send another message to retry.`,\n ),\n };\n break;\n }\n // Tool pairing 400: orphaned tool_result or tool_use in message history.\n // Run repair and retry once — if repair can't fix it, surface the error.\n if (isToolPairingError(err) && !toolPairingRepaired) {\n toolPairingRepaired = true;\n diag(\"tool_pairing_repair\", { error: errMsg.slice(0, 200) });\n repairToolPairingAdjacent(messages);\n turn--;\n continue;\n }\n // Abort errors (user cancellation) — exit loop cleanly instead of\n // crashing the process with an unhandled rejection.\n if (isAbortError(err) || options.signal?.aborted) {\n diag(\"aborted\", { turn, provider: options.provider, model: options.model });\n break;\n }\n // Unhandled error — log before throwing so the crash is traceable\n diag(\"unhandled_error\", {\n error: errMsg.slice(0, 500),\n turn,\n provider: options.provider,\n model: options.model,\n });\n throw err;\n } finally {\n if (idleTimer) clearTimeout(idleTimer);\n if (hardTimer) clearTimeout(hardTimer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n overloadRetries = 0;\n stallRetries = 0;\n\n // Detect empty/degenerate responses — the API occasionally returns 0 tokens\n // with no content, or \"thinks\" without producing actionable output.\n // Reasoning models (MiMo, DeepSeek) may report outputTokens > 0 from\n // thinking alone while producing no text or tool calls — still a dud.\n const contentArr = Array.isArray(response.message.content) ? response.message.content : null;\n const hasActionableContent =\n response.message.content !== \"\" &&\n contentArr !== null &&\n contentArr.some(\n (p) => p.type === \"text\" || p.type === \"tool_call\" || p.type === \"server_tool_call\",\n );\n if (!hasActionableContent) {\n if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {\n emptyResponseRetries++;\n diag(\"retry\", {\n reason: \"empty_response\",\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n provider: options.provider,\n model: options.model,\n contentTypes: contentArr?.map((p) => p.type).join(\",\") ?? \"empty\",\n });\n yield {\n type: \"retry\" as const,\n reason: \"empty_response\" as const,\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n delayMs: 0,\n };\n turn--; // Don't count the failed turn — keep useNonStreamingFallback set\n // so the retry doesn't bounce back into a streaming connection that\n // will stall again with the same upstream problem.\n continue;\n }\n // Exhausted retries — fall through and let the agent finish\n }\n emptyResponseRetries = 0;\n\n // Only clear the non-streaming fallback after an actionable response —\n // an empty non-streaming reply means the upstream issue hasn't resolved,\n // so staying in non-streaming mode avoids retrying into another stall.\n useNonStreamingFallback = false;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n\n // If no tool calls to execute, check for steering messages before stopping.\n // Check content (not just stopReason) because some providers (e.g. GLM)\n // return finish_reason=\"stop\" even when tool calls are present.\n if (response.stopReason !== \"tool_use\" && allToolCalls.length === 0) {\n // Check for queued steering messages — if present, inject and continue\n // the loop instead of returning (follow-up pattern).\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue; // Next iteration will call LLM with injected messages\n }\n }\n // Follow-up: lower priority than steering — only when agent would otherwise stop.\n if (options.getFollowUpMessages) {\n const followUp = await options.getFollowUpMessages();\n if (followUp && followUp.length > 0) {\n for (const msg of followUp) {\n yield { type: \"follow_up_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue;\n }\n }\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: ToolResultContent;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: toolResultPreview(resultContent),\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n const resultsMap = new Map(results.map((r) => [r.toolCallId, r]));\n for (const tc of toolCalls) {\n const r = resultsMap.get(tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n let toolsAborted = false;\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } catch (err) {\n // Tool event stream aborted (Ctrl+C) — don't propagate, just mark\n // so the finally block can clean up and the loop can exit.\n if (isAbortError(err) || options.signal?.aborted) {\n toolsAborted = true;\n } else {\n throw err;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n const resolvedIds = new Set(toolResults.map((r) => r.toolCallId));\n for (const tc of toolCalls) {\n if (!resolvedIds.has(tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n // Guard: cap oversized tool results before they enter conversation history.\n // Uses head+tail strategy to preserve error messages / closing structure at the end.\n if (options.maxToolResultChars) {\n const HARD_MAX = 400_000; // absolute ceiling regardless of context window\n const max = Math.min(options.maxToolResultChars, HARD_MAX);\n for (const tr of toolResults) {\n // Only truncate string content — array content (text+image blocks)\n // is already size-bounded by the image resizer.\n if (typeof tr.content === \"string\" && tr.content.length > max) {\n // Keep 70% head + 30% tail to preserve errors/diagnostics at the end\n const headChars = Math.floor(max * 0.7);\n const tailChars = max - headChars;\n const head = tr.content.slice(0, headChars);\n const tail = tr.content.slice(-tailChars);\n const omitted = tr.content.length - headChars - tailChars;\n tr.content = head + `\\n\\n[... ${omitted} characters omitted ...]\\n\\n` + tail;\n }\n }\n }\n\n messages.push({ role: \"tool\", content: toolResults });\n }\n\n // Exit loop after cleaning up aborted tools\n if (toolsAborted) break;\n\n // ── Steering messages: inject user messages queued during tool execution ──\n // Polled after tools complete so the next LLM call sees them in context.\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n }\n } finally {\n // Sanitize orphaned server_tool_use blocks on abort.\n // When a stream is aborted mid-server-tool (e.g. web_search), the\n // assistant message containing the server_tool_use may already be in\n // the messages array, but the corresponding web_search_tool_result\n // never arrived. The API rejects the next request with a 400 if it\n // finds an unmatched server_tool_use, so we strip it here.\n sanitizeOrphanedServerTools(messages);\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\n/** Flatten tool result content to a plain-text preview for the tool_call_end event.\n * Image blocks become a \"[image]\" placeholder so the UI has something to render. */\nfunction toolResultPreview(content: ToolResultContent): string {\n if (typeof content === \"string\") return content;\n return content\n .map((block) => (block.type === \"text\" ? block.text : `[image ${block.mediaType}]`))\n .join(\"\\n\");\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n\n/**\n * Remove orphaned server_tool_use blocks from the last assistant message.\n * When a stream is aborted mid-server-tool (e.g. web_search), the assistant\n * message may contain a server_tool_call without a matching server_tool_result.\n * The API rejects the next request if these are unmatched.\n */\nfunction sanitizeOrphanedServerTools(messages: Message[]): void {\n // Find the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) break;\n\n // Collect server_tool_call ids and matched server_tool_result ids\n const serverToolIds = new Set<string>();\n const resultToolIds = new Set<string>();\n for (const part of msg.content) {\n if (part.type === \"server_tool_call\") serverToolIds.add(part.id);\n if (part.type === \"server_tool_result\") resultToolIds.add(part.toolUseId);\n }\n\n // Find unmatched server_tool_call blocks\n const orphanedIds = new Set<string>();\n for (const id of serverToolIds) {\n if (!resultToolIds.has(id)) orphanedIds.add(id);\n }\n\n if (orphanedIds.size === 0) break;\n\n // Strip orphaned server_tool_call blocks from the content\n const filtered = msg.content.filter(\n (part) => !(part.type === \"server_tool_call\" && orphanedIds.has(part.id)),\n );\n\n if (filtered.length === 0) {\n // Nothing left — remove the entire message\n messages.splice(i, 1);\n } else {\n (msg as { content: ContentPart[] }).content = filtered;\n }\n break;\n }\n}\n\n/**\n * Ensure every assistant message with tool_call blocks is immediately followed\n * by a tool message with matching tool_result entries. This prevents Anthropic\n * API 400 errors (\"tool_use ids found without tool_result blocks immediately\n * after\") that can occur after compaction, session restore, or abort recovery.\n *\n * Repairs in-place by inserting synthetic tool_result messages where needed.\n */\nfunction repairToolPairingAdjacent(messages: Message[]): void {\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) continue;\n\n const toolCallIds = (msg.content as ContentPart[])\n .filter((p) => p.type === \"tool_call\")\n .map((p) => (p as ContentPart & { type: \"tool_call\"; id: string }).id);\n if (toolCallIds.length === 0) continue;\n\n const next = messages[i + 1];\n if (next?.role === \"tool\" && Array.isArray(next.content)) {\n // Tool message exists — check for missing results\n const existingIds = new Set((next.content as ToolResult[]).map((r) => r.toolCallId));\n const missing = toolCallIds.filter((id) => !existingIds.has(id));\n if (missing.length > 0) {\n for (const id of missing) {\n (next.content as ToolResult[]).push({\n type: \"tool_result\",\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n });\n }\n }\n } else {\n // No tool message follows — insert a synthetic one\n messages.splice(i + 1, 0, {\n role: \"tool\" as const,\n content: toolCallIds.map((id) => ({\n type: \"tool_result\" as const,\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n })),\n });\n }\n }\n\n // Reverse repair: strip tool_result entries whose tool_use_id has no matching\n // tool_call in the preceding assistant message. This can happen when compaction\n // or stall recovery removes an assistant message but leaves its tool_result behind.\n const toolCallIdSet = new Set<string>();\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n for (const p of msg.content as ContentPart[]) {\n if (p.type === \"tool_call\") toolCallIdSet.add((p as ToolCall).id);\n }\n }\n if (msg.role === \"tool\" && Array.isArray(msg.content)) {\n const results = msg.content as ToolResult[];\n const filtered = results.filter((r) => toolCallIdSet.has(r.toolCallId));\n if (filtered.length === 0) {\n // Entire tool message is orphaned — remove it\n messages.splice(i, 1);\n i--;\n } else if (filtered.length < results.length) {\n (msg as { content: ToolResult[] }).content = filtered;\n }\n }\n }\n}\n"],"mappings":"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","names":["import_ai"]}
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { StreamOptions, Tool, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
1
+ import { StreamOptions, Tool, ToolResultContent, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
2
2
  import { z } from 'zod';
3
3
 
4
4
  interface StructuredToolResult {
5
- content: string;
5
+ content: ToolResultContent;
6
6
  details?: unknown;
7
7
  }
8
8
  type ToolExecuteResult = string | StructuredToolResult;
@@ -55,7 +55,7 @@ interface AgentDoneEvent {
55
55
  }
56
56
  interface AgentRetryEvent {
57
57
  type: "retry";
58
- reason: "overloaded" | "rate_limit" | "empty_response" | "context_overflow" | "stream_stall";
58
+ reason: "overloaded" | "rate_limit" | "empty_response" | "stream_stall";
59
59
  attempt: number;
60
60
  maxAttempts: number;
61
61
  delayMs: number;
@@ -106,6 +106,9 @@ interface AgentOptions {
106
106
  signal?: AbortSignal;
107
107
  accountId?: string;
108
108
  cacheRetention?: StreamOptions["cacheRetention"];
109
+ /** Whether the target model supports image input. When false, image blocks
110
+ * in messages/tool_results are downgraded to text placeholders. Default: true. */
111
+ supportsImages?: boolean;
109
112
  /** Enable provider-native web search. */
110
113
  webSearch?: boolean;
111
114
  /** Enable server-side compaction (Anthropic only, beta). */
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { StreamOptions, Tool, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
1
+ import { StreamOptions, Tool, ToolResultContent, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
2
2
  import { z } from 'zod';
3
3
 
4
4
  interface StructuredToolResult {
5
- content: string;
5
+ content: ToolResultContent;
6
6
  details?: unknown;
7
7
  }
8
8
  type ToolExecuteResult = string | StructuredToolResult;
@@ -55,7 +55,7 @@ interface AgentDoneEvent {
55
55
  }
56
56
  interface AgentRetryEvent {
57
57
  type: "retry";
58
- reason: "overloaded" | "rate_limit" | "empty_response" | "context_overflow" | "stream_stall";
58
+ reason: "overloaded" | "rate_limit" | "empty_response" | "stream_stall";
59
59
  attempt: number;
60
60
  maxAttempts: number;
61
61
  delayMs: number;
@@ -106,6 +106,9 @@ interface AgentOptions {
106
106
  signal?: AbortSignal;
107
107
  accountId?: string;
108
108
  cacheRetention?: StreamOptions["cacheRetention"];
109
+ /** Whether the target model supports image input. When false, image blocks
110
+ * in messages/tool_results are downgraded to text placeholders. Default: true. */
111
+ supportsImages?: boolean;
109
112
  /** Enable provider-native web search. */
110
113
  webSearch?: boolean;
111
114
  /** Enable server-side compaction (Anthropic only, beta). */
package/dist/index.js CHANGED
@@ -22,18 +22,20 @@ function isAbortError(err) {
22
22
  }
23
23
  function isContextOverflow(err) {
24
24
  if (!(err instanceof Error)) return false;
25
+ if (isBillingError(err)) return false;
25
26
  const msg = err.message.toLowerCase();
26
27
  return msg.includes("prompt is too long") || msg.includes("context_length_exceeded") || msg.includes("maximum context length") || msg.includes("token") && msg.includes("exceed");
27
28
  }
28
29
  function isBillingError(err) {
29
30
  if (!(err instanceof Error)) return false;
30
31
  const msg = err.message.toLowerCase();
31
- return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge") || msg.includes("subscription plan") || msg.includes("does not yet include access");
32
+ return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge") || msg.includes("subscription plan") || msg.includes("does not yet include access") || msg.includes("token quota") || msg.includes("exceeded_current_quota_error") || msg.includes("check your account balance");
32
33
  }
33
34
  function isToolPairingError(err) {
34
35
  if (!(err instanceof Error)) return false;
35
36
  const msg = err.message.toLowerCase();
36
- return msg.includes("tool_use") && msg.includes("tool_result") || msg.includes("unexpected `tool_use_id`") || msg.includes("tool_use ids found without");
37
+ return msg.includes("tool_use") && msg.includes("tool_result") || msg.includes("unexpected `tool_use_id`") || msg.includes("tool_use ids found without") || // Moonshot/OpenAI-compatible: "tool call id <id> is not found"
38
+ msg.includes("tool call id") && msg.includes("is not found");
37
39
  }
38
40
  function isOverloaded(err) {
39
41
  if (!(err instanceof Error)) return false;
@@ -41,6 +43,31 @@ function isOverloaded(err) {
41
43
  const msg = err.message.toLowerCase();
42
44
  return msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("529");
43
45
  }
46
+ function isMalformedStream(err) {
47
+ if (!(err instanceof Error)) return false;
48
+ if (err.name === "SyntaxError") return true;
49
+ const cause = err.cause;
50
+ if (cause instanceof Error && cause.name === "SyntaxError") return true;
51
+ const msg = err.message;
52
+ return /\bin JSON at position \d+/i.test(msg);
53
+ }
54
+ function abortableSleep(ms, signal) {
55
+ if (signal?.aborted) {
56
+ return Promise.reject(new DOMException("Aborted", "AbortError"));
57
+ }
58
+ return new Promise((resolve, reject) => {
59
+ let onAbort = null;
60
+ const timer = setTimeout(() => {
61
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
62
+ resolve();
63
+ }, ms);
64
+ onAbort = () => {
65
+ clearTimeout(timer);
66
+ reject(new DOMException("Aborted", "AbortError"));
67
+ };
68
+ signal?.addEventListener("abort", onAbort, { once: true });
69
+ });
70
+ }
44
71
  async function* agentLoop(messages, options) {
45
72
  const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
46
73
  const maxContinuations = options.maxContinuations ?? 5;
@@ -50,14 +77,14 @@ async function* agentLoop(messages, options) {
50
77
  let firstTurn = true;
51
78
  let consecutivePauses = 0;
52
79
  let toolPairingRepaired = false;
53
- let overflowRetries = 0;
54
80
  let overloadRetries = 0;
55
81
  let emptyResponseRetries = 0;
56
82
  let stallRetries = 0;
57
- const MAX_OVERFLOW_RETRIES = 3;
83
+ let useNonStreamingFallback = false;
58
84
  const MAX_OVERLOAD_RETRIES = 10;
59
85
  const MAX_EMPTY_RESPONSE_RETRIES = 2;
60
86
  const MAX_STALL_RETRIES = 5;
87
+ const STALL_RETRIES_BEFORE_NON_STREAMING = 2;
61
88
  const STALL_DELAY_MS = 1e3;
62
89
  const OVERLOAD_BASE_DELAY_MS = 2e3;
63
90
  const OVERLOAD_MAX_DELAY_MS = 3e4;
@@ -67,6 +94,7 @@ async function* agentLoop(messages, options) {
67
94
  const STREAM_OUTPUT_HARD_TIMEOUT_MS = 3e5;
68
95
  const STREAM_THINKING_IDLE_TIMEOUT_MS = 3e5;
69
96
  const STREAM_THINKING_HARD_TIMEOUT_MS = 6e5;
97
+ const NON_STREAMING_HARD_TIMEOUT_MS = 3e5;
70
98
  try {
71
99
  while (turn < maxTurns) {
72
100
  options.signal?.throwIfAborted();
@@ -129,6 +157,7 @@ async function* agentLoop(messages, options) {
129
157
  let hasReceivedEvent = false;
130
158
  let hasReceivedThinking = false;
131
159
  const resetIdleTimer = () => {
160
+ if (useNonStreamingFallback) return;
132
161
  if (idleTimer) clearTimeout(idleTimer);
133
162
  const timeoutMs = hasReceivedEvent ? STREAM_IDLE_TIMEOUT_MS : hasReceivedThinking ? STREAM_THINKING_IDLE_TIMEOUT_MS : STREAM_FIRST_EVENT_TIMEOUT_MS;
134
163
  idleTimer = setTimeout(() => {
@@ -144,16 +173,17 @@ async function* agentLoop(messages, options) {
144
173
  streamController.abort();
145
174
  }, timeoutMs);
146
175
  };
147
- let hardTimeoutMs = STREAM_HARD_TIMEOUT_MS;
176
+ let hardTimeoutMs = useNonStreamingFallback ? NON_STREAMING_HARD_TIMEOUT_MS : STREAM_HARD_TIMEOUT_MS;
148
177
  hardTimer = setTimeout(() => {
149
178
  diag("hard_timeout_fired", {
150
- events: typeof streamEventCount !== "undefined" ? streamEventCount : 0
179
+ events: typeof streamEventCount !== "undefined" ? streamEventCount : 0,
180
+ nonStreaming: useNonStreamingFallback
151
181
  });
152
182
  idleTimedOut = true;
153
183
  streamController.abort();
154
184
  }, hardTimeoutMs);
155
185
  try {
156
- diag("stream_call");
186
+ diag("stream_call", { nonStreaming: useNonStreamingFallback });
157
187
  streamCallStart = Date.now();
158
188
  const result = stream({
159
189
  provider: options.provider,
@@ -170,8 +200,11 @@ async function* agentLoop(messages, options) {
170
200
  signal: streamController.signal,
171
201
  accountId: options.accountId,
172
202
  cacheRetention: options.cacheRetention,
203
+ supportsImages: options.supportsImages,
173
204
  compaction: options.compaction,
174
- clearToolUses: options.clearToolUses
205
+ clearToolUses: options.clearToolUses,
206
+ // Flip to non-streaming fallback after repeated stream stalls.
207
+ ...useNonStreamingFallback ? { streaming: false } : {}
175
208
  });
176
209
  diag("stream_created", { setupMs: Date.now() - streamCallStart });
177
210
  result.response.catch(() => {
@@ -269,27 +302,9 @@ async function* agentLoop(messages, options) {
269
302
  provider: options.provider,
270
303
  model: options.model
271
304
  });
272
- if (overflowRetries < MAX_OVERFLOW_RETRIES && isContextOverflow(err) && options.transformContext) {
273
- overflowRetries++;
274
- diag("retry", {
275
- reason: "context_overflow",
276
- attempt: overflowRetries,
277
- maxAttempts: MAX_OVERFLOW_RETRIES
278
- });
279
- yield {
280
- type: "retry",
281
- reason: "context_overflow",
282
- attempt: overflowRetries,
283
- maxAttempts: MAX_OVERFLOW_RETRIES,
284
- delayMs: 0
285
- };
286
- const transformed = await options.transformContext(messages, { force: true });
287
- if (transformed !== messages) {
288
- messages.length = 0;
289
- messages.push(...transformed);
290
- }
291
- turn--;
292
- continue;
305
+ if (isContextOverflow(err)) {
306
+ yield { type: "error", error: err instanceof Error ? err : new Error(errMsg) };
307
+ throw err;
293
308
  }
294
309
  if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {
295
310
  overloadRetries++;
@@ -310,19 +325,31 @@ async function* agentLoop(messages, options) {
310
325
  maxAttempts: MAX_OVERLOAD_RETRIES,
311
326
  delayMs
312
327
  };
313
- await new Promise((r) => setTimeout(r, delayMs));
328
+ await abortableSleep(delayMs, options.signal);
314
329
  turn--;
315
330
  continue;
316
331
  }
317
- if (idleTimedOut && !options.signal?.aborted && stallRetries < MAX_STALL_RETRIES) {
332
+ const malformed = isMalformedStream(err);
333
+ const transportFailure = (idleTimedOut || malformed) && !options.signal?.aborted;
334
+ if (transportFailure && stallRetries < MAX_STALL_RETRIES) {
318
335
  stallRetries++;
336
+ if (!useNonStreamingFallback && stallRetries >= STALL_RETRIES_BEFORE_NON_STREAMING) {
337
+ useNonStreamingFallback = true;
338
+ diag("non_streaming_fallback_enabled", {
339
+ stallRetries,
340
+ provider: options.provider,
341
+ model: options.model,
342
+ cause: malformed ? "malformed_stream" : "stream_stall"
343
+ });
344
+ }
319
345
  const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8e3);
320
346
  diag("retry", {
321
- reason: "stream_stall",
347
+ reason: malformed ? "malformed_stream" : "stream_stall",
322
348
  attempt: stallRetries,
323
349
  maxAttempts: MAX_STALL_RETRIES,
324
350
  delayMs,
325
- events: streamEventCount
351
+ events: streamEventCount,
352
+ nonStreaming: useNonStreamingFallback
326
353
  });
327
354
  yield {
328
355
  type: "retry",
@@ -332,11 +359,11 @@ async function* agentLoop(messages, options) {
332
359
  delayMs,
333
360
  silent: stallRetries <= 2
334
361
  };
335
- await new Promise((r) => setTimeout(r, delayMs));
362
+ await abortableSleep(delayMs, options.signal);
336
363
  turn--;
337
364
  continue;
338
365
  }
339
- if (idleTimedOut && !options.signal?.aborted) {
366
+ if (transportFailure) {
340
367
  diag("stall_exhausted", {
341
368
  stallRetries: MAX_STALL_RETRIES,
342
369
  provider: options.provider,
@@ -373,7 +400,6 @@ async function* agentLoop(messages, options) {
373
400
  if (hardTimer) clearTimeout(hardTimer);
374
401
  options.signal?.removeEventListener("abort", forwardAbort);
375
402
  }
376
- overflowRetries = 0;
377
403
  overloadRetries = 0;
378
404
  stallRetries = 0;
379
405
  const contentArr = Array.isArray(response.message.content) ? response.message.content : null;
@@ -403,6 +429,7 @@ async function* agentLoop(messages, options) {
403
429
  }
404
430
  }
405
431
  emptyResponseRetries = 0;
432
+ useNonStreamingFallback = false;
406
433
  totalUsage.inputTokens += response.usage.inputTokens;
407
434
  totalUsage.outputTokens += response.usage.outputTokens;
408
435
  if (response.usage.cacheRead) {
@@ -515,7 +542,7 @@ async function* agentLoop(messages, options) {
515
542
  eventStream.push({
516
543
  type: "tool_call_end",
517
544
  toolCallId: toolCall.id,
518
- result: resultContent,
545
+ result: toolResultPreview(resultContent),
519
546
  details,
520
547
  isError,
521
548
  durationMs
@@ -568,7 +595,7 @@ async function* agentLoop(messages, options) {
568
595
  const HARD_MAX = 4e5;
569
596
  const max = Math.min(options.maxToolResultChars, HARD_MAX);
570
597
  for (const tr of toolResults) {
571
- if (tr.content.length > max) {
598
+ if (typeof tr.content === "string" && tr.content.length > max) {
572
599
  const headChars = Math.floor(max * 0.7);
573
600
  const tailChars = max - headChars;
574
601
  const head = tr.content.slice(0, headChars);
@@ -619,6 +646,10 @@ async function* agentLoop(messages, options) {
619
646
  function normalizeToolResult(raw) {
620
647
  return typeof raw === "string" ? { content: raw } : raw;
621
648
  }
649
+ function toolResultPreview(content) {
650
+ if (typeof content === "string") return content;
651
+ return content.map((block) => block.type === "text" ? block.text : `[image ${block.mediaType}]`).join("\n");
652
+ }
622
653
  function extractToolCalls(content) {
623
654
  if (typeof content === "string") return [];
624
655
  return content.filter((part) => part.type === "tool_call");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n private steeringQueue: Message[] = [];\n private followUpQueue: Message[] = [];\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Queue a steering message for injection after current tool execution completes. */\n steer(msg: Message): void {\n this.steeringQueue.push(msg);\n }\n\n /** Queue a follow-up message for injection when the agent would otherwise stop. */\n followUp(msg: Message): void {\n this.followUpQueue.push(msg);\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const optionsWithQueues: AgentOptions = {\n ...this.options,\n getSteeringMessages: async () => {\n const callerResult = (await this.options.getSteeringMessages?.()) ?? [];\n const queued = this.steeringQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n getFollowUpMessages: async () => {\n const callerResult = (await this.options.getFollowUpMessages?.()) ?? [];\n const queued = this.followUpQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n };\n\n const generator = agentLoop(this.messages, optionsWithQueues);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 200;\n\n/**\n * Lightweight stream diagnostic callback. When set, the agent loop calls this\n * at every phase boundary with timing and state info. This lets the hosting\n * app (ezcoder, come-alive, etc.) log stall diagnostics without the agent\n * package needing fs/process dependencies.\n */\nexport type StreamDiagnosticFn = (phase: string, data?: Record<string, unknown>) => void;\n\n/** Global diagnostic hook — set by the hosting app before calling agentLoop. */\nlet _diagFn: StreamDiagnosticFn | null = null;\n\n/** Register a diagnostic callback for stream stall tracing. */\nexport function setStreamDiagnostic(fn: StreamDiagnosticFn | null): void {\n _diagFn = fn;\n}\n\nfunction diag(phase: string, data?: Record<string, unknown>): void {\n _diagFn?.(phase, data);\n}\n\n/**\n * Detect abort errors — user-initiated cancellation or AbortSignal.\n * These should be caught and handled gracefully, not re-thrown.\n */\nexport function isAbortError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"AbortError\") return true;\n const msg = err.message.toLowerCase();\n return msg.includes(\"aborted\") || msg.includes(\"abort\");\n}\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect billing/quota errors — these should NOT be retried.\n * GLM returns HTTP 429 with \"Insufficient balance\" for quota exhaustion.\n */\nexport function isBillingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"no resource package\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\") ||\n msg.includes(\"recharge\") ||\n msg.includes(\"subscription plan\") ||\n msg.includes(\"does not yet include access\")\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n * Excludes billing/quota errors which won't resolve with a retry.\n */\n/**\n * Detect tool pairing errors — orphaned tool_use or tool_result blocks.\n * These are 400 errors that can be recovered by repairing the message history.\n */\nexport function isToolPairingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n (msg.includes(\"tool_use\") && msg.includes(\"tool_result\")) ||\n msg.includes(\"unexpected `tool_use_id`\") ||\n msg.includes(\"tool_use ids found without\")\n );\n}\n\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let firstTurn = true;\n let consecutivePauses = 0;\n let toolPairingRepaired = false;\n let overflowRetries = 0;\n let overloadRetries = 0;\n let emptyResponseRetries = 0;\n let stallRetries = 0;\n const MAX_OVERFLOW_RETRIES = 3;\n const MAX_OVERLOAD_RETRIES = 10;\n const MAX_EMPTY_RESPONSE_RETRIES = 2;\n const MAX_STALL_RETRIES = 5;\n const STALL_DELAY_MS = 1_000; // Brief pause before retry — just enough to avoid tight loops\n const OVERLOAD_BASE_DELAY_MS = 2_000;\n const OVERLOAD_MAX_DELAY_MS = 30_000;\n const STREAM_FIRST_EVENT_TIMEOUT_MS = 45_000; // 45s to get first event (Opus thinks long)\n const STREAM_IDLE_TIMEOUT_MS = 30_000; // 30s between events once streaming starts\n // Anthropic models can pause 10-20s mid-stream while computing the next chunk\n // (e.g. generating tool call args for a large write). 10s was too aggressive\n // and caused false \"stream stalled\" errors, especially in plan mode.\n const STREAM_HARD_TIMEOUT_MS = 90_000; // 90s absolute cap before output starts\n // Once output events (text_delta) are actively streaming, extend the hard\n // timeout — long responses (plan mode, detailed explanations) can legitimately\n // take 2-3+ minutes while events flow continuously.\n const STREAM_OUTPUT_HARD_TIMEOUT_MS = 300_000; // 5min hard cap once output is flowing\n // Reasoning models (MiMo) can pause 3-5 minutes between thinking and output\n // generation. Once we've seen thinking events, extend timeouts significantly.\n const STREAM_THINKING_IDLE_TIMEOUT_MS = 300_000; // 5min idle after thinking\n const STREAM_THINKING_HARD_TIMEOUT_MS = 600_000; // 10min hard cap with thinking\n\n try {\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // Estimate message payload size for diagnostics\n let msgChars = 0;\n for (const m of messages) {\n if (typeof m.content === \"string\") msgChars += m.content.length;\n else if (Array.isArray(m.content)) {\n for (const p of m.content) {\n if (\"text\" in p && typeof p.text === \"string\") msgChars += p.text.length;\n if (\"content\" in p && typeof p.content === \"string\") msgChars += p.content.length;\n }\n }\n }\n diag(\"turn_start\", {\n turn,\n messages: messages.length,\n chars: msgChars,\n provider: options.provider,\n model: options.model,\n });\n\n // ── Initial steering poll: catch messages queued before the first LLM call ──\n if (firstTurn && options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n firstTurn = false;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n diag(\"transform_start\");\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n diag(\"transform_compacted\", {\n before: messages.length,\n after: transformed.length,\n });\n messages.length = 0;\n messages.push(...transformed);\n }\n diag(\"transform_end\");\n }\n\n // ── Repair tool pairing: ensure every tool_use has an adjacent tool_result ──\n repairToolPairingAdjacent(messages);\n\n // ── Call LLM with overflow recovery ──\n let response;\n // Per-attempt abort controller: allows idle timeout to abort the stream\n // without affecting the caller's signal. The caller's abort is forwarded.\n const streamController = new AbortController();\n let idleTimer: ReturnType<typeof setTimeout> | null = null;\n let hardTimer: ReturnType<typeof setTimeout> | null = null;\n let idleTimedOut = false;\n\n // Stream event counters — declared here so timeout callbacks can access them\n let streamEventCount = 0;\n let lastEventTime = Date.now();\n let streamCallStart = Date.now();\n // Track event types for diagnostics — shows what arrived before a stall\n const eventTypeCounts: Record<string, number> = {};\n let lastEventType = \"\";\n // Track consumer processing time — helps distinguish \"API stopped sending\"\n // from \"our consumer was slow to pull the next event\"\n let lastYieldEndTime = Date.now();\n let maxConsumerLagMs = 0;\n\n // Forward caller abort to the per-attempt controller\n const forwardAbort = () => streamController.abort();\n options.signal?.addEventListener(\"abort\", forwardAbort, { once: true });\n\n // Three-phase idle timeout:\n // - Before first event: STREAM_FIRST_EVENT_TIMEOUT_MS (45s) — Opus can\n // take 30s+ to start on large contexts, that's not a stall.\n // - After output event (text_delta, server_toolcall): STREAM_IDLE_TIMEOUT_MS\n // (10s) — once output is streaming, 10s of silence is dead. Retry fast.\n // - After thinking events only: STREAM_THINKING_IDLE_TIMEOUT_MS (5min) —\n // reasoning models (MiMo) can pause minutes between thinking and output.\n let hasReceivedEvent = false;\n let hasReceivedThinking = false;\n const resetIdleTimer = () => {\n if (idleTimer) clearTimeout(idleTimer);\n const timeoutMs = hasReceivedEvent\n ? STREAM_IDLE_TIMEOUT_MS\n : hasReceivedThinking\n ? STREAM_THINKING_IDLE_TIMEOUT_MS\n : STREAM_FIRST_EVENT_TIMEOUT_MS;\n idleTimer = setTimeout(() => {\n diag(\"idle_timeout_fired\", {\n events: streamEventCount,\n sinceLastEventMs: Date.now() - lastEventTime,\n lastEventType,\n maxConsumerLagMs,\n phase: hasReceivedEvent\n ? \"mid_stream\"\n : hasReceivedThinking\n ? \"post_thinking\"\n : \"first_event\",\n eventTypes: eventTypeCounts,\n });\n idleTimedOut = true;\n streamController.abort();\n }, timeoutMs);\n };\n\n // Hard timeout: absolute cap per LLM call. Safety net for streams that\n // keep sending sparse events (e.g. keep-alive pings) but never complete.\n // Extended dynamically when thinking events arrive (see thinking_delta handler).\n let hardTimeoutMs = STREAM_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", {\n events: typeof streamEventCount !== \"undefined\" ? streamEventCount : 0,\n });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n\n try {\n diag(\"stream_call\");\n streamCallStart = Date.now();\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: streamController.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n compaction: options.compaction,\n clearToolUses: options.clearToolUses,\n });\n diag(\"stream_created\", { setupMs: Date.now() - streamCallStart });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas — reset idle timer on each event\n streamEventCount = 0;\n hasReceivedEvent = false;\n lastEventTime = Date.now();\n streamCallStart = Date.now();\n resetIdleTimer();\n for await (const event of result) {\n // Measure consumer lag: time between finishing previous yield and\n // receiving this event. High lag means React rendering is starving\n // the stream consumer. Low lag means the API was slow to send.\n const pullTime = Date.now();\n const consumerLag = pullTime - lastYieldEndTime;\n if (consumerLag > maxConsumerLagMs) maxConsumerLagMs = consumerLag;\n\n streamEventCount++;\n eventTypeCounts[event.type] = (eventTypeCounts[event.type] ?? 0) + 1;\n lastEventType = event.type;\n\n // Flip to mid-stream timeout on confirmed output events — text\n // deltas, completed tool calls, and tool call deltas (large file\n // writes can stream toolcall_delta for minutes without any text_delta).\n // Reasoning models (MiMo) are handled separately below — they can\n // stream hundreds of thinking events then pause minutes before output.\n if (\n (event.type === \"text_delta\" ||\n event.type === \"server_toolcall\" ||\n event.type === \"toolcall_delta\") &&\n !hasReceivedEvent\n ) {\n hasReceivedEvent = true;\n // Extend hard timeout now that output is actively streaming.\n // Long responses (plan mode, detailed code) can exceed 90s while\n // events flow continuously — the idle timeout (10s) catches real stalls.\n if (hardTimer && hardTimeoutMs < STREAM_OUTPUT_HARD_TIMEOUT_MS) {\n clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_OUTPUT_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n }\n // Track thinking events — extends idle timeout and hard timeout\n // so reasoning models aren't killed during thinking→output transition.\n if (event.type === \"thinking_delta\" && !hasReceivedThinking) {\n hasReceivedThinking = true;\n // Extend the hard timeout now that we know the model is reasoning\n if (hardTimer) clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_THINKING_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n\n const now = Date.now();\n const gap = now - lastEventTime;\n // Log first event and any suspiciously long gaps\n if (streamEventCount === 1) {\n diag(\"first_event\", { type: event.type, ttfMs: now - streamCallStart });\n } else if (gap > 3000) {\n diag(\"slow_gap\", {\n type: event.type,\n gapMs: gap,\n eventNum: streamEventCount,\n sinceStartMs: now - streamCallStart,\n });\n }\n lastEventTime = now;\n resetIdleTimer();\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n } else if (event.type === \"toolcall_delta\") {\n yield {\n type: \"toolcall_delta\" as const,\n chars: event.argsJson?.length ?? 0,\n };\n }\n lastYieldEndTime = Date.now();\n }\n\n diag(\"stream_done\", {\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n maxConsumerLagMs,\n eventTypes: eventTypeCounts,\n });\n response = await result.response;\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n diag(\"stream_error\", {\n error: errMsg.slice(0, 200),\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n idleTimedOut,\n aborted: !!options.signal?.aborted,\n eventTypes: eventTypeCounts,\n provider: options.provider,\n model: options.model,\n });\n // Context overflow: force-compact via transformContext and retry (up to 3 times)\n if (\n overflowRetries < MAX_OVERFLOW_RETRIES &&\n isContextOverflow(err) &&\n options.transformContext\n ) {\n overflowRetries++;\n diag(\"retry\", {\n reason: \"context_overflow\",\n attempt: overflowRetries,\n maxAttempts: MAX_OVERFLOW_RETRIES,\n });\n yield {\n type: \"retry\" as const,\n reason: \"context_overflow\" as const,\n attempt: overflowRetries,\n maxAttempts: MAX_OVERFLOW_RETRIES,\n delayMs: 0,\n };\n const transformed = await options.transformContext(messages, { force: true });\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n turn--; // Don't count the failed turn\n continue;\n }\n // Overloaded / rate-limited: exponential backoff, retry up to 10 times\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n const delayMs = Math.min(\n OVERLOAD_BASE_DELAY_MS * 2 ** (overloadRetries - 1),\n OVERLOAD_MAX_DELAY_MS,\n );\n diag(\"retry\", {\n reason: \"overloaded\",\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n });\n yield {\n type: \"retry\" as const,\n reason: \"overloaded\" as const,\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n };\n await new Promise((r) => setTimeout(r, delayMs));\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall: the API connection hung without closing.\n // Retry with exponential backoff — most stalls are transient server-side\n // issues, not payload-dependent. First 2 retries are silent (hidden retries).\n if (idleTimedOut && !options.signal?.aborted && stallRetries < MAX_STALL_RETRIES) {\n stallRetries++;\n const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8_000);\n diag(\"retry\", {\n reason: \"stream_stall\",\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n events: streamEventCount,\n });\n yield {\n type: \"retry\" as const,\n reason: \"stream_stall\" as const,\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n silent: stallRetries <= 2,\n };\n await new Promise((r) => setTimeout(r, delayMs));\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall retries exhausted — surface a clear error so the UI\n // can distinguish \"gave up after stalls\" from \"completed normally\".\n if (idleTimedOut && !options.signal?.aborted) {\n diag(\"stall_exhausted\", {\n stallRetries: MAX_STALL_RETRIES,\n provider: options.provider,\n model: options.model,\n });\n yield {\n type: \"error\" as const,\n error: new Error(\n `The API provider's stream stalled ${MAX_STALL_RETRIES} times — the provider may be experiencing capacity issues. ` +\n `Your conversation is preserved. Send another message to retry.`,\n ),\n };\n break;\n }\n // Tool pairing 400: orphaned tool_result or tool_use in message history.\n // Run repair and retry once — if repair can't fix it, surface the error.\n if (isToolPairingError(err) && !toolPairingRepaired) {\n toolPairingRepaired = true;\n diag(\"tool_pairing_repair\", { error: errMsg.slice(0, 200) });\n repairToolPairingAdjacent(messages);\n turn--;\n continue;\n }\n // Abort errors (user cancellation) — exit loop cleanly instead of\n // crashing the process with an unhandled rejection.\n if (isAbortError(err) || options.signal?.aborted) {\n diag(\"aborted\", { turn, provider: options.provider, model: options.model });\n break;\n }\n // Unhandled error — log before throwing so the crash is traceable\n diag(\"unhandled_error\", {\n error: errMsg.slice(0, 500),\n turn,\n provider: options.provider,\n model: options.model,\n });\n throw err;\n } finally {\n if (idleTimer) clearTimeout(idleTimer);\n if (hardTimer) clearTimeout(hardTimer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n // Reset retry counters after successful call\n overflowRetries = 0;\n overloadRetries = 0;\n stallRetries = 0;\n\n // Detect empty/degenerate responses — the API occasionally returns 0 tokens\n // with no content, or \"thinks\" without producing actionable output.\n // Reasoning models (MiMo, DeepSeek) may report outputTokens > 0 from\n // thinking alone while producing no text or tool calls — still a dud.\n const contentArr = Array.isArray(response.message.content) ? response.message.content : null;\n const hasActionableContent =\n response.message.content !== \"\" &&\n contentArr !== null &&\n contentArr.some(\n (p) => p.type === \"text\" || p.type === \"tool_call\" || p.type === \"server_tool_call\",\n );\n if (!hasActionableContent) {\n if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {\n emptyResponseRetries++;\n diag(\"retry\", {\n reason: \"empty_response\",\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n provider: options.provider,\n model: options.model,\n contentTypes: contentArr?.map((p) => p.type).join(\",\") ?? \"empty\",\n });\n yield {\n type: \"retry\" as const,\n reason: \"empty_response\" as const,\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n delayMs: 0,\n };\n turn--; // Don't count the failed turn\n continue;\n }\n // Exhausted retries — fall through and let the agent finish\n }\n emptyResponseRetries = 0;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n\n // If no tool calls to execute, check for steering messages before stopping.\n // Check content (not just stopReason) because some providers (e.g. GLM)\n // return finish_reason=\"stop\" even when tool calls are present.\n if (response.stopReason !== \"tool_use\" && allToolCalls.length === 0) {\n // Check for queued steering messages — if present, inject and continue\n // the loop instead of returning (follow-up pattern).\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue; // Next iteration will call LLM with injected messages\n }\n }\n // Follow-up: lower priority than steering — only when agent would otherwise stop.\n if (options.getFollowUpMessages) {\n const followUp = await options.getFollowUpMessages();\n if (followUp && followUp.length > 0) {\n for (const msg of followUp) {\n yield { type: \"follow_up_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue;\n }\n }\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: string;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: resultContent,\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n const resultsMap = new Map(results.map((r) => [r.toolCallId, r]));\n for (const tc of toolCalls) {\n const r = resultsMap.get(tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n let toolsAborted = false;\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } catch (err) {\n // Tool event stream aborted (Ctrl+C) — don't propagate, just mark\n // so the finally block can clean up and the loop can exit.\n if (isAbortError(err) || options.signal?.aborted) {\n toolsAborted = true;\n } else {\n throw err;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n const resolvedIds = new Set(toolResults.map((r) => r.toolCallId));\n for (const tc of toolCalls) {\n if (!resolvedIds.has(tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n // Guard: cap oversized tool results before they enter conversation history.\n // Uses head+tail strategy to preserve error messages / closing structure at the end.\n if (options.maxToolResultChars) {\n const HARD_MAX = 400_000; // absolute ceiling regardless of context window\n const max = Math.min(options.maxToolResultChars, HARD_MAX);\n for (const tr of toolResults) {\n if (tr.content.length > max) {\n // Keep 70% head + 30% tail to preserve errors/diagnostics at the end\n const headChars = Math.floor(max * 0.7);\n const tailChars = max - headChars;\n const head = tr.content.slice(0, headChars);\n const tail = tr.content.slice(-tailChars);\n const omitted = tr.content.length - headChars - tailChars;\n tr.content = head + `\\n\\n[... ${omitted} characters omitted ...]\\n\\n` + tail;\n }\n }\n }\n\n messages.push({ role: \"tool\", content: toolResults });\n }\n\n // Exit loop after cleaning up aborted tools\n if (toolsAborted) break;\n\n // ── Steering messages: inject user messages queued during tool execution ──\n // Polled after tools complete so the next LLM call sees them in context.\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n }\n } finally {\n // Sanitize orphaned server_tool_use blocks on abort.\n // When a stream is aborted mid-server-tool (e.g. web_search), the\n // assistant message containing the server_tool_use may already be in\n // the messages array, but the corresponding web_search_tool_result\n // never arrived. The API rejects the next request with a 400 if it\n // finds an unmatched server_tool_use, so we strip it here.\n sanitizeOrphanedServerTools(messages);\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n\n/**\n * Remove orphaned server_tool_use blocks from the last assistant message.\n * When a stream is aborted mid-server-tool (e.g. web_search), the assistant\n * message may contain a server_tool_call without a matching server_tool_result.\n * The API rejects the next request if these are unmatched.\n */\nfunction sanitizeOrphanedServerTools(messages: Message[]): void {\n // Find the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) break;\n\n // Collect server_tool_call ids and matched server_tool_result ids\n const serverToolIds = new Set<string>();\n const resultToolIds = new Set<string>();\n for (const part of msg.content) {\n if (part.type === \"server_tool_call\") serverToolIds.add(part.id);\n if (part.type === \"server_tool_result\") resultToolIds.add(part.toolUseId);\n }\n\n // Find unmatched server_tool_call blocks\n const orphanedIds = new Set<string>();\n for (const id of serverToolIds) {\n if (!resultToolIds.has(id)) orphanedIds.add(id);\n }\n\n if (orphanedIds.size === 0) break;\n\n // Strip orphaned server_tool_call blocks from the content\n const filtered = msg.content.filter(\n (part) => !(part.type === \"server_tool_call\" && orphanedIds.has(part.id)),\n );\n\n if (filtered.length === 0) {\n // Nothing left — remove the entire message\n messages.splice(i, 1);\n } else {\n (msg as { content: ContentPart[] }).content = filtered;\n }\n break;\n }\n}\n\n/**\n * Ensure every assistant message with tool_call blocks is immediately followed\n * by a tool message with matching tool_result entries. This prevents Anthropic\n * API 400 errors (\"tool_use ids found without tool_result blocks immediately\n * after\") that can occur after compaction, session restore, or abort recovery.\n *\n * Repairs in-place by inserting synthetic tool_result messages where needed.\n */\nfunction repairToolPairingAdjacent(messages: Message[]): void {\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) continue;\n\n const toolCallIds = (msg.content as ContentPart[])\n .filter((p) => p.type === \"tool_call\")\n .map((p) => (p as ContentPart & { type: \"tool_call\"; id: string }).id);\n if (toolCallIds.length === 0) continue;\n\n const next = messages[i + 1];\n if (next?.role === \"tool\" && Array.isArray(next.content)) {\n // Tool message exists — check for missing results\n const existingIds = new Set((next.content as ToolResult[]).map((r) => r.toolCallId));\n const missing = toolCallIds.filter((id) => !existingIds.has(id));\n if (missing.length > 0) {\n for (const id of missing) {\n (next.content as ToolResult[]).push({\n type: \"tool_result\",\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n });\n }\n }\n } else {\n // No tool message follows — insert a synthetic one\n messages.splice(i + 1, 0, {\n role: \"tool\" as const,\n content: toolCallIds.map((id) => ({\n type: \"tool_result\" as const,\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n })),\n });\n }\n }\n\n // Reverse repair: strip tool_result entries whose tool_use_id has no matching\n // tool_call in the preceding assistant message. This can happen when compaction\n // or stall recovery removes an assistant message but leaves its tool_result behind.\n const toolCallIdSet = new Set<string>();\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n for (const p of msg.content as ContentPart[]) {\n if (p.type === \"tool_call\") toolCallIdSet.add((p as ToolCall).id);\n }\n }\n if (msg.role === \"tool\" && Array.isArray(msg.content)) {\n const results = msg.content as ToolResult[];\n const filtered = results.filter((r) => toolCallIdSet.has(r.toolCallId));\n if (filtered.length === 0) {\n // Entire tool message is orphaned — remove it\n messages.splice(i, 1);\n i--;\n } else if (filtered.length < results.length) {\n (msg as { content: ToolResult[] }).content = filtered;\n }\n }\n }\n}\n"],"mappings":"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","names":["EventStream","EventStream"]}
1
+ {"version":3,"sources":["../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n private steeringQueue: Message[] = [];\n private followUpQueue: Message[] = [];\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n /** Queue a steering message for injection after current tool execution completes. */\n steer(msg: Message): void {\n this.steeringQueue.push(msg);\n }\n\n /** Queue a follow-up message for injection when the agent would otherwise stop. */\n followUp(msg: Message): void {\n this.followUpQueue.push(msg);\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const optionsWithQueues: AgentOptions = {\n ...this.options,\n getSteeringMessages: async () => {\n const callerResult = (await this.options.getSteeringMessages?.()) ?? [];\n const queued = this.steeringQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n getFollowUpMessages: async () => {\n const callerResult = (await this.options.getFollowUpMessages?.()) ?? [];\n const queued = this.followUpQueue.splice(0);\n const all = [...(callerResult ?? []), ...queued];\n return all.length > 0 ? all : null;\n },\n };\n\n const generator = agentLoop(this.messages, optionsWithQueues);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type ToolResultContent,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 200;\n\n/**\n * Lightweight stream diagnostic callback. When set, the agent loop calls this\n * at every phase boundary with timing and state info. This lets the hosting\n * app (ezcoder, come-alive, etc.) log stall diagnostics without the agent\n * package needing fs/process dependencies.\n */\nexport type StreamDiagnosticFn = (phase: string, data?: Record<string, unknown>) => void;\n\n/** Global diagnostic hook — set by the hosting app before calling agentLoop. */\nlet _diagFn: StreamDiagnosticFn | null = null;\n\n/** Register a diagnostic callback for stream stall tracing. */\nexport function setStreamDiagnostic(fn: StreamDiagnosticFn | null): void {\n _diagFn = fn;\n}\n\nfunction diag(phase: string, data?: Record<string, unknown>): void {\n _diagFn?.(phase, data);\n}\n\n/**\n * Detect abort errors — user-initiated cancellation or AbortSignal.\n * These should be caught and handled gracefully, not re-thrown.\n */\nexport function isAbortError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"AbortError\") return true;\n const msg = err.message.toLowerCase();\n return msg.includes(\"aborted\") || msg.includes(\"abort\");\n}\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect billing/quota errors — these should NOT be retried.\n * GLM returns HTTP 429 with \"Insufficient balance\" for quota exhaustion.\n */\nexport function isBillingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"no resource package\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\") ||\n msg.includes(\"recharge\") ||\n msg.includes(\"subscription plan\") ||\n msg.includes(\"does not yet include access\") ||\n msg.includes(\"token quota\") ||\n msg.includes(\"exceeded_current_quota_error\") ||\n msg.includes(\"check your account balance\")\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n * Excludes billing/quota errors which won't resolve with a retry.\n */\n/**\n * Detect tool pairing errors — orphaned tool_use or tool_result blocks.\n * These are 400 errors that can be recovered by repairing the message history.\n */\nexport function isToolPairingError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n (msg.includes(\"tool_use\") && msg.includes(\"tool_result\")) ||\n msg.includes(\"unexpected `tool_use_id`\") ||\n msg.includes(\"tool_use ids found without\") ||\n // Moonshot/OpenAI-compatible: \"tool call id <id> is not found\"\n (msg.includes(\"tool call id\") && msg.includes(\"is not found\"))\n );\n}\n\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isBillingError(err)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\n/**\n * Detect malformed-stream errors — the SDK's SSE decoder threw a JSON parse\n * error mid-stream, typically because a chunk was truncated or corrupted by\n * an intermediary (CDN, proxy). Same class of transport failure as a stall:\n * replaying the request — and ideally flipping to non-streaming — recovers.\n */\nexport function isMalformedStream(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (err.name === \"SyntaxError\") return true;\n const cause = (err as { cause?: unknown }).cause;\n if (cause instanceof Error && cause.name === \"SyntaxError\") return true;\n const msg = err.message;\n // V8 JSON.parse error messages: \"Expected ... in JSON at position N\"\n // and \"Unexpected token ... in JSON at position N\"\n return /\\bin JSON at position \\d+/i.test(msg);\n}\n\n/**\n * Promise-returning sleep that rejects with AbortError if `signal` fires.\n * Used by retry backoffs so ESC/Ctrl+C cancel immediately instead of having\n * to wait out the full delay (up to 30s per overload retry × 10 retries).\n */\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(new DOMException(\"Aborted\", \"AbortError\"));\n }\n return new Promise<void>((resolve, reject) => {\n let onAbort: (() => void) | null = null;\n const timer = setTimeout(() => {\n if (onAbort) signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n onAbort = () => {\n clearTimeout(timer);\n reject(new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let firstTurn = true;\n let consecutivePauses = 0;\n let toolPairingRepaired = false;\n let overloadRetries = 0;\n let emptyResponseRetries = 0;\n let stallRetries = 0;\n // Non-streaming fallback mode. After repeated stream stalls, flip to a\n // plain non-streaming request/response -- often survives broken SSE\n // connections (transient CDN / proxy issues) that streaming retries cannot.\n let useNonStreamingFallback = false;\n const MAX_OVERLOAD_RETRIES = 10;\n const MAX_EMPTY_RESPONSE_RETRIES = 2;\n const MAX_STALL_RETRIES = 5;\n // After this many streaming stalls in a row, switch to non-streaming mode\n // for the remaining stall retries. Keeps the first two retries fast (the\n // cheap \"transient glitch\" case) before paying for a full response round-trip.\n const STALL_RETRIES_BEFORE_NON_STREAMING = 2;\n const STALL_DELAY_MS = 1_000; // Brief pause before retry -- just enough to avoid tight loops\n const OVERLOAD_BASE_DELAY_MS = 2_000;\n const OVERLOAD_MAX_DELAY_MS = 30_000;\n const STREAM_FIRST_EVENT_TIMEOUT_MS = 45_000; // 45s to get first event (Opus thinks long)\n const STREAM_IDLE_TIMEOUT_MS = 30_000; // 30s between events once streaming starts\n // Anthropic models can pause 10-20s mid-stream while computing the next chunk\n // (e.g. generating tool call args for a large write). 10s was too aggressive\n // and caused false \"stream stalled\" errors, especially in plan mode.\n const STREAM_HARD_TIMEOUT_MS = 90_000; // 90s absolute cap before output starts\n // Once output events (text_delta) are actively streaming, extend the hard\n // timeout -- long responses (plan mode, detailed explanations) can legitimately\n // take 2-3+ minutes while events flow continuously.\n const STREAM_OUTPUT_HARD_TIMEOUT_MS = 300_000; // 5min hard cap once output is flowing\n // Reasoning models (MiMo) can pause 3-5 minutes between thinking and output\n // generation. Once we've seen thinking events, extend timeouts significantly.\n const STREAM_THINKING_IDLE_TIMEOUT_MS = 300_000; // 5min idle after thinking\n const STREAM_THINKING_HARD_TIMEOUT_MS = 600_000; // 10min hard cap with thinking\n // Non-streaming mode has no per-event idle -- the entire response arrives in\n // one HTTP round-trip. Use a single generous hard cap instead. This matches\n // Claude Code's v2.1.110/111 behaviour: cap non-streaming retries so API\n // unreachability doesn't cause multi-minute hangs, but not so aggressively\n // that slow-but-healthy backends get killed.\n const NON_STREAMING_HARD_TIMEOUT_MS = 300_000; // 5min for full non-streaming response\n\n try {\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // Estimate message payload size for diagnostics\n let msgChars = 0;\n for (const m of messages) {\n if (typeof m.content === \"string\") msgChars += m.content.length;\n else if (Array.isArray(m.content)) {\n for (const p of m.content) {\n if (\"text\" in p && typeof p.text === \"string\") msgChars += p.text.length;\n if (\"content\" in p && typeof p.content === \"string\") msgChars += p.content.length;\n }\n }\n }\n diag(\"turn_start\", {\n turn,\n messages: messages.length,\n chars: msgChars,\n provider: options.provider,\n model: options.model,\n });\n\n // ── Initial steering poll: catch messages queued before the first LLM call ──\n if (firstTurn && options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n firstTurn = false;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n diag(\"transform_start\");\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n diag(\"transform_compacted\", {\n before: messages.length,\n after: transformed.length,\n });\n messages.length = 0;\n messages.push(...transformed);\n }\n diag(\"transform_end\");\n }\n\n // ── Repair tool pairing: ensure every tool_use has an adjacent tool_result ──\n repairToolPairingAdjacent(messages);\n\n // ── Call LLM with overflow recovery ──\n let response;\n // Per-attempt abort controller: allows idle timeout to abort the stream\n // without affecting the caller's signal. The caller's abort is forwarded.\n const streamController = new AbortController();\n let idleTimer: ReturnType<typeof setTimeout> | null = null;\n let hardTimer: ReturnType<typeof setTimeout> | null = null;\n let idleTimedOut = false;\n\n // Stream event counters — declared here so timeout callbacks can access them\n let streamEventCount = 0;\n let lastEventTime = Date.now();\n let streamCallStart = Date.now();\n // Track event types for diagnostics — shows what arrived before a stall\n const eventTypeCounts: Record<string, number> = {};\n let lastEventType = \"\";\n // Track consumer processing time — helps distinguish \"API stopped sending\"\n // from \"our consumer was slow to pull the next event\"\n let lastYieldEndTime = Date.now();\n let maxConsumerLagMs = 0;\n\n // Forward caller abort to the per-attempt controller\n const forwardAbort = () => streamController.abort();\n options.signal?.addEventListener(\"abort\", forwardAbort, { once: true });\n\n // Three-phase idle timeout:\n // - Before first event: STREAM_FIRST_EVENT_TIMEOUT_MS (45s) -- Opus can\n // take 30s+ to start on large contexts, that's not a stall.\n // - After output event (text_delta, server_toolcall): STREAM_IDLE_TIMEOUT_MS\n // (10s) -- once output is streaming, 10s of silence is dead. Retry fast.\n // - After thinking events only: STREAM_THINKING_IDLE_TIMEOUT_MS (5min) --\n // reasoning models (MiMo) can pause minutes between thinking and output.\n //\n // In non-streaming fallback mode the entire response arrives in a single\n // HTTP round-trip, so the idle timer is disabled -- only the hard timeout\n // applies. Synthesized events all arrive at once when the response returns.\n let hasReceivedEvent = false;\n let hasReceivedThinking = false;\n const resetIdleTimer = () => {\n if (useNonStreamingFallback) return; // no inter-event idle in non-streaming mode\n if (idleTimer) clearTimeout(idleTimer);\n const timeoutMs = hasReceivedEvent\n ? STREAM_IDLE_TIMEOUT_MS\n : hasReceivedThinking\n ? STREAM_THINKING_IDLE_TIMEOUT_MS\n : STREAM_FIRST_EVENT_TIMEOUT_MS;\n idleTimer = setTimeout(() => {\n diag(\"idle_timeout_fired\", {\n events: streamEventCount,\n sinceLastEventMs: Date.now() - lastEventTime,\n lastEventType,\n maxConsumerLagMs,\n phase: hasReceivedEvent\n ? \"mid_stream\"\n : hasReceivedThinking\n ? \"post_thinking\"\n : \"first_event\",\n eventTypes: eventTypeCounts,\n });\n idleTimedOut = true;\n streamController.abort();\n }, timeoutMs);\n };\n\n // Hard timeout: absolute cap per LLM call. Safety net for streams that\n // keep sending sparse events (e.g. keep-alive pings) but never complete.\n // Extended dynamically when thinking events arrive (see thinking_delta handler).\n // Non-streaming fallback uses a single larger cap since there's no stream\n // to observe -- just wait for the full response up to the cap.\n let hardTimeoutMs = useNonStreamingFallback\n ? NON_STREAMING_HARD_TIMEOUT_MS\n : STREAM_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", {\n events: typeof streamEventCount !== \"undefined\" ? streamEventCount : 0,\n nonStreaming: useNonStreamingFallback,\n });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n\n try {\n diag(\"stream_call\", { nonStreaming: useNonStreamingFallback });\n streamCallStart = Date.now();\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: streamController.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n supportsImages: options.supportsImages,\n compaction: options.compaction,\n clearToolUses: options.clearToolUses,\n // Flip to non-streaming fallback after repeated stream stalls.\n ...(useNonStreamingFallback ? { streaming: false } : {}),\n });\n diag(\"stream_created\", { setupMs: Date.now() - streamCallStart });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas — reset idle timer on each event\n streamEventCount = 0;\n hasReceivedEvent = false;\n lastEventTime = Date.now();\n streamCallStart = Date.now();\n resetIdleTimer();\n for await (const event of result) {\n // Measure consumer lag: time between finishing previous yield and\n // receiving this event. High lag means React rendering is starving\n // the stream consumer. Low lag means the API was slow to send.\n const pullTime = Date.now();\n const consumerLag = pullTime - lastYieldEndTime;\n if (consumerLag > maxConsumerLagMs) maxConsumerLagMs = consumerLag;\n\n streamEventCount++;\n eventTypeCounts[event.type] = (eventTypeCounts[event.type] ?? 0) + 1;\n lastEventType = event.type;\n\n // Flip to mid-stream timeout on confirmed output events — text\n // deltas, completed tool calls, and tool call deltas (large file\n // writes can stream toolcall_delta for minutes without any text_delta).\n // Reasoning models (MiMo) are handled separately below — they can\n // stream hundreds of thinking events then pause minutes before output.\n if (\n (event.type === \"text_delta\" ||\n event.type === \"server_toolcall\" ||\n event.type === \"toolcall_delta\") &&\n !hasReceivedEvent\n ) {\n hasReceivedEvent = true;\n // Extend hard timeout now that output is actively streaming.\n // Long responses (plan mode, detailed code) can exceed 90s while\n // events flow continuously — the idle timeout (10s) catches real stalls.\n if (hardTimer && hardTimeoutMs < STREAM_OUTPUT_HARD_TIMEOUT_MS) {\n clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_OUTPUT_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n }\n // Track thinking events — extends idle timeout and hard timeout\n // so reasoning models aren't killed during thinking→output transition.\n if (event.type === \"thinking_delta\" && !hasReceivedThinking) {\n hasReceivedThinking = true;\n // Extend the hard timeout now that we know the model is reasoning\n if (hardTimer) clearTimeout(hardTimer);\n hardTimeoutMs = STREAM_THINKING_HARD_TIMEOUT_MS;\n hardTimer = setTimeout(() => {\n diag(\"hard_timeout_fired\", { events: streamEventCount });\n idleTimedOut = true;\n streamController.abort();\n }, hardTimeoutMs);\n }\n\n const now = Date.now();\n const gap = now - lastEventTime;\n // Log first event and any suspiciously long gaps\n if (streamEventCount === 1) {\n diag(\"first_event\", { type: event.type, ttfMs: now - streamCallStart });\n } else if (gap > 3000) {\n diag(\"slow_gap\", {\n type: event.type,\n gapMs: gap,\n eventNum: streamEventCount,\n sinceStartMs: now - streamCallStart,\n });\n }\n lastEventTime = now;\n resetIdleTimer();\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n } else if (event.type === \"toolcall_delta\") {\n yield {\n type: \"toolcall_delta\" as const,\n chars: event.argsJson?.length ?? 0,\n };\n }\n lastYieldEndTime = Date.now();\n }\n\n diag(\"stream_done\", {\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n maxConsumerLagMs,\n eventTypes: eventTypeCounts,\n });\n response = await result.response;\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n diag(\"stream_error\", {\n error: errMsg.slice(0, 200),\n events: streamEventCount,\n totalMs: Date.now() - streamCallStart,\n idleTimedOut,\n aborted: !!options.signal?.aborted,\n eventTypes: eventTypeCounts,\n provider: options.provider,\n model: options.model,\n });\n // Context overflow: surface immediately as an error.\n // The pre-turn transformContext check should prevent overflow proactively.\n // Compacting mid-retry is unreliable (calls the same provider, may not\n // reduce enough) and was removed along with stall-compaction.\n if (isContextOverflow(err)) {\n yield { type: \"error\" as const, error: err instanceof Error ? err : new Error(errMsg) };\n throw err;\n }\n // Overloaded / rate-limited: exponential backoff, retry up to 10 times\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n const delayMs = Math.min(\n OVERLOAD_BASE_DELAY_MS * 2 ** (overloadRetries - 1),\n OVERLOAD_MAX_DELAY_MS,\n );\n diag(\"retry\", {\n reason: \"overloaded\",\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n });\n yield {\n type: \"retry\" as const,\n reason: \"overloaded\" as const,\n attempt: overloadRetries,\n maxAttempts: MAX_OVERLOAD_RETRIES,\n delayMs,\n };\n await abortableSleep(delayMs, options.signal);\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall: the API connection hung without closing.\n // Malformed stream: the SDK's SSE decoder hit truncated/corrupted JSON.\n // Both are transport failures — retry with exponential backoff and flip\n // to non-streaming mode after STALL_RETRIES_BEFORE_NON_STREAMING attempts,\n // since broken SSE often recovers when replayed as plain HTTP.\n const malformed = isMalformedStream(err);\n const transportFailure = (idleTimedOut || malformed) && !options.signal?.aborted;\n if (transportFailure && stallRetries < MAX_STALL_RETRIES) {\n stallRetries++;\n if (!useNonStreamingFallback && stallRetries >= STALL_RETRIES_BEFORE_NON_STREAMING) {\n useNonStreamingFallback = true;\n diag(\"non_streaming_fallback_enabled\", {\n stallRetries,\n provider: options.provider,\n model: options.model,\n cause: malformed ? \"malformed_stream\" : \"stream_stall\",\n });\n }\n const delayMs = Math.min(STALL_DELAY_MS * 2 ** (stallRetries - 1), 8_000);\n diag(\"retry\", {\n reason: malformed ? \"malformed_stream\" : \"stream_stall\",\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n events: streamEventCount,\n nonStreaming: useNonStreamingFallback,\n });\n yield {\n type: \"retry\" as const,\n reason: \"stream_stall\" as const,\n attempt: stallRetries,\n maxAttempts: MAX_STALL_RETRIES,\n delayMs,\n silent: stallRetries <= 2,\n };\n await abortableSleep(delayMs, options.signal);\n turn--; // Don't count the failed turn\n continue;\n }\n // Stream stall retries exhausted — surface a clear error so the UI\n // can distinguish \"gave up after stalls\" from \"completed normally\".\n if (transportFailure) {\n diag(\"stall_exhausted\", {\n stallRetries: MAX_STALL_RETRIES,\n provider: options.provider,\n model: options.model,\n });\n yield {\n type: \"error\" as const,\n error: new Error(\n `The API provider's stream stalled ${MAX_STALL_RETRIES} times — the provider may be experiencing capacity issues. ` +\n `Your conversation is preserved. Send another message to retry.`,\n ),\n };\n break;\n }\n // Tool pairing 400: orphaned tool_result or tool_use in message history.\n // Run repair and retry once — if repair can't fix it, surface the error.\n if (isToolPairingError(err) && !toolPairingRepaired) {\n toolPairingRepaired = true;\n diag(\"tool_pairing_repair\", { error: errMsg.slice(0, 200) });\n repairToolPairingAdjacent(messages);\n turn--;\n continue;\n }\n // Abort errors (user cancellation) — exit loop cleanly instead of\n // crashing the process with an unhandled rejection.\n if (isAbortError(err) || options.signal?.aborted) {\n diag(\"aborted\", { turn, provider: options.provider, model: options.model });\n break;\n }\n // Unhandled error — log before throwing so the crash is traceable\n diag(\"unhandled_error\", {\n error: errMsg.slice(0, 500),\n turn,\n provider: options.provider,\n model: options.model,\n });\n throw err;\n } finally {\n if (idleTimer) clearTimeout(idleTimer);\n if (hardTimer) clearTimeout(hardTimer);\n options.signal?.removeEventListener(\"abort\", forwardAbort);\n }\n\n overloadRetries = 0;\n stallRetries = 0;\n\n // Detect empty/degenerate responses — the API occasionally returns 0 tokens\n // with no content, or \"thinks\" without producing actionable output.\n // Reasoning models (MiMo, DeepSeek) may report outputTokens > 0 from\n // thinking alone while producing no text or tool calls — still a dud.\n const contentArr = Array.isArray(response.message.content) ? response.message.content : null;\n const hasActionableContent =\n response.message.content !== \"\" &&\n contentArr !== null &&\n contentArr.some(\n (p) => p.type === \"text\" || p.type === \"tool_call\" || p.type === \"server_tool_call\",\n );\n if (!hasActionableContent) {\n if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {\n emptyResponseRetries++;\n diag(\"retry\", {\n reason: \"empty_response\",\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n provider: options.provider,\n model: options.model,\n contentTypes: contentArr?.map((p) => p.type).join(\",\") ?? \"empty\",\n });\n yield {\n type: \"retry\" as const,\n reason: \"empty_response\" as const,\n attempt: emptyResponseRetries,\n maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,\n delayMs: 0,\n };\n turn--; // Don't count the failed turn — keep useNonStreamingFallback set\n // so the retry doesn't bounce back into a streaming connection that\n // will stall again with the same upstream problem.\n continue;\n }\n // Exhausted retries — fall through and let the agent finish\n }\n emptyResponseRetries = 0;\n\n // Only clear the non-streaming fallback after an actionable response —\n // an empty non-streaming reply means the upstream issue hasn't resolved,\n // so staying in non-streaming mode avoids retrying into another stall.\n useNonStreamingFallback = false;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n\n // If no tool calls to execute, check for steering messages before stopping.\n // Check content (not just stopReason) because some providers (e.g. GLM)\n // return finish_reason=\"stop\" even when tool calls are present.\n if (response.stopReason !== \"tool_use\" && allToolCalls.length === 0) {\n // Check for queued steering messages — if present, inject and continue\n // the loop instead of returning (follow-up pattern).\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue; // Next iteration will call LLM with injected messages\n }\n }\n // Follow-up: lower priority than steering — only when agent would otherwise stop.\n if (options.getFollowUpMessages) {\n const followUp = await options.getFollowUpMessages();\n if (followUp && followUp.length > 0) {\n for (const msg of followUp) {\n yield { type: \"follow_up_message\" as const, content: msg.content };\n messages.push(msg);\n }\n continue;\n }\n }\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: ToolResultContent;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: toolResultPreview(resultContent),\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n const resultsMap = new Map(results.map((r) => [r.toolCallId, r]));\n for (const tc of toolCalls) {\n const r = resultsMap.get(tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n let toolsAborted = false;\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } catch (err) {\n // Tool event stream aborted (Ctrl+C) — don't propagate, just mark\n // so the finally block can clean up and the loop can exit.\n if (isAbortError(err) || options.signal?.aborted) {\n toolsAborted = true;\n } else {\n throw err;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n const resolvedIds = new Set(toolResults.map((r) => r.toolCallId));\n for (const tc of toolCalls) {\n if (!resolvedIds.has(tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n // Guard: cap oversized tool results before they enter conversation history.\n // Uses head+tail strategy to preserve error messages / closing structure at the end.\n if (options.maxToolResultChars) {\n const HARD_MAX = 400_000; // absolute ceiling regardless of context window\n const max = Math.min(options.maxToolResultChars, HARD_MAX);\n for (const tr of toolResults) {\n // Only truncate string content — array content (text+image blocks)\n // is already size-bounded by the image resizer.\n if (typeof tr.content === \"string\" && tr.content.length > max) {\n // Keep 70% head + 30% tail to preserve errors/diagnostics at the end\n const headChars = Math.floor(max * 0.7);\n const tailChars = max - headChars;\n const head = tr.content.slice(0, headChars);\n const tail = tr.content.slice(-tailChars);\n const omitted = tr.content.length - headChars - tailChars;\n tr.content = head + `\\n\\n[... ${omitted} characters omitted ...]\\n\\n` + tail;\n }\n }\n }\n\n messages.push({ role: \"tool\", content: toolResults });\n }\n\n // Exit loop after cleaning up aborted tools\n if (toolsAborted) break;\n\n // ── Steering messages: inject user messages queued during tool execution ──\n // Polled after tools complete so the next LLM call sees them in context.\n if (options.getSteeringMessages) {\n const steering = await options.getSteeringMessages();\n if (steering && steering.length > 0) {\n for (const msg of steering) {\n yield { type: \"steering_message\" as const, content: msg.content };\n messages.push(msg);\n }\n }\n }\n }\n } finally {\n // Sanitize orphaned server_tool_use blocks on abort.\n // When a stream is aborted mid-server-tool (e.g. web_search), the\n // assistant message containing the server_tool_use may already be in\n // the messages array, but the corresponding web_search_tool_result\n // never arrived. The API rejects the next request with a 400 if it\n // finds an unmatched server_tool_use, so we strip it here.\n sanitizeOrphanedServerTools(messages);\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\n/** Flatten tool result content to a plain-text preview for the tool_call_end event.\n * Image blocks become a \"[image]\" placeholder so the UI has something to render. */\nfunction toolResultPreview(content: ToolResultContent): string {\n if (typeof content === \"string\") return content;\n return content\n .map((block) => (block.type === \"text\" ? block.text : `[image ${block.mediaType}]`))\n .join(\"\\n\");\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n\n/**\n * Remove orphaned server_tool_use blocks from the last assistant message.\n * When a stream is aborted mid-server-tool (e.g. web_search), the assistant\n * message may contain a server_tool_call without a matching server_tool_result.\n * The API rejects the next request if these are unmatched.\n */\nfunction sanitizeOrphanedServerTools(messages: Message[]): void {\n // Find the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) break;\n\n // Collect server_tool_call ids and matched server_tool_result ids\n const serverToolIds = new Set<string>();\n const resultToolIds = new Set<string>();\n for (const part of msg.content) {\n if (part.type === \"server_tool_call\") serverToolIds.add(part.id);\n if (part.type === \"server_tool_result\") resultToolIds.add(part.toolUseId);\n }\n\n // Find unmatched server_tool_call blocks\n const orphanedIds = new Set<string>();\n for (const id of serverToolIds) {\n if (!resultToolIds.has(id)) orphanedIds.add(id);\n }\n\n if (orphanedIds.size === 0) break;\n\n // Strip orphaned server_tool_call blocks from the content\n const filtered = msg.content.filter(\n (part) => !(part.type === \"server_tool_call\" && orphanedIds.has(part.id)),\n );\n\n if (filtered.length === 0) {\n // Nothing left — remove the entire message\n messages.splice(i, 1);\n } else {\n (msg as { content: ContentPart[] }).content = filtered;\n }\n break;\n }\n}\n\n/**\n * Ensure every assistant message with tool_call blocks is immediately followed\n * by a tool message with matching tool_result entries. This prevents Anthropic\n * API 400 errors (\"tool_use ids found without tool_result blocks immediately\n * after\") that can occur after compaction, session restore, or abort recovery.\n *\n * Repairs in-place by inserting synthetic tool_result messages where needed.\n */\nfunction repairToolPairingAdjacent(messages: Message[]): void {\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role !== \"assistant\") continue;\n if (typeof msg.content === \"string\" || !Array.isArray(msg.content)) continue;\n\n const toolCallIds = (msg.content as ContentPart[])\n .filter((p) => p.type === \"tool_call\")\n .map((p) => (p as ContentPart & { type: \"tool_call\"; id: string }).id);\n if (toolCallIds.length === 0) continue;\n\n const next = messages[i + 1];\n if (next?.role === \"tool\" && Array.isArray(next.content)) {\n // Tool message exists — check for missing results\n const existingIds = new Set((next.content as ToolResult[]).map((r) => r.toolCallId));\n const missing = toolCallIds.filter((id) => !existingIds.has(id));\n if (missing.length > 0) {\n for (const id of missing) {\n (next.content as ToolResult[]).push({\n type: \"tool_result\",\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n });\n }\n }\n } else {\n // No tool message follows — insert a synthetic one\n messages.splice(i + 1, 0, {\n role: \"tool\" as const,\n content: toolCallIds.map((id) => ({\n type: \"tool_result\" as const,\n toolCallId: id,\n content: \"Tool execution was interrupted.\",\n isError: true,\n })),\n });\n }\n }\n\n // Reverse repair: strip tool_result entries whose tool_use_id has no matching\n // tool_call in the preceding assistant message. This can happen when compaction\n // or stall recovery removes an assistant message but leaves its tool_result behind.\n const toolCallIdSet = new Set<string>();\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]!;\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n for (const p of msg.content as ContentPart[]) {\n if (p.type === \"tool_call\") toolCallIdSet.add((p as ToolCall).id);\n }\n }\n if (msg.role === \"tool\" && Array.isArray(msg.content)) {\n const results = msg.content as ToolResult[];\n const filtered = results.filter((r) => toolCallIdSet.has(r.toolCallId));\n if (filtered.length === 0) {\n // Entire tool message is orphaned — remove it\n messages.splice(i, 1);\n i--;\n } else if (filtered.length < results.length) {\n (msg as { content: ToolResult[] }).content = filtered;\n }\n }\n }\n}\n"],"mappings":"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","names":["EventStream","EventStream"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prestyj/agent",
3
- "version": "4.3.15",
3
+ "version": "4.3.54",
4
4
  "type": "module",
5
5
  "description": "Agentic loop system with tool execution for LLMs",
6
6
  "license": "MIT",
@@ -24,11 +24,11 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "zod": "^4.3.6",
27
- "@prestyj/ai": "4.3.15"
27
+ "@prestyj/ai": "4.3.54"
28
28
  },
29
29
  "devDependencies": {
30
30
  "typescript": "^5.9.3",
31
- "vitest": "^4.1.0"
31
+ "vitest": "^4.1.4"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"