@witqq/agent-sdk 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
  2. package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -102
  3. package/dist/auth/index.cjs +72 -1
  4. package/dist/auth/index.cjs.map +1 -1
  5. package/dist/auth/index.d.cts +21 -154
  6. package/dist/auth/index.d.ts +21 -154
  7. package/dist/auth/index.js +72 -1
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/backends/claude.cjs +480 -261
  10. package/dist/backends/claude.cjs.map +1 -1
  11. package/dist/backends/claude.d.cts +3 -1
  12. package/dist/backends/claude.d.ts +3 -1
  13. package/dist/backends/claude.js +480 -261
  14. package/dist/backends/claude.js.map +1 -1
  15. package/dist/backends/copilot.cjs +337 -112
  16. package/dist/backends/copilot.cjs.map +1 -1
  17. package/dist/backends/copilot.d.cts +12 -4
  18. package/dist/backends/copilot.d.ts +12 -4
  19. package/dist/backends/copilot.js +337 -112
  20. package/dist/backends/copilot.js.map +1 -1
  21. package/dist/backends/mock-llm.cjs +719 -0
  22. package/dist/backends/mock-llm.cjs.map +1 -0
  23. package/dist/backends/mock-llm.d.cts +37 -0
  24. package/dist/backends/mock-llm.d.ts +37 -0
  25. package/dist/backends/mock-llm.js +717 -0
  26. package/dist/backends/mock-llm.js.map +1 -0
  27. package/dist/backends/vercel-ai.cjs +301 -61
  28. package/dist/backends/vercel-ai.cjs.map +1 -1
  29. package/dist/backends/vercel-ai.d.cts +3 -1
  30. package/dist/backends/vercel-ai.d.ts +3 -1
  31. package/dist/backends/vercel-ai.js +301 -61
  32. package/dist/backends/vercel-ai.js.map +1 -1
  33. package/dist/backends-Cno0gZjy.d.cts +114 -0
  34. package/dist/backends-Cno0gZjy.d.ts +114 -0
  35. package/dist/chat/accumulator.cjs +1 -1
  36. package/dist/chat/accumulator.cjs.map +1 -1
  37. package/dist/chat/accumulator.d.cts +5 -2
  38. package/dist/chat/accumulator.d.ts +5 -2
  39. package/dist/chat/accumulator.js +1 -1
  40. package/dist/chat/accumulator.js.map +1 -1
  41. package/dist/chat/backends.cjs +1084 -821
  42. package/dist/chat/backends.cjs.map +1 -1
  43. package/dist/chat/backends.d.cts +10 -6
  44. package/dist/chat/backends.d.ts +10 -6
  45. package/dist/chat/backends.js +1082 -800
  46. package/dist/chat/backends.js.map +1 -1
  47. package/dist/chat/context.cjs +50 -0
  48. package/dist/chat/context.cjs.map +1 -1
  49. package/dist/chat/context.d.cts +27 -3
  50. package/dist/chat/context.d.ts +27 -3
  51. package/dist/chat/context.js +50 -0
  52. package/dist/chat/context.js.map +1 -1
  53. package/dist/chat/core.cjs +60 -27
  54. package/dist/chat/core.cjs.map +1 -1
  55. package/dist/chat/core.d.cts +41 -382
  56. package/dist/chat/core.d.ts +41 -382
  57. package/dist/chat/core.js +58 -28
  58. package/dist/chat/core.js.map +1 -1
  59. package/dist/chat/errors.cjs +48 -26
  60. package/dist/chat/errors.cjs.map +1 -1
  61. package/dist/chat/errors.d.cts +6 -31
  62. package/dist/chat/errors.d.ts +6 -31
  63. package/dist/chat/errors.js +48 -25
  64. package/dist/chat/errors.js.map +1 -1
  65. package/dist/chat/events.cjs.map +1 -1
  66. package/dist/chat/events.d.cts +6 -2
  67. package/dist/chat/events.d.ts +6 -2
  68. package/dist/chat/events.js.map +1 -1
  69. package/dist/chat/index.cjs +1612 -1125
  70. package/dist/chat/index.cjs.map +1 -1
  71. package/dist/chat/index.d.cts +35 -10
  72. package/dist/chat/index.d.ts +35 -10
  73. package/dist/chat/index.js +1600 -1097
  74. package/dist/chat/index.js.map +1 -1
  75. package/dist/chat/react/theme.css +2517 -0
  76. package/dist/chat/react.cjs +2212 -1158
  77. package/dist/chat/react.cjs.map +1 -1
  78. package/dist/chat/react.d.cts +665 -122
  79. package/dist/chat/react.d.ts +665 -122
  80. package/dist/chat/react.js +2191 -1156
  81. package/dist/chat/react.js.map +1 -1
  82. package/dist/chat/runtime.cjs +405 -186
  83. package/dist/chat/runtime.cjs.map +1 -1
  84. package/dist/chat/runtime.d.cts +92 -28
  85. package/dist/chat/runtime.d.ts +92 -28
  86. package/dist/chat/runtime.js +405 -186
  87. package/dist/chat/runtime.js.map +1 -1
  88. package/dist/chat/server.cjs +2247 -212
  89. package/dist/chat/server.cjs.map +1 -1
  90. package/dist/chat/server.d.cts +451 -90
  91. package/dist/chat/server.d.ts +451 -90
  92. package/dist/chat/server.js +2234 -213
  93. package/dist/chat/server.js.map +1 -1
  94. package/dist/chat/sessions.cjs +64 -66
  95. package/dist/chat/sessions.cjs.map +1 -1
  96. package/dist/chat/sessions.d.cts +37 -118
  97. package/dist/chat/sessions.d.ts +37 -118
  98. package/dist/chat/sessions.js +65 -67
  99. package/dist/chat/sessions.js.map +1 -1
  100. package/dist/chat/sqlite.cjs +536 -0
  101. package/dist/chat/sqlite.cjs.map +1 -0
  102. package/dist/chat/sqlite.d.cts +164 -0
  103. package/dist/chat/sqlite.d.ts +164 -0
  104. package/dist/chat/sqlite.js +527 -0
  105. package/dist/chat/sqlite.js.map +1 -0
  106. package/dist/chat/state.cjs +14 -1
  107. package/dist/chat/state.cjs.map +1 -1
  108. package/dist/chat/state.d.cts +5 -2
  109. package/dist/chat/state.d.ts +5 -2
  110. package/dist/chat/state.js +14 -1
  111. package/dist/chat/state.js.map +1 -1
  112. package/dist/chat/storage.cjs +58 -33
  113. package/dist/chat/storage.cjs.map +1 -1
  114. package/dist/chat/storage.d.cts +18 -8
  115. package/dist/chat/storage.d.ts +18 -8
  116. package/dist/chat/storage.js +59 -34
  117. package/dist/chat/storage.js.map +1 -1
  118. package/dist/errors-C-so0M4t.d.cts +33 -0
  119. package/dist/errors-C-so0M4t.d.ts +33 -0
  120. package/dist/errors-CmVvczxZ.d.cts +28 -0
  121. package/dist/errors-CmVvczxZ.d.ts +28 -0
  122. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-7EIit9Xk.d.ts} +72 -33
  123. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
  124. package/dist/index.cjs +354 -60
  125. package/dist/index.cjs.map +1 -1
  126. package/dist/index.d.cts +294 -123
  127. package/dist/index.d.ts +294 -123
  128. package/dist/index.js +347 -60
  129. package/dist/index.js.map +1 -1
  130. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  131. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  132. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  133. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  134. package/dist/testing.cjs +1107 -0
  135. package/dist/testing.cjs.map +1 -0
  136. package/dist/testing.d.cts +144 -0
  137. package/dist/testing.d.ts +144 -0
  138. package/dist/testing.js +1101 -0
  139. package/dist/testing.js.map +1 -0
  140. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  141. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  142. package/dist/{transport-DX1Nhm4N.d.cts → transport-DLWCN18G.d.cts} +5 -4
  143. package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.d.ts} +5 -4
  144. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  145. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  146. package/dist/types-DgtI1hzh.d.ts +364 -0
  147. package/dist/types-DkSXALKg.d.cts +364 -0
  148. package/package.json +41 -5
  149. package/LICENSE +0 -21
  150. package/README.md +0 -948
  151. package/dist/errors-BDLbNu9w.d.cts +0 -13
  152. package/dist/errors-BDLbNu9w.d.ts +0 -13
  153. package/dist/types-DLZzlJxt.d.ts +0 -39
  154. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,4 +1,42 @@
