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