1
- // src/types.ts
1
+ // src/types/errors.ts
2
+ var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
3
+ "TIMEOUT" /* TIMEOUT */,
4
+ "RATE_LIMIT" /* RATE_LIMIT */,
5
+ "NETWORK" /* NETWORK */,
6
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
7
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
8
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
9
+ ]);
10
+ function isRecoverableErrorCode(code) {
11
+ return RECOVERABLE_CODES.has(code);
12
+ }
13
+ function classifyAgentError(error) {
14
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
15
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
16
+ return "TIMEOUT" /* TIMEOUT */;
17
+ }
18
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
19
+ return "RATE_LIMIT" /* RATE_LIMIT */;
20
+ }
21
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
22
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
23
+ }
24
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
25
+ return "NETWORK" /* NETWORK */;
26
+ }
27
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
28
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
29
+ }
30
+ if (msg.includes("abort") || msg.includes("cancel")) {
31
+ return "ABORTED" /* ABORTED */;
32
+ }
33
+ if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
34
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
35
+ }
36
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
37
+ }
38
+
39
+ // src/types/guards.ts
2
40
  function getTextContent(content) {
3
41
  if (typeof content === "string") return content;
4
42
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
@@ -8,9 +46,18 @@ function getTextContent(content) {
8
46
  var AgentSDKError = class extends Error {
9
47
  /** @internal Marker for cross-bundle identity checks */
10
48
  _agentSDKError = true;
49
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
50
+ code;
51
+ /** Whether this error is safe to retry */
52
+ retryable;
53
+ /** HTTP status code hint for error classification */
54
+ httpStatus;
11
55
  constructor(message, options) {
12
56
  super(message, options);
13
57
  this.name = "AgentSDKError";
58
+ this.code = options?.code;
59
+ this.retryable = options?.retryable ?? false;
60
+ this.httpStatus = options?.httpStatus;
14
61
  }
15
62
  /** Check if an error is an AgentSDKError (works across bundled copies) */
16
63
  static is(error) {
@@ -19,28 +66,41 @@ var AgentSDKError = class extends Error {
19
66
  };
20
67
  var ReentrancyError = class extends AgentSDKError {
21
68
  constructor() {
22
- super("Agent is already running. Await the current run before starting another.");
69
+ super("Agent is already running. Await the current run before starting another.", {
70
+ code: "REENTRANCY" /* REENTRANCY */
71
+ });
23
72
  this.name = "ReentrancyError";
24
73
  }
25
74
  };
26
75
  var DisposedError = class extends AgentSDKError {
27
76
  constructor(entity) {
28
- super(`${entity} has been disposed and cannot be used.`);
77
+ super(`${entity} has been disposed and cannot be used.`, {
78
+ code: "DISPOSED" /* DISPOSED */
79
+ });
29
80
  this.name = "DisposedError";
30
81
  }
31
82
  };
32
83
  var SubprocessError = class extends AgentSDKError {
33
84
  constructor(message, options) {
34
- super(message, options);
85
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
35
86
  this.name = "SubprocessError";
36
87
  }
37
88
  };
38
89
  var AbortError = class extends AgentSDKError {
39
90
  constructor() {
40
- super("Agent run was aborted.");
91
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
41
92
  this.name = "AbortError";
42
93
  }
43
94
  };
95
+ var ActivityTimeoutError = class extends AgentSDKError {
96
+ constructor(timeoutMs) {
97
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
98
+ code: "TIMEOUT" /* TIMEOUT */,
99
+ retryable: true
100
+ });
101
+ this.name = "ActivityTimeoutError";
102
+ }
103
+ };
44
104
 
45
105
  // src/base-agent.ts
46
106
  var BaseAgent = class {
@@ -48,6 +108,7 @@ var BaseAgent = class {
48
108
  abortController = null;
49
109
  config;
50
110
  _cleanupExternalSignal = null;
111
+ _streamMiddleware = [];
51
112
  /** CLI session ID for persistent mode. Override in backends that support it. */
52
113
  get sessionId() {
53
114
  return void 0;
@@ -63,8 +124,11 @@ var BaseAgent = class {
63
124
  this.state = "running";
64
125
  try {
65
126
  const messages = [{ role: "user", content: prompt }];
66
- const result = await this.executeRun(messages, options, ac.signal);
67
- this.enrichAndNotifyUsage(result);
127
+ const result = await this.withRetry(
128
+ () => this.executeRun(messages, options, ac.signal),
129
+ options
130
+ );
131
+ this.enrichAndNotifyUsage(result, options);
68
132
  return result;
69
133
  } finally {
70
134
  this.cleanupRun();
@@ -76,8 +140,11 @@ var BaseAgent = class {
76
140
  const ac = this.createAbortController(options?.signal);
77
141
  this.state = "running";
78
142
  try {
79
- const result = await this.executeRun(messages, options, ac.signal);
80
- this.enrichAndNotifyUsage(result);
143
+ const result = await this.withRetry(
144
+ () => this.executeRun(messages, options, ac.signal),
145
+ options
146
+ );
147
+ this.enrichAndNotifyUsage(result, options);
81
148
  return result;
82
149
  } finally {
83
150
  this.cleanupRun();
@@ -90,13 +157,11 @@ var BaseAgent = class {
90
157
  this.state = "running";
91
158
  try {
92
159
  const messages = [{ role: "user", content: prompt }];
93
- const result = await this.executeRunStructured(
94
- messages,
95
- schema,
96
- options,
97
- ac.signal
160
+ const result = await this.withRetry(
161
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
162
+ options
98
163
  );
99
- this.enrichAndNotifyUsage(result);
164
+ this.enrichAndNotifyUsage(result, options);
100
165
  return result;
101
166
  } finally {
102
167
  this.cleanupRun();
@@ -109,8 +174,10 @@ var BaseAgent = class {
109
174
  this.state = "streaming";
110
175
  try {
111
176
  const messages = [{ role: "user", content: prompt }];
112
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
113
- yield* this.heartbeatStream(enriched);
177
+ yield* this.streamWithRetry(
178
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
179
+ options
180
+ );
114
181
  } finally {
115
182
  this.cleanupRun();
116
183
  }
@@ -121,12 +188,37 @@ var BaseAgent = class {
121
188
  const ac = this.createAbortController(options?.signal);
122
189
  this.state = "streaming";
123
190
  try {
124
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
125
- yield* this.heartbeatStream(enriched);
191
+ yield* this.streamWithRetry(
192
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
193
+ options
194
+ );
126
195
  } finally {
127
196
  this.cleanupRun();
128
197
  }
129
198
  }
199
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
200
+ addStreamMiddleware(middleware) {
201
+ this.guardDisposed();
202
+ this._streamMiddleware.push(middleware);
203
+ }
204
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
205
+ async *applyStreamPipeline(source, options, ac) {
206
+ let stream = this.enrichStream(source, options);
207
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
208
+ stream = this.heartbeatStream(stream);
209
+ if (this._streamMiddleware.length > 0) {
210
+ const ctx = {
211
+ model: options.model,
212
+ backend: this.backendName,
213
+ abortController: ac,
214
+ config: Object.freeze({ ...this.config })
215
+ };
216
+ for (const mw of this._streamMiddleware) {
217
+ stream = mw(stream, ctx);
218
+ }
219
+ }
220
+ yield* stream;
221
+ }
130
222
  abort() {
131
223
  if (this.abortController) {
132
224
  this.abortController.abort();
@@ -149,26 +241,109 @@ var BaseAgent = class {
149
241
  this.abort();
150
242
  this.state = "disposed";
151
243
  }
244
+ // ─── Retry Logic ─────────────────────────────────────────────
245
+ /** Check if an error should be retried given the retry configuration. */
246
+ isRetryableError(error, retry) {
247
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
248
+ return false;
249
+ }
250
+ if (AgentSDKError.is(error)) {
251
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
252
+ return retry.retryableErrors.includes(error.code);
253
+ }
254
+ if (error.retryable) return true;
255
+ if (error.code) return isRecoverableErrorCode(error.code);
256
+ }
257
+ return false;
258
+ }
259
+ /** Execute a function with retry logic per RetryConfig. */
260
+ async withRetry(fn, options) {
261
+ const retry = options?.retry;
262
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
263
+ return fn();
264
+ }
265
+ const maxRetries = retry.maxRetries;
266
+ const initialDelay = retry.initialDelayMs ?? 1e3;
267
+ const multiplier = retry.backoffMultiplier ?? 2;
268
+ let lastError;
269
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
270
+ try {
271
+ return await fn();
272
+ } catch (err) {
273
+ lastError = err;
274
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
275
+ throw err;
276
+ }
277
+ const delay = initialDelay * Math.pow(multiplier, attempt);
278
+ await new Promise((resolve) => setTimeout(resolve, delay));
279
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
280
+ throw err;
281
+ }
282
+ }
283
+ }
284
+ throw lastError;
285
+ }
286
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
287
+ async *streamWithRetry(factory, options) {
288
+ const retry = options?.retry;
289
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
290
+ yield* factory();
291
+ return;
292
+ }
293
+ const maxRetries = retry.maxRetries;
294
+ const initialDelay = retry.initialDelayMs ?? 1e3;
295
+ const multiplier = retry.backoffMultiplier ?? 2;
296
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
297
+ try {
298
+ const stream = factory();
299
+ const iterator = stream[Symbol.asyncIterator]();
300
+ const first = await iterator.next();
301
+ if (first.done) return;
302
+ yield first.value;
303
+ while (true) {
304
+ const next = await iterator.next();
305
+ if (next.done) break;
306
+ yield next.value;
307
+ }
308
+ return;
309
+ } catch (err) {
310
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
311
+ throw err;
312
+ }
313
+ const delay = initialDelay * Math.pow(multiplier, attempt);
314
+ await new Promise((resolve) => setTimeout(resolve, delay));
315
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
316
+ throw err;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ // ─── CallOptions Resolution ──────────────────────────────────
322
+ /** Resolve tools to use for this call (per-call override > config default) */
323
+ resolveTools(options) {
324
+ return options?.tools ?? this.config.tools ?? [];
325
+ }
152
326
  // ─── Usage Enrichment ───────────────────────────────────────────
153
327
  /** Enrich result usage with model/backend and fire onUsage callback */
154
- enrichAndNotifyUsage(result) {
328
+ enrichAndNotifyUsage(result, options) {
155
329
  if (result.usage) {
156
330
  result.usage = {
157
331
  ...result.usage,
158
- model: this.config.model,
332
+ model: options.model,
159
333
  backend: this.backendName
160
334
  };
161
335
  this.callOnUsage(result.usage);
162
336
  }
163
337
  }
164
338
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
165
- async *enrichStream(source) {
339
+ async *enrichStream(source, options) {
340
+ const model = options.model;
166
341
  for await (const event of source) {
167
342
  if (event.type === "usage_update") {
168
343
  const usage = {
169
344
  promptTokens: event.promptTokens,
170
345
  completionTokens: event.completionTokens,
171
- model: this.config.model,
346
+ model,
172
347
  backend: this.backendName
173
348
  };
174
349
  this.callOnUsage(usage);
@@ -238,6 +413,35 @@ var BaseAgent = class {
238
413
  heartbeatResolve = null;
239
414
  }
240
415
  }
416
+ // ─── Activity Timeout ────────────────────────────────────────
417
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
418
+ * When timeoutMs is not set, passes through directly. */
419
+ async *activityTimeoutStream(source, timeoutMs, ac) {
420
+ if (!timeoutMs || timeoutMs <= 0) {
421
+ yield* source;
422
+ return;
423
+ }
424
+ const iterator = source[Symbol.asyncIterator]();
425
+ let timerId;
426
+ try {
427
+ while (true) {
428
+ const timeoutPromise = new Promise((_, reject) => {
429
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
430
+ });
431
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
432
+ clearTimeout(timerId);
433
+ if (result.done) break;
434
+ yield result.value;
435
+ }
436
+ } catch (err) {
437
+ if (err instanceof ActivityTimeoutError) {
438
+ ac.abort(err);
439
+ }
440
+ throw err;
441
+ } finally {
442
+ clearTimeout(timerId);
443
+ }
444
+ }
241
445
  // ─── Guards ───────────────────────────────────────────────────
242
446
  guardReentrancy() {
243
447
  if (this.state === "running" || this.state === "streaming") {
@@ -336,6 +540,61 @@ function extractSchemaFromDef(schema) {
336
540
  }
337
541
  }
338
542
 
543
+ // src/backends/shared.ts
544
+ function extractLastUserPrompt(messages) {
545
+ for (let i = messages.length - 1; i >= 0; i--) {
546
+ const msg = messages[i];
547
+ if (msg.role === "user") {
548
+ return getTextContent(msg.content);
549
+ }
550
+ }
551
+ return "";
552
+ }
553
+ function serializeToolCall(tc) {
554
+ const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
555
+ return ` Tool call: ${tc.name}(${args})`;
556
+ }
557
+ function serializeToolResult(tr) {
558
+ const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
559
+ const prefix = tr.isError ? "[ERROR] " : "";
560
+ return ` ${tr.name} \u2192 ${prefix}${result}`;
561
+ }
562
+ function buildContextualPrompt(messages) {
563
+ if (messages.length <= 1) {
564
+ return extractLastUserPrompt(messages);
565
+ }
566
+ const history = messages.slice(0, -1).map((msg) => {
567
+ if (msg.role === "user") {
568
+ return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
569
+ }
570
+ if (msg.role === "tool" && msg.toolResults) {
571
+ const results = msg.toolResults.map(serializeToolResult).join("\n");
572
+ return `Tool results:
573
+ ${results}`;
574
+ }
575
+ if (msg.role === "assistant") {
576
+ const parts = [];
577
+ const thinking = msg.thinking;
578
+ if (thinking) {
579
+ parts.push(`[reasoning: ${thinking}]`);
580
+ }
581
+ const text2 = msg.content ? getTextContent(msg.content) : "";
582
+ if (text2) parts.push(text2);
583
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
584
+ parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
585
+ }
586
+ return `Assistant: ${parts.join("\n")}`;
587
+ }
588
+ const text = msg.content ? getTextContent(msg.content) : "";
589
+ return `${msg.role}: ${text}`;
590
+ }).join("\n");
591
+ const lastPrompt = extractLastUserPrompt(messages);
592
+ return `Conversation history:
593
+ ${history}
594
+
595
+ User: ${lastPrompt}`;
596
+ }
597
+
339
598
  // src/backends/claude.ts
340
599
  var MCP_SERVER_NAME = "agent-sdk-tools";
341
600
  var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
@@ -345,12 +604,12 @@ function mcpToolName(toolName) {
345
604
  function stripMcpPrefix(name) {
346
605
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
347
606
  }
348
- var sdkModule = null;
607
+ var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
608
+ var _sdkMock = null;
349
609
  async function loadSDK() {
350
- if (sdkModule) return sdkModule;
610
+ if (_sdkMock) return _sdkMock;
351
611
  try {
352
- sdkModule = await import('@anthropic-ai/claude-agent-sdk');
353
- return sdkModule;
612
+ return await import('@anthropic-ai/claude-agent-sdk');
354
613
  } catch {
355
614
  throw new SubprocessError(
356
615
  "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
@@ -358,16 +617,35 @@ async function loadSDK() {
358
617
  }
359
618
  }
360
619
  function _injectSDK(mock) {
361
- sdkModule = mock;
620
+ _sdkMock = mock;
362
621
  }
363
622
  function _resetSDK() {
364
- sdkModule = null;
623
+ _sdkMock = null;
365
624
  }
366
625
  var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
367
626
  var ANTHROPIC_API_VERSION = "2023-06-01";
368
627
  var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
369
- function buildMcpServer(sdk, tools, toolResultCapture) {
370
- if (tools.length === 0) return void 0;
628
+ function normalizeAskUserInput(args) {
629
+ if (typeof args.question === "string") {
630
+ return {
631
+ question: args.question,
632
+ choices: Array.isArray(args.choices) ? args.choices : void 0,
633
+ allowFreeform: args.allowFreeform !== false
634
+ };
635
+ }
636
+ const questions = args.questions;
637
+ if (questions && questions.length > 0) {
638
+ const first = questions[0];
639
+ return {
640
+ question: first.question,
641
+ choices: first.options?.map((o) => o.label),
642
+ allowFreeform: true
643
+ };
644
+ }
645
+ return { question: JSON.stringify(args), allowFreeform: true };
646
+ }
647
+ function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
648
+ if (tools.length === 0 && !onAskUser) return void 0;
371
649
  const mcpTools = tools.map((tool) => {
372
650
  const zodSchema = tool.parameters;
373
651
  const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
@@ -391,6 +669,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
391
669
  }
392
670
  );
393
671
  });
672
+ if (onAskUser) {
673
+ const askUserTool = sdk.tool(
674
+ "ask_user",
675
+ "Ask the user a question and wait for their response",
676
+ {
677
+ question: { type: "string", description: "The question to ask the user" },
678
+ choices: {
679
+ type: "array",
680
+ items: { type: "string" },
681
+ description: "Optional list of choices for multiple choice"
682
+ },
683
+ questions: {
684
+ type: "array",
685
+ items: {
686
+ type: "object",
687
+ properties: {
688
+ question: { type: "string" },
689
+ options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
690
+ }
691
+ },
692
+ description: "Alternative nested question format"
693
+ }
694
+ },
695
+ async (args) => {
696
+ const normalized = normalizeAskUserInput(args);
697
+ const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
698
+ return {
699
+ content: [{ type: "text", text: response.answer }]
700
+ };
701
+ }
702
+ );
703
+ mcpTools.push(askUserTool);
704
+ }
394
705
  return sdk.createSdkMcpServer({
395
706
  name: MCP_SERVER_NAME,
396
707
  version: "1.0.0",
@@ -440,6 +751,7 @@ function buildCanUseTool(config) {
440
751
  const unifiedRequest = {
441
752
  toolName,
442
753
  toolArgs: input,
754
+ toolCallId: options.toolUseID,
443
755
  suggestedScope: extractSuggestedScope(options.suggestions),
444
756
  rawSDKRequest: { toolName, input, ...options }
445
757
  };
@@ -516,6 +828,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
516
828
  if (block.type === "tool_use") {
517
829
  const toolCallId = String(block.id ?? "");
518
830
  const toolName = stripMcpPrefix(block.name ?? "unknown");
831
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
519
832
  if (toolCallTracker) {
520
833
  toolCallTracker.trackStart(toolCallId, toolName);
521
834
  }
@@ -539,6 +852,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
539
852
  case "tool_use_summary": {
540
853
  const summary = msg.summary;
541
854
  const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
855
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
542
856
  const precedingIds = msg.preceding_tool_use_ids;
543
857
  let toolCallId = "";
544
858
  if (precedingIds && precedingIds.length > 0) {
@@ -592,10 +906,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
592
906
  }
593
907
  if (msg.is_error) {
594
908
  const r = msg;
909
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
910
+ const code = classifyAgentError(errorMsg);
595
911
  return {
596
912
  type: "error",
597
- error: r.errors?.join("; ") ?? "Unknown error",
598
- recoverable: false
913
+ error: errorMsg,
914
+ recoverable: isRecoverableErrorCode(code),
915
+ code
599
916
  };
600
917
  }
601
918
  return null;
@@ -621,11 +938,6 @@ var ClaudeAgent = class extends BaseAgent {
621
938
  if (options.resumeSessionId) {
622
939
  this._sessionId = options.resumeSessionId;
623
940
  }
624
- if (config.supervisor?.onAskUser) {
625
- console.warn(
626
- "[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
627
- );
628
- }
629
941
  }
630
942
  get sessionId() {
631
943
  return this._sessionId;
@@ -649,12 +961,12 @@ var ClaudeAgent = class extends BaseAgent {
649
961
  const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
650
962
  return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
651
963
  }
652
- buildQueryOptions(signal) {
964
+ buildQueryOptions(signal, options) {
653
965
  const ac = new AbortController();
654
966
  signal.addEventListener("abort", () => ac.abort(), { once: true });
655
967
  const opts = {
656
968
  abortController: ac,
657
- model: this.config.model,
969
+ model: options.model,
658
970
  maxTurns: this.options.maxTurns,
659
971
  cwd: this.options.workingDirectory,
660
972
  pathToClaudeCodeExecutable: this.options.cliPath,
@@ -684,25 +996,85 @@ var ClaudeAgent = class extends BaseAgent {
684
996
  return opts;
685
997
  }
686
998
  async buildMcpConfig(opts, toolResultCapture) {
687
- if (this.tools.length === 0) return opts;
999
+ const onAskUser = this.config.supervisor?.onAskUser;
1000
+ if (this.tools.length === 0 && !onAskUser) return opts;
688
1001
  const sdk = await loadSDK();
689
- const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
1002
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
690
1003
  if (mcpServer) {
691
1004
  opts.mcpServers = {
692
1005
  [MCP_SERVER_NAME]: mcpServer
693
1006
  };
694
1007
  const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1008
+ if (onAskUser) {
1009
+ mcpToolNames.push(mcpToolName("ask_user"));
1010
+ }
695
1011
  opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
696
1012
  }
1013
+ if (onAskUser) {
1014
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1015
+ }
697
1016
  return opts;
698
1017
  }
1018
+ // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
1019
+ /** Setup a retry query: clear session, rebuild with full history */
1020
+ async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
1021
+ this.clearPersistentSession();
1022
+ const retryPrompt = buildContextualPrompt(messages);
1023
+ let retryOpts = this.buildQueryOptions(signal, options);
1024
+ toolResultCapture.clear();
1025
+ retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1026
+ modifyOpts?.(retryOpts);
1027
+ const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1028
+ this.activeQuery = retryQ;
1029
+ return retryQ;
1030
+ }
1031
+ /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
1032
+ collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
1033
+ if (msg.type !== "assistant") return;
1034
+ const betaMessage = msg.message;
1035
+ if (!betaMessage?.content) return;
1036
+ for (const block of betaMessage.content) {
1037
+ if (block.type === "tool_use") {
1038
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1039
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1040
+ toolCalls.push({
1041
+ toolName,
1042
+ args: block.input ?? {},
1043
+ result: toolResultCapture.get(toolName) ?? null,
1044
+ approved: true
1045
+ });
1046
+ }
1047
+ }
1048
+ }
1049
+ /** Back-fill tool results from capture map on summary/result messages */
1050
+ backfillToolResults(msg, toolCalls, toolResultCapture) {
1051
+ if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
1052
+ for (const tc of toolCalls) {
1053
+ if (tc.result === null) {
1054
+ const captured = toolResultCapture.get(tc.toolName);
1055
+ if (captured !== void 0) tc.result = captured;
1056
+ }
1057
+ }
1058
+ }
1059
+ /** Wrap retry inner loop with shared error handling */
1060
+ async withRetryErrorHandling(signal, fn) {
1061
+ try {
1062
+ return await fn();
1063
+ } catch (retryError) {
1064
+ if (this.isPersistent) this.clearPersistentSession();
1065
+ if (signal.aborted) throw new AbortError();
1066
+ throw retryError;
1067
+ } finally {
1068
+ this.activeQuery = null;
1069
+ }
1070
+ }
699
1071
  // ─── executeRun ─────────────────────────────────────────────────
700
- async executeRun(messages, _options, signal) {
1072
+ async executeRun(messages, options, signal) {
701
1073
  this.checkAbort(signal);
702
1074
  const sdk = await loadSDK();
703
1075
  const isResuming = this.isPersistent && this._sessionId !== void 0;
704
1076
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
705
- let opts = this.buildQueryOptions(signal);
1077
+ let opts = this.buildQueryOptions(signal, options);
706
1078
  const toolResultCapture = /* @__PURE__ */ new Map();
707
1079
  opts = await this.buildMcpConfig(opts, toolResultCapture);
708
1080
  const q = sdk.query({ prompt, options: opts });
@@ -712,30 +1084,8 @@ var ClaudeAgent = class extends BaseAgent {
712
1084
  let usage;
713
1085
  try {
714
1086
  for await (const msg of q) {
715
- if (msg.type === "assistant") {
716
- const betaMessage = msg.message;
717
- if (betaMessage?.content) {
718
- for (const block of betaMessage.content) {
719
- if (block.type === "tool_use") {
720
- const toolName = stripMcpPrefix(block.name ?? "unknown");
721
- toolCalls.push({
722
- toolName,
723
- args: block.input ?? {},
724
- result: toolResultCapture.get(toolName) ?? null,
725
- approved: true
726
- });
727
- }
728
- }
729
- }
730
- }
731
- if (msg.type === "tool_use_summary" || msg.type === "result") {
732
- for (const tc of toolCalls) {
733
- if (tc.result === null) {
734
- const captured = toolResultCapture.get(tc.toolName);
735
- if (captured !== void 0) tc.result = captured;
736
- }
737
- }
738
- }
1087
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1088
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
739
1089
  if (msg.type === "result") {
740
1090
  if (msg.subtype === "success") {
741
1091
  const r = msg;
@@ -755,41 +1105,13 @@ var ClaudeAgent = class extends BaseAgent {
755
1105
  } catch (e) {
756
1106
  if (signal.aborted) throw new AbortError();
757
1107
  if (isResuming && this.isPersistent) {
758
- this.clearPersistentSession();
759
- const retryPrompt = buildContextualPrompt(messages);
760
- let retryOpts = this.buildQueryOptions(signal);
761
- toolResultCapture.clear();
762
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
763
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
764
- this.activeQuery = retryQ;
1108
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
765
1109
  toolCalls.length = 0;
766
1110
  output = null;
767
- try {
1111
+ return this.withRetryErrorHandling(signal, async () => {
768
1112
  for await (const msg of retryQ) {
769
- if (msg.type === "assistant") {
770
- const betaMessage = msg.message;
771
- if (betaMessage?.content) {
772
- for (const block of betaMessage.content) {
773
- if (block.type === "tool_use") {
774
- const toolName = stripMcpPrefix(block.name ?? "unknown");
775
- toolCalls.push({
776
- toolName,
777
- args: block.input ?? {},
778
- result: toolResultCapture.get(toolName) ?? null,
779
- approved: true
780
- });
781
- }
782
- }
783
- }
784
- }
785
- if (msg.type === "tool_use_summary" || msg.type === "result") {
786
- for (const tc of toolCalls) {
787
- if (tc.result === null) {
788
- const captured = toolResultCapture.get(tc.toolName);
789
- if (captured !== void 0) tc.result = captured;
790
- }
791
- }
792
- }
1113
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1114
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
793
1115
  if (msg.type === "result") {
794
1116
  if (msg.subtype === "success") {
795
1117
  const r = msg;
@@ -806,23 +1128,17 @@ var ClaudeAgent = class extends BaseAgent {
806
1128
  }
807
1129
  }
808
1130
  }
809
- } catch (retryError) {
810
- if (this.isPersistent) this.clearPersistentSession();
811
- if (signal.aborted) throw new AbortError();
812
- throw retryError;
813
- } finally {
814
- this.activeQuery = null;
815
- }
816
- return {
817
- output,
818
- structuredOutput: void 0,
819
- toolCalls,
820
- messages: [
821
- ...messages,
822
- ...output !== null ? [{ role: "assistant", content: output }] : []
823
- ],
824
- usage
825
- };
1131
+ return {
1132
+ output,
1133
+ structuredOutput: void 0,
1134
+ toolCalls,
1135
+ messages: [
1136
+ ...messages,
1137
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1138
+ ],
1139
+ usage
1140
+ };
1141
+ });
826
1142
  }
827
1143
  if (this.isPersistent) this.clearPersistentSession();
828
1144
  throw e;
@@ -841,12 +1157,12 @@ var ClaudeAgent = class extends BaseAgent {
841
1157
  };
842
1158
  }
843
1159
  // ─── executeRunStructured ───────────────────────────────────────
844
- async executeRunStructured(messages, schema, _options, signal) {
1160
+ async executeRunStructured(messages, schema, options, signal) {
845
1161
  this.checkAbort(signal);
846
1162
  const sdk = await loadSDK();
847
1163
  const isResuming = this.isPersistent && this._sessionId !== void 0;
848
1164
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
849
- let opts = this.buildQueryOptions(signal);
1165
+ let opts = this.buildQueryOptions(signal, options);
850
1166
  const toolResultCapture = /* @__PURE__ */ new Map();
851
1167
  opts = await this.buildMcpConfig(opts, toolResultCapture);
852
1168
  const jsonSchema = zodToJsonSchema(schema.schema);
@@ -862,30 +1178,8 @@ var ClaudeAgent = class extends BaseAgent {
862
1178
  let usage;
863
1179
  try {
864
1180
  for await (const msg of q) {
865
- if (msg.type === "assistant") {
866
- const betaMessage = msg.message;
867
- if (betaMessage?.content) {
868
- for (const block of betaMessage.content) {
869
- if (block.type === "tool_use") {
870
- const toolName = stripMcpPrefix(block.name ?? "unknown");
871
- toolCalls.push({
872
- toolName,
873
- args: block.input ?? {},
874
- result: toolResultCapture.get(toolName) ?? null,
875
- approved: true
876
- });
877
- }
878
- }
879
- }
880
- }
881
- if (msg.type === "tool_use_summary" || msg.type === "result") {
882
- for (const tc of toolCalls) {
883
- if (tc.result === null) {
884
- const captured = toolResultCapture.get(tc.toolName);
885
- if (captured !== void 0) tc.result = captured;
886
- }
887
- }
888
- }
1181
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1182
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
889
1183
  if (msg.type === "result" && msg.subtype === "success") {
890
1184
  const r = msg;
891
1185
  output = r.result;
@@ -920,46 +1214,23 @@ var ClaudeAgent = class extends BaseAgent {
920
1214
  } catch (e) {
921
1215
  if (signal.aborted) throw new AbortError();
922
1216
  if (isResuming && this.isPersistent) {
923
- this.clearPersistentSession();
924
- const retryPrompt = buildContextualPrompt(messages);
925
- let retryOpts = this.buildQueryOptions(signal);
926
- toolResultCapture.clear();
927
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
928
- retryOpts.outputFormat = {
929
- type: "json_schema",
930
- schema: jsonSchema
931
- };
932
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
933
- this.activeQuery = retryQ;
1217
+ const retryQ = await this.prepareRetryQuery(
1218
+ sdk,
1219
+ messages,
1220
+ signal,
1221
+ options,
1222
+ toolResultCapture,
1223
+ (opts2) => {
1224
+ opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
1225
+ }
1226
+ );
934
1227
  toolCalls.length = 0;
935
1228
  output = null;
936
1229
  structuredOutput = void 0;
937
- try {
1230
+ return this.withRetryErrorHandling(signal, async () => {
938
1231
  for await (const msg of retryQ) {
939
- if (msg.type === "assistant") {
940
- const betaMessage = msg.message;
941
- if (betaMessage?.content) {
942
- for (const block of betaMessage.content) {
943
- if (block.type === "tool_use") {
944
- const toolName = stripMcpPrefix(block.name ?? "unknown");
945
- toolCalls.push({
946
- toolName,
947
- args: block.input ?? {},
948
- result: toolResultCapture.get(toolName) ?? null,
949
- approved: true
950
- });
951
- }
952
- }
953
- }
954
- }
955
- if (msg.type === "tool_use_summary" || msg.type === "result") {
956
- for (const tc of toolCalls) {
957
- if (tc.result === null) {
958
- const captured = toolResultCapture.get(tc.toolName);
959
- if (captured !== void 0) tc.result = captured;
960
- }
961
- }
962
- }
1232
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1233
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
963
1234
  if (msg.type === "result" && msg.subtype === "success") {
964
1235
  const r = msg;
965
1236
  output = r.result;
@@ -991,23 +1262,17 @@ var ClaudeAgent = class extends BaseAgent {
991
1262
  );
992
1263
  }
993
1264
  }
994
- } catch (retryError) {
995
- if (this.isPersistent) this.clearPersistentSession();
996
- if (signal.aborted) throw new AbortError();
997
- throw retryError;
998
- } finally {
999
- this.activeQuery = null;
1000
- }
1001
- return {
1002
- output,
1003
- structuredOutput,
1004
- toolCalls,
1005
- messages: [
1006
- ...messages,
1007
- ...output !== null ? [{ role: "assistant", content: output }] : []
1008
- ],
1009
- usage
1010
- };
1265
+ return {
1266
+ output,
1267
+ structuredOutput,
1268
+ toolCalls,
1269
+ messages: [
1270
+ ...messages,
1271
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1272
+ ],
1273
+ usage
1274
+ };
1275
+ });
1011
1276
  }
1012
1277
  if (this.isPersistent) this.clearPersistentSession();
1013
1278
  throw e;
@@ -1026,12 +1291,12 @@ var ClaudeAgent = class extends BaseAgent {
1026
1291
  };
1027
1292
  }
1028
1293
  // ─── executeStream ──────────────────────────────────────────────
1029
- async *executeStream(messages, _options, signal) {
1294
+ async *executeStream(messages, options, signal) {
1030
1295
  this.checkAbort(signal);
1031
1296
  const sdk = await loadSDK();
1032
1297
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1033
1298
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1034
- let opts = this.buildQueryOptions(signal);
1299
+ let opts = this.buildQueryOptions(signal, options);
1035
1300
  const toolResultCapture = /* @__PURE__ */ new Map();
1036
1301
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1037
1302
  const q = sdk.query({ prompt, options: opts });
@@ -1039,6 +1304,7 @@ var ClaudeAgent = class extends BaseAgent {
1039
1304
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
1040
1305
  const toolCallTracker = new ClaudeToolCallTracker();
1041
1306
  const pendingStreamToolCalls = /* @__PURE__ */ new Map();
1307
+ let hasStreamedText = false;
1042
1308
  try {
1043
1309
  for await (const msg of q) {
1044
1310
  if (signal.aborted) throw new AbortError();
@@ -1056,6 +1322,7 @@ var ClaudeAgent = class extends BaseAgent {
1056
1322
  } else if (e.type === "tool_call_end") {
1057
1323
  pendingStreamToolCalls.delete(e.toolCallId);
1058
1324
  }
1325
+ if (e.type === "text_delta") hasStreamedText = true;
1059
1326
  yield e;
1060
1327
  }
1061
1328
  }
@@ -1079,22 +1346,21 @@ var ClaudeAgent = class extends BaseAgent {
1079
1346
  }
1080
1347
  yield this.emitSessionInfo(r.session_id);
1081
1348
  }
1082
- yield { type: "done", finalOutput: r.result };
1349
+ yield {
1350
+ type: "done",
1351
+ finalOutput: hasStreamedText ? null : r.result,
1352
+ ...hasStreamedText ? { streamed: true } : {}
1353
+ };
1083
1354
  }
1084
1355
  }
1085
1356
  } catch (e) {
1086
1357
  if (signal.aborted) throw new AbortError();
1087
1358
  if (isResuming && this.isPersistent) {
1088
- this.clearPersistentSession();
1089
- const retryPrompt = buildContextualPrompt(messages);
1090
- let retryOpts = this.buildQueryOptions(signal);
1091
- toolResultCapture.clear();
1092
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1093
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1094
- this.activeQuery = retryQ;
1359
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1095
1360
  const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
1096
1361
  const retryToolCallTracker = new ClaudeToolCallTracker();
1097
1362
  const retryPendingToolCalls = /* @__PURE__ */ new Map();
1363
+ let retryHasStreamedText = false;
1098
1364
  try {
1099
1365
  for await (const msg of retryQ) {
1100
1366
  if (signal.aborted) throw new AbortError();
@@ -1112,6 +1378,7 @@ var ClaudeAgent = class extends BaseAgent {
1112
1378
  } else if (ev.type === "tool_call_end") {
1113
1379
  retryPendingToolCalls.delete(ev.toolCallId);
1114
1380
  }
1381
+ if (ev.type === "text_delta") retryHasStreamedText = true;
1115
1382
  yield ev;
1116
1383
  }
1117
1384
  }
@@ -1135,7 +1402,11 @@ var ClaudeAgent = class extends BaseAgent {
1135
1402
  }
1136
1403
  yield this.emitSessionInfo(r.session_id);
1137
1404
  }
1138
- yield { type: "done", finalOutput: r.result };
1405
+ yield {
1406
+ type: "done",
1407
+ finalOutput: retryHasStreamedText ? null : r.result,
1408
+ ...retryHasStreamedText ? { streamed: true } : {}
1409
+ };
1139
1410
  }
1140
1411
  }
1141
1412
  } catch (retryError) {
@@ -1158,59 +1429,6 @@ var ClaudeAgent = class extends BaseAgent {
1158
1429
  super.dispose();
1159
1430
  }
1160
1431
  };
1161
- function extractLastUserPrompt(messages) {
1162
- for (let i = messages.length - 1; i >= 0; i--) {
1163
- const msg = messages[i];
1164
- if (msg.role === "user") {
1165
- return getTextContent(msg.content);
1166
- }
1167
- }
1168
- return "";
1169
- }
1170
- function serializeToolCall(tc) {
1171
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
1172
- return ` Tool call: ${tc.name}(${args})`;
1173
- }
1174
- function serializeToolResult(tr) {
1175
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
1176
- const prefix = tr.isError ? "[ERROR] " : "";
1177
- return ` ${tr.name} \u2192 ${prefix}${result}`;
1178
- }
1179
- function buildContextualPrompt(messages) {
1180
- if (messages.length <= 1) {
1181
- return extractLastUserPrompt(messages);
1182
- }
1183
- const history = messages.slice(0, -1).map((msg) => {
1184
- if (msg.role === "user") {
1185
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
1186
- }
1187
- if (msg.role === "tool" && msg.toolResults) {
1188
- const results = msg.toolResults.map(serializeToolResult).join("\n");
1189
- return `Tool results:
1190
- ${results}`;
1191
- }
1192
- if (msg.role === "assistant") {
1193
- const parts = [];
1194
- const thinking = msg.thinking;
1195
- if (thinking) {
1196
- parts.push(`[reasoning: ${thinking}]`);
1197
- }
1198
- const text2 = msg.content ? getTextContent(msg.content) : "";
1199
- if (text2) parts.push(text2);
1200
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1201
- parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
1202
- }
1203
- return `Assistant: ${parts.join("\n")}`;
1204
- }
1205
- const text = msg.content ? getTextContent(msg.content) : "";
1206
- return `${msg.role}: ${text}`;
1207
- }).join("\n");
1208
- const lastPrompt = extractLastUserPrompt(messages);
1209
- return `Conversation history:
1210
- ${history}
1211
-
1212
- User: ${lastPrompt}`;
1213
- }
1214
1432
  var ClaudeAgentService = class {
1215
1433
  name = "claude";
1216
1434
  disposed = false;
@@ -1250,7 +1468,8 @@ var ClaudeAgentService = class {
1250
1468
  this.cachedModels = body.data.map((m) => ({
1251
1469
  id: m.id,
1252
1470
  name: m.display_name,
1253
- provider: "claude"
1471
+ provider: "claude",
1472
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
1254
1473
  }));
1255
1474
  return this.cachedModels;
1256
1475
  }