@witqq/agent-sdk 0.6.1 → 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 (145) hide show
  1. package/README.md +539 -6
  2. package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
  3. package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
  4. package/dist/auth/index.cjs +260 -2
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +21 -138
  7. package/dist/auth/index.d.ts +21 -138
  8. package/dist/auth/index.js +260 -3
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/backends/claude.cjs +653 -140
  11. package/dist/backends/claude.cjs.map +1 -1
  12. package/dist/backends/claude.d.cts +4 -1
  13. package/dist/backends/claude.d.ts +4 -1
  14. package/dist/backends/claude.js +653 -140
  15. package/dist/backends/claude.js.map +1 -1
  16. package/dist/backends/copilot.cjs +428 -88
  17. package/dist/backends/copilot.cjs.map +1 -1
  18. package/dist/backends/copilot.d.cts +13 -4
  19. package/dist/backends/copilot.d.ts +13 -4
  20. package/dist/backends/copilot.js +428 -88
  21. package/dist/backends/copilot.js.map +1 -1
  22. package/dist/backends/vercel-ai.cjs +349 -77
  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 +349 -77
  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 +147 -0
  31. package/dist/chat/accumulator.cjs.map +1 -0
  32. package/dist/chat/accumulator.d.cts +64 -0
  33. package/dist/chat/accumulator.d.ts +64 -0
  34. package/dist/chat/accumulator.js +145 -0
  35. package/dist/chat/accumulator.js.map +1 -0
  36. package/dist/chat/backends.cjs +3524 -0
  37. package/dist/chat/backends.cjs.map +1 -0
  38. package/dist/chat/backends.d.cts +66 -0
  39. package/dist/chat/backends.d.ts +66 -0
  40. package/dist/chat/backends.js +3512 -0
  41. package/dist/chat/backends.js.map +1 -0
  42. package/dist/chat/context.cjs +280 -0
  43. package/dist/chat/context.cjs.map +1 -0
  44. package/dist/chat/context.d.cts +191 -0
  45. package/dist/chat/context.d.ts +191 -0
  46. package/dist/chat/context.js +277 -0
  47. package/dist/chat/context.js.map +1 -0
  48. package/dist/chat/core.cjs +305 -0
  49. package/dist/chat/core.cjs.map +1 -0
  50. package/dist/chat/core.d.cts +84 -0
  51. package/dist/chat/core.d.ts +84 -0
  52. package/dist/chat/core.js +282 -0
  53. package/dist/chat/core.js.map +1 -0
  54. package/dist/chat/errors.cjs +273 -0
  55. package/dist/chat/errors.cjs.map +1 -0
  56. package/dist/chat/errors.d.cts +97 -0
  57. package/dist/chat/errors.d.ts +97 -0
  58. package/dist/chat/errors.js +266 -0
  59. package/dist/chat/errors.js.map +1 -0
  60. package/dist/chat/events.cjs +203 -0
  61. package/dist/chat/events.cjs.map +1 -0
  62. package/dist/chat/events.d.cts +245 -0
  63. package/dist/chat/events.d.ts +245 -0
  64. package/dist/chat/events.js +196 -0
  65. package/dist/chat/events.js.map +1 -0
  66. package/dist/chat/index.cjs +5550 -0
  67. package/dist/chat/index.cjs.map +1 -0
  68. package/dist/chat/index.d.cts +77 -0
  69. package/dist/chat/index.d.ts +77 -0
  70. package/dist/chat/index.js +5505 -0
  71. package/dist/chat/index.js.map +1 -0
  72. package/dist/chat/react/theme.css +2517 -0
  73. package/dist/chat/react.cjs +3589 -0
  74. package/dist/chat/react.cjs.map +1 -0
  75. package/dist/chat/react.d.cts +1088 -0
  76. package/dist/chat/react.d.ts +1088 -0
  77. package/dist/chat/react.js +3547 -0
  78. package/dist/chat/react.js.map +1 -0
  79. package/dist/chat/runtime.cjs +1245 -0
  80. package/dist/chat/runtime.cjs.map +1 -0
  81. package/dist/chat/runtime.d.cts +182 -0
  82. package/dist/chat/runtime.d.ts +182 -0
  83. package/dist/chat/runtime.js +1243 -0
  84. package/dist/chat/runtime.js.map +1 -0
  85. package/dist/chat/server.cjs +2668 -0
  86. package/dist/chat/server.cjs.map +1 -0
  87. package/dist/chat/server.d.cts +648 -0
  88. package/dist/chat/server.d.ts +648 -0
  89. package/dist/chat/server.js +2628 -0
  90. package/dist/chat/server.js.map +1 -0
  91. package/dist/chat/sessions.cjs +380 -0
  92. package/dist/chat/sessions.cjs.map +1 -0
  93. package/dist/chat/sessions.d.cts +158 -0
  94. package/dist/chat/sessions.d.ts +158 -0
  95. package/dist/chat/sessions.js +376 -0
  96. package/dist/chat/sessions.js.map +1 -0
  97. package/dist/chat/sqlite.cjs +441 -0
  98. package/dist/chat/sqlite.cjs.map +1 -0
  99. package/dist/chat/sqlite.d.cts +128 -0
  100. package/dist/chat/sqlite.d.ts +128 -0
  101. package/dist/chat/sqlite.js +435 -0
  102. package/dist/chat/sqlite.js.map +1 -0
  103. package/dist/chat/state.cjs +190 -0
  104. package/dist/chat/state.cjs.map +1 -0
  105. package/dist/chat/state.d.cts +95 -0
  106. package/dist/chat/state.d.ts +95 -0
  107. package/dist/chat/state.js +180 -0
  108. package/dist/chat/state.js.map +1 -0
  109. package/dist/chat/storage.cjs +249 -0
  110. package/dist/chat/storage.cjs.map +1 -0
  111. package/dist/chat/storage.d.cts +197 -0
  112. package/dist/chat/storage.d.ts +197 -0
  113. package/dist/chat/storage.js +245 -0
  114. package/dist/chat/storage.js.map +1 -0
  115. package/dist/errors-C-so0M4t.d.cts +33 -0
  116. package/dist/errors-C-so0M4t.d.ts +33 -0
  117. package/dist/errors-CmVvczxZ.d.cts +28 -0
  118. package/dist/errors-CmVvczxZ.d.ts +28 -0
  119. package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
  120. package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
  121. package/dist/index.cjs +365 -59
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.d.cts +322 -125
  124. package/dist/index.d.ts +322 -125
  125. package/dist/index.js +359 -60
  126. package/dist/index.js.map +1 -1
  127. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  128. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  129. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  130. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  131. package/dist/testing.cjs +383 -0
  132. package/dist/testing.cjs.map +1 -0
  133. package/dist/testing.d.cts +132 -0
  134. package/dist/testing.d.ts +132 -0
  135. package/dist/testing.js +377 -0
  136. package/dist/testing.js.map +1 -0
  137. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  138. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  139. package/dist/transport-Cdh3M0tS.d.cts +68 -0
  140. package/dist/transport-Ciap4PWK.d.ts +68 -0
  141. package/dist/types-4vbcmPTp.d.cts +143 -0
  142. package/dist/types-BxggH0Yh.d.ts +143 -0
  143. package/dist/types-DRgd_9R7.d.cts +363 -0
  144. package/dist/types-ajANVzf7.d.ts +363 -0
  145. package/package.json +178 -6
@@ -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");
@@ -6,41 +44,71 @@ function getTextContent(content) {
6
44
 
7
45
  // src/errors.ts
8
46
  var AgentSDKError = class extends Error {
47
+ /** @internal Marker for cross-bundle identity checks */
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;
9
55
  constructor(message, options) {
10
56
  super(message, options);
11
57
  this.name = "AgentSDKError";
58
+ this.code = options?.code;
59
+ this.retryable = options?.retryable ?? false;
60
+ this.httpStatus = options?.httpStatus;
61
+ }
62
+ /** Check if an error is an AgentSDKError (works across bundled copies) */
63
+ static is(error) {
64
+ return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
12
65
  }
13
66
  };
14
67
  var ReentrancyError = class extends AgentSDKError {
15
68
  constructor() {
16
- 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
+ });
17
72
  this.name = "ReentrancyError";
18
73
  }
19
74
  };
20
75
  var DisposedError = class extends AgentSDKError {
21
76
  constructor(entity) {
22
- super(`${entity} has been disposed and cannot be used.`);
77
+ super(`${entity} has been disposed and cannot be used.`, {
78
+ code: "DISPOSED" /* DISPOSED */
79
+ });
23
80
  this.name = "DisposedError";
24
81
  }
25
82
  };
26
83
  var SubprocessError = class extends AgentSDKError {
27
84
  constructor(message, options) {
28
- super(message, options);
85
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
29
86
  this.name = "SubprocessError";
30
87
  }
31
88
  };
32
89
  var AbortError = class extends AgentSDKError {
33
90
  constructor() {
34
- super("Agent run was aborted.");
91
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
35
92
  this.name = "AbortError";
36
93
  }
37
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
+ };
38
104
 
39
105
  // src/base-agent.ts
40
106
  var BaseAgent = class {
41
107
  state = "idle";
42
108
  abortController = null;
43
109
  config;
110
+ _cleanupExternalSignal = null;
111
+ _streamMiddleware = [];
44
112
  /** CLI session ID for persistent mode. Override in backends that support it. */
45
113
  get sessionId() {
46
114
  return void 0;
@@ -56,12 +124,14 @@ var BaseAgent = class {
56
124
  this.state = "running";
57
125
  try {
58
126
  const messages = [{ role: "user", content: prompt }];
59
- const result = await this.executeRun(messages, options, ac.signal);
60
- this.enrichAndNotifyUsage(result);
127
+ const result = await this.withRetry(
128
+ () => this.executeRun(messages, options, ac.signal),
129
+ options
130
+ );
131
+ this.enrichAndNotifyUsage(result, options);
61
132
  return result;
62
133
  } finally {
63
- this.state = "idle";
64
- this.abortController = null;
134
+ this.cleanupRun();
65
135
  }
66
136
  }
67
137
  async runWithContext(messages, options) {
@@ -70,12 +140,14 @@ var BaseAgent = class {
70
140
  const ac = this.createAbortController(options?.signal);
71
141
  this.state = "running";
72
142
  try {
73
- const result = await this.executeRun(messages, options, ac.signal);
74
- this.enrichAndNotifyUsage(result);
143
+ const result = await this.withRetry(
144
+ () => this.executeRun(messages, options, ac.signal),
145
+ options
146
+ );
147
+ this.enrichAndNotifyUsage(result, options);
75
148
  return result;
76
149
  } finally {
77
- this.state = "idle";
78
- this.abortController = null;
150
+ this.cleanupRun();
79
151
  }
80
152
  }
81
153
  async runStructured(prompt, schema, options) {
@@ -85,17 +157,14 @@ var BaseAgent = class {
85
157
  this.state = "running";
86
158
  try {
87
159
  const messages = [{ role: "user", content: prompt }];
88
- const result = await this.executeRunStructured(
89
- messages,
90
- schema,
91
- options,
92
- ac.signal
160
+ const result = await this.withRetry(
161
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
162
+ options
93
163
  );
94
- this.enrichAndNotifyUsage(result);
164
+ this.enrichAndNotifyUsage(result, options);
95
165
  return result;
96
166
  } finally {
97
- this.state = "idle";
98
- this.abortController = null;
167
+ this.cleanupRun();
99
168
  }
100
169
  }
101
170
  async *stream(prompt, options) {
@@ -105,11 +174,12 @@ var BaseAgent = class {
105
174
  this.state = "streaming";
106
175
  try {
107
176
  const messages = [{ role: "user", content: prompt }];
108
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
109
- yield* this.heartbeatStream(enriched);
177
+ yield* this.streamWithRetry(
178
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
179
+ options
180
+ );
110
181
  } finally {
111
- this.state = "idle";
112
- this.abortController = null;
182
+ this.cleanupRun();
113
183
  }
114
184
  }
115
185
  async *streamWithContext(messages, options) {
@@ -118,12 +188,36 @@ var BaseAgent = class {
118
188
  const ac = this.createAbortController(options?.signal);
119
189
  this.state = "streaming";
120
190
  try {
121
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
122
- yield* this.heartbeatStream(enriched);
191
+ yield* this.streamWithRetry(
192
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
193
+ options
194
+ );
123
195
  } finally {
124
- this.state = "idle";
125
- this.abortController = null;
196
+ this.cleanupRun();
197
+ }
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
+ }
126
219
  }
220
+ yield* stream;
127
221
  }
128
222
  abort() {
129
223
  if (this.abortController) {
@@ -142,29 +236,114 @@ var BaseAgent = class {
142
236
  }
143
237
  /** Mark agent as disposed. Override to add cleanup. */
144
238
  dispose() {
239
+ this._cleanupExternalSignal?.();
240
+ this._cleanupExternalSignal = null;
145
241
  this.abort();
146
242
  this.state = "disposed";
147
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
+ }
148
326
  // ─── Usage Enrichment ───────────────────────────────────────────
149
327
  /** Enrich result usage with model/backend and fire onUsage callback */
150
- enrichAndNotifyUsage(result) {
328
+ enrichAndNotifyUsage(result, options) {
151
329
  if (result.usage) {
152
330
  result.usage = {
153
331
  ...result.usage,
154
- model: this.config.model,
332
+ model: options.model,
155
333
  backend: this.backendName
156
334
  };
157
335
  this.callOnUsage(result.usage);
158
336
  }
159
337
  }
160
338
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
161
- async *enrichStream(source) {
339
+ async *enrichStream(source, options) {
340
+ const model = options.model;
162
341
  for await (const event of source) {
163
342
  if (event.type === "usage_update") {
164
343
  const usage = {
165
344
  promptTokens: event.promptTokens,
166
345
  completionTokens: event.completionTokens,
167
- model: this.config.model,
346
+ model,
168
347
  backend: this.backendName
169
348
  };
170
349
  this.callOnUsage(usage);
@@ -234,6 +413,35 @@ var BaseAgent = class {
234
413
  heartbeatResolve = null;
235
414
  }
236
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
+ }
237
445
  // ─── Guards ───────────────────────────────────────────────────
238
446
  guardReentrancy() {
239
447
  if (this.state === "running" || this.state === "streaming") {
@@ -252,16 +460,24 @@ var BaseAgent = class {
252
460
  }
253
461
  }
254
462
  // ─── Internal Helpers ─────────────────────────────────────────
463
+ /** Clean up after a run completes (success, error, or abort). */
464
+ cleanupRun() {
465
+ this._cleanupExternalSignal?.();
466
+ this._cleanupExternalSignal = null;
467
+ this.state = "idle";
468
+ this.abortController = null;
469
+ }
255
470
  createAbortController(externalSignal) {
256
471
  const ac = new AbortController();
257
472
  this.abortController = ac;
473
+ this._cleanupExternalSignal = null;
258
474
  if (externalSignal) {
259
475
  if (externalSignal.aborted) {
260
476
  ac.abort();
261
477
  } else {
262
- externalSignal.addEventListener("abort", () => ac.abort(), {
263
- once: true
264
- });
478
+ const listener = () => ac.abort();
479
+ externalSignal.addEventListener("abort", listener, { once: true });
480
+ this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
265
481
  }
266
482
  }
267
483
  return ac;
@@ -324,6 +540,61 @@ function extractSchemaFromDef(schema) {
324
540
  }
325
541
  }
326
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
+
327
598
  // src/backends/claude.ts
328
599
  var MCP_SERVER_NAME = "agent-sdk-tools";
329
600
  var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
@@ -333,12 +604,12 @@ function mcpToolName(toolName) {
333
604
  function stripMcpPrefix(name) {
334
605
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
335
606
  }
336
- var sdkModule = null;
607
+ var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
608
+ var _sdkMock = null;
337
609
  async function loadSDK() {
338
- if (sdkModule) return sdkModule;
610
+ if (_sdkMock) return _sdkMock;
339
611
  try {
340
- sdkModule = await import('@anthropic-ai/claude-agent-sdk');
341
- return sdkModule;
612
+ return await import('@anthropic-ai/claude-agent-sdk');
342
613
  } catch {
343
614
  throw new SubprocessError(
344
615
  "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
@@ -346,16 +617,35 @@ async function loadSDK() {
346
617
  }
347
618
  }
348
619
  function _injectSDK(mock) {
349
- sdkModule = mock;
620
+ _sdkMock = mock;
350
621
  }
351
622
  function _resetSDK() {
352
- sdkModule = null;
623
+ _sdkMock = null;
353
624
  }
354
625
  var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
355
626
  var ANTHROPIC_API_VERSION = "2023-06-01";
356
627
  var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
357
- function buildMcpServer(sdk, tools, toolResultCapture) {
358
- 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;
359
649
  const mcpTools = tools.map((tool) => {
360
650
  const zodSchema = tool.parameters;
361
651
  const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
@@ -379,6 +669,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
379
669
  }
380
670
  );
381
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
+ }
382
705
  return sdk.createSdkMcpServer({
383
706
  name: MCP_SERVER_NAME,
384
707
  version: "1.0.0",
@@ -428,6 +751,7 @@ function buildCanUseTool(config) {
428
751
  const unifiedRequest = {
429
752
  toolName,
430
753
  toolArgs: input,
754
+ toolCallId: options.toolUseID,
431
755
  suggestedScope: extractSuggestedScope(options.suggestions),
432
756
  rawSDKRequest: { toolName, input, ...options }
433
757
  };
@@ -504,6 +828,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
504
828
  if (block.type === "tool_use") {
505
829
  const toolCallId = String(block.id ?? "");
506
830
  const toolName = stripMcpPrefix(block.name ?? "unknown");
831
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
507
832
  if (toolCallTracker) {
508
833
  toolCallTracker.trackStart(toolCallId, toolName);
509
834
  }
@@ -527,6 +852,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
527
852
  case "tool_use_summary": {
528
853
  const summary = msg.summary;
529
854
  const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
855
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
530
856
  const precedingIds = msg.preceding_tool_use_ids;
531
857
  let toolCallId = "";
532
858
  if (precedingIds && precedingIds.length > 0) {
@@ -580,10 +906,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
580
906
  }
581
907
  if (msg.is_error) {
582
908
  const r = msg;
909
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
910
+ const code = classifyAgentError(errorMsg);
583
911
  return {
584
912
  type: "error",
585
- error: r.errors?.join("; ") ?? "Unknown error",
586
- recoverable: false
913
+ error: errorMsg,
914
+ recoverable: isRecoverableErrorCode(code),
915
+ code
587
916
  };
588
917
  }
589
918
  return null;
@@ -603,13 +932,11 @@ var ClaudeAgent = class extends BaseAgent {
603
932
  constructor(config, options) {
604
933
  super(config);
605
934
  this.options = options;
606
- this.tools = config.tools;
935
+ this.tools = config.tools ?? [];
607
936
  this.canUseTool = buildCanUseTool(config);
608
937
  this.isPersistent = config.sessionMode === "persistent";
609
- if (config.supervisor?.onAskUser) {
610
- console.warn(
611
- "[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
612
- );
938
+ if (options.resumeSessionId) {
939
+ this._sessionId = options.resumeSessionId;
613
940
  }
614
941
  }
615
942
  get sessionId() {
@@ -634,12 +961,12 @@ var ClaudeAgent = class extends BaseAgent {
634
961
  const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
635
962
  return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
636
963
  }
637
- buildQueryOptions(signal) {
964
+ buildQueryOptions(signal, options) {
638
965
  const ac = new AbortController();
639
966
  signal.addEventListener("abort", () => ac.abort(), { once: true });
640
967
  const opts = {
641
968
  abortController: ac,
642
- model: this.config.model,
969
+ model: options.model,
643
970
  maxTurns: this.options.maxTurns,
644
971
  cwd: this.options.workingDirectory,
645
972
  pathToClaudeCodeExecutable: this.options.cliPath,
@@ -664,30 +991,90 @@ var ClaudeAgent = class extends BaseAgent {
664
991
  opts.permissionMode = "default";
665
992
  }
666
993
  if (this.config.availableTools) {
667
- opts.allowedTools = [...this.config.availableTools];
994
+ opts.tools = [...this.config.availableTools];
668
995
  }
669
996
  return opts;
670
997
  }
671
998
  async buildMcpConfig(opts, toolResultCapture) {
672
- if (this.tools.length === 0) return opts;
999
+ const onAskUser = this.config.supervisor?.onAskUser;
1000
+ if (this.tools.length === 0 && !onAskUser) return opts;
673
1001
  const sdk = await loadSDK();
674
- const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
1002
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
675
1003
  if (mcpServer) {
676
1004
  opts.mcpServers = {
677
1005
  [MCP_SERVER_NAME]: mcpServer
678
1006
  };
679
1007
  const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1008
+ if (onAskUser) {
1009
+ mcpToolNames.push(mcpToolName("ask_user"));
1010
+ }
680
1011
  opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
681
1012
  }
1013
+ if (onAskUser) {
1014
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1015
+ }
682
1016
  return opts;
683
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
+ }
684
1071
  // ─── executeRun ─────────────────────────────────────────────────
685
- async executeRun(messages, _options, signal) {
1072
+ async executeRun(messages, options, signal) {
686
1073
  this.checkAbort(signal);
687
1074
  const sdk = await loadSDK();
688
1075
  const isResuming = this.isPersistent && this._sessionId !== void 0;
689
1076
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
690
- let opts = this.buildQueryOptions(signal);
1077
+ let opts = this.buildQueryOptions(signal, options);
691
1078
  const toolResultCapture = /* @__PURE__ */ new Map();
692
1079
  opts = await this.buildMcpConfig(opts, toolResultCapture);
693
1080
  const q = sdk.query({ prompt, options: opts });
@@ -697,30 +1084,8 @@ var ClaudeAgent = class extends BaseAgent {
697
1084
  let usage;
698
1085
  try {
699
1086
  for await (const msg of q) {
700
- if (msg.type === "assistant") {
701
- const betaMessage = msg.message;
702
- if (betaMessage?.content) {
703
- for (const block of betaMessage.content) {
704
- if (block.type === "tool_use") {
705
- const toolName = stripMcpPrefix(block.name ?? "unknown");
706
- toolCalls.push({
707
- toolName,
708
- args: block.input ?? {},
709
- result: toolResultCapture.get(toolName) ?? null,
710
- approved: true
711
- });
712
- }
713
- }
714
- }
715
- }
716
- if (msg.type === "tool_use_summary" || msg.type === "result") {
717
- for (const tc of toolCalls) {
718
- if (tc.result === null) {
719
- const captured = toolResultCapture.get(tc.toolName);
720
- if (captured !== void 0) tc.result = captured;
721
- }
722
- }
723
- }
1087
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1088
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
724
1089
  if (msg.type === "result") {
725
1090
  if (msg.subtype === "success") {
726
1091
  const r = msg;
@@ -738,8 +1103,44 @@ var ClaudeAgent = class extends BaseAgent {
738
1103
  }
739
1104
  }
740
1105
  } catch (e) {
741
- if (this.isPersistent) this.clearPersistentSession();
742
1106
  if (signal.aborted) throw new AbortError();
1107
+ if (isResuming && this.isPersistent) {
1108
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1109
+ toolCalls.length = 0;
1110
+ output = null;
1111
+ return this.withRetryErrorHandling(signal, async () => {
1112
+ for await (const msg of retryQ) {
1113
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1114
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1115
+ if (msg.type === "result") {
1116
+ if (msg.subtype === "success") {
1117
+ const r = msg;
1118
+ output = r.result;
1119
+ usage = aggregateUsage(r.modelUsage);
1120
+ if (this.isPersistent && r.session_id) {
1121
+ this._sessionId = r.session_id;
1122
+ }
1123
+ } else if (msg.is_error) {
1124
+ const r = msg;
1125
+ throw new Error(
1126
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
1127
+ );
1128
+ }
1129
+ }
1130
+ }
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
+ });
1142
+ }
1143
+ if (this.isPersistent) this.clearPersistentSession();
743
1144
  throw e;
744
1145
  } finally {
745
1146
  this.activeQuery = null;
@@ -756,12 +1157,12 @@ var ClaudeAgent = class extends BaseAgent {
756
1157
  };
757
1158
  }
758
1159
  // ─── executeRunStructured ───────────────────────────────────────
759
- async executeRunStructured(messages, schema, _options, signal) {
1160
+ async executeRunStructured(messages, schema, options, signal) {
760
1161
  this.checkAbort(signal);
761
1162
  const sdk = await loadSDK();
762
1163
  const isResuming = this.isPersistent && this._sessionId !== void 0;
763
1164
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
764
- let opts = this.buildQueryOptions(signal);
1165
+ let opts = this.buildQueryOptions(signal, options);
765
1166
  const toolResultCapture = /* @__PURE__ */ new Map();
766
1167
  opts = await this.buildMcpConfig(opts, toolResultCapture);
767
1168
  const jsonSchema = zodToJsonSchema(schema.schema);
@@ -777,30 +1178,8 @@ var ClaudeAgent = class extends BaseAgent {
777
1178
  let usage;
778
1179
  try {
779
1180
  for await (const msg of q) {
780
- if (msg.type === "assistant") {
781
- const betaMessage = msg.message;
782
- if (betaMessage?.content) {
783
- for (const block of betaMessage.content) {
784
- if (block.type === "tool_use") {
785
- const toolName = stripMcpPrefix(block.name ?? "unknown");
786
- toolCalls.push({
787
- toolName,
788
- args: block.input ?? {},
789
- result: toolResultCapture.get(toolName) ?? null,
790
- approved: true
791
- });
792
- }
793
- }
794
- }
795
- }
796
- if (msg.type === "tool_use_summary" || msg.type === "result") {
797
- for (const tc of toolCalls) {
798
- if (tc.result === null) {
799
- const captured = toolResultCapture.get(tc.toolName);
800
- if (captured !== void 0) tc.result = captured;
801
- }
802
- }
803
- }
1181
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1182
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
804
1183
  if (msg.type === "result" && msg.subtype === "success") {
805
1184
  const r = msg;
806
1185
  output = r.result;
@@ -833,8 +1212,69 @@ var ClaudeAgent = class extends BaseAgent {
833
1212
  }
834
1213
  }
835
1214
  } catch (e) {
836
- if (this.isPersistent) this.clearPersistentSession();
837
1215
  if (signal.aborted) throw new AbortError();
1216
+ if (isResuming && this.isPersistent) {
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
+ );
1227
+ toolCalls.length = 0;
1228
+ output = null;
1229
+ structuredOutput = void 0;
1230
+ return this.withRetryErrorHandling(signal, async () => {
1231
+ for await (const msg of retryQ) {
1232
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1233
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1234
+ if (msg.type === "result" && msg.subtype === "success") {
1235
+ const r = msg;
1236
+ output = r.result;
1237
+ if (r.structured_output !== void 0) {
1238
+ try {
1239
+ structuredOutput = schema.schema.parse(r.structured_output);
1240
+ } catch {
1241
+ try {
1242
+ structuredOutput = schema.schema.parse(JSON.parse(r.result));
1243
+ } catch {
1244
+ }
1245
+ }
1246
+ } else if (r.result) {
1247
+ try {
1248
+ const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
1249
+ const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
1250
+ structuredOutput = schema.schema.parse(JSON.parse(raw));
1251
+ } catch {
1252
+ }
1253
+ }
1254
+ usage = aggregateUsage(r.modelUsage);
1255
+ if (this.isPersistent && r.session_id) {
1256
+ this._sessionId = r.session_id;
1257
+ }
1258
+ } else if (msg.type === "result" && msg.is_error) {
1259
+ const r = msg;
1260
+ throw new Error(
1261
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
1262
+ );
1263
+ }
1264
+ }
1265
+ return {
1266
+ output,
1267
+ structuredOutput,
1268
+ toolCalls,
1269
+ messages: [
1270
+ ...messages,
1271
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1272
+ ],
1273
+ usage
1274
+ };
1275
+ });
1276
+ }
1277
+ if (this.isPersistent) this.clearPersistentSession();
838
1278
  throw e;
839
1279
  } finally {
840
1280
  this.activeQuery = null;
@@ -851,39 +1291,134 @@ var ClaudeAgent = class extends BaseAgent {
851
1291
  };
852
1292
  }
853
1293
  // ─── executeStream ──────────────────────────────────────────────
854
- async *executeStream(messages, _options, signal) {
1294
+ async *executeStream(messages, options, signal) {
855
1295
  this.checkAbort(signal);
856
1296
  const sdk = await loadSDK();
857
1297
  const isResuming = this.isPersistent && this._sessionId !== void 0;
858
1298
  const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
859
- let opts = this.buildQueryOptions(signal);
860
- opts = await this.buildMcpConfig(opts);
1299
+ let opts = this.buildQueryOptions(signal, options);
1300
+ const toolResultCapture = /* @__PURE__ */ new Map();
1301
+ opts = await this.buildMcpConfig(opts, toolResultCapture);
861
1302
  const q = sdk.query({ prompt, options: opts });
862
1303
  this.activeQuery = q;
863
1304
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
864
1305
  const toolCallTracker = new ClaudeToolCallTracker();
1306
+ const pendingStreamToolCalls = /* @__PURE__ */ new Map();
1307
+ let hasStreamedText = false;
865
1308
  try {
866
1309
  for await (const msg of q) {
867
1310
  if (signal.aborted) throw new AbortError();
868
1311
  const events = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
869
1312
  if (events) {
870
1313
  const mapped = Array.isArray(events) ? events : [events];
871
- for (const e of mapped) yield e;
1314
+ for (const e of mapped) {
1315
+ if (e.type === "tool_call_start") {
1316
+ pendingStreamToolCalls.set(e.toolCallId, e.toolName);
1317
+ }
1318
+ if (e.type === "tool_call_end" && toolResultCapture.has(e.toolName)) {
1319
+ e.result = toolResultCapture.get(e.toolName);
1320
+ toolResultCapture.delete(e.toolName);
1321
+ pendingStreamToolCalls.delete(e.toolCallId);
1322
+ } else if (e.type === "tool_call_end") {
1323
+ pendingStreamToolCalls.delete(e.toolCallId);
1324
+ }
1325
+ if (e.type === "text_delta") hasStreamedText = true;
1326
+ yield e;
1327
+ }
872
1328
  }
873
1329
  if (msg.type === "result" && msg.subtype === "success") {
874
1330
  const r = msg;
1331
+ for (const [toolCallId, toolName] of pendingStreamToolCalls) {
1332
+ if (toolResultCapture.has(toolName)) {
1333
+ yield {
1334
+ type: "tool_call_end",
1335
+ toolCallId,
1336
+ toolName,
1337
+ result: toolResultCapture.get(toolName)
1338
+ };
1339
+ toolResultCapture.delete(toolName);
1340
+ }
1341
+ }
1342
+ pendingStreamToolCalls.clear();
875
1343
  if (r.session_id) {
876
1344
  if (this.isPersistent) {
877
1345
  this._sessionId = r.session_id;
878
1346
  }
879
1347
  yield this.emitSessionInfo(r.session_id);
880
1348
  }
881
- yield { type: "done", finalOutput: r.result };
1349
+ yield {
1350
+ type: "done",
1351
+ finalOutput: hasStreamedText ? null : r.result,
1352
+ ...hasStreamedText ? { streamed: true } : {}
1353
+ };
882
1354
  }
883
1355
  }
884
1356
  } catch (e) {
885
- if (this.isPersistent) this.clearPersistentSession();
886
1357
  if (signal.aborted) throw new AbortError();
1358
+ if (isResuming && this.isPersistent) {
1359
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1360
+ const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
1361
+ const retryToolCallTracker = new ClaudeToolCallTracker();
1362
+ const retryPendingToolCalls = /* @__PURE__ */ new Map();
1363
+ let retryHasStreamedText = false;
1364
+ try {
1365
+ for await (const msg of retryQ) {
1366
+ if (signal.aborted) throw new AbortError();
1367
+ const retryEvents = mapSDKMessage(msg, retryThinkingBlockIndices, retryToolCallTracker);
1368
+ if (retryEvents) {
1369
+ const mapped = Array.isArray(retryEvents) ? retryEvents : [retryEvents];
1370
+ for (const ev of mapped) {
1371
+ if (ev.type === "tool_call_start") {
1372
+ retryPendingToolCalls.set(ev.toolCallId, ev.toolName);
1373
+ }
1374
+ if (ev.type === "tool_call_end" && toolResultCapture.has(ev.toolName)) {
1375
+ ev.result = toolResultCapture.get(ev.toolName);
1376
+ toolResultCapture.delete(ev.toolName);
1377
+ retryPendingToolCalls.delete(ev.toolCallId);
1378
+ } else if (ev.type === "tool_call_end") {
1379
+ retryPendingToolCalls.delete(ev.toolCallId);
1380
+ }
1381
+ if (ev.type === "text_delta") retryHasStreamedText = true;
1382
+ yield ev;
1383
+ }
1384
+ }
1385
+ if (msg.type === "result" && msg.subtype === "success") {
1386
+ const r = msg;
1387
+ for (const [toolCallId, toolName] of retryPendingToolCalls) {
1388
+ if (toolResultCapture.has(toolName)) {
1389
+ yield {
1390
+ type: "tool_call_end",
1391
+ toolCallId,
1392
+ toolName,
1393
+ result: toolResultCapture.get(toolName)
1394
+ };
1395
+ toolResultCapture.delete(toolName);
1396
+ }
1397
+ }
1398
+ retryPendingToolCalls.clear();
1399
+ if (r.session_id) {
1400
+ if (this.isPersistent) {
1401
+ this._sessionId = r.session_id;
1402
+ }
1403
+ yield this.emitSessionInfo(r.session_id);
1404
+ }
1405
+ yield {
1406
+ type: "done",
1407
+ finalOutput: retryHasStreamedText ? null : r.result,
1408
+ ...retryHasStreamedText ? { streamed: true } : {}
1409
+ };
1410
+ }
1411
+ }
1412
+ } catch (retryError) {
1413
+ if (this.isPersistent) this.clearPersistentSession();
1414
+ if (signal.aborted) throw new AbortError();
1415
+ throw retryError;
1416
+ } finally {
1417
+ this.activeQuery = null;
1418
+ }
1419
+ return;
1420
+ }
1421
+ if (this.isPersistent) this.clearPersistentSession();
887
1422
  throw e;
888
1423
  } finally {
889
1424
  this.activeQuery = null;
@@ -894,29 +1429,6 @@ var ClaudeAgent = class extends BaseAgent {
894
1429
  super.dispose();
895
1430
  }
896
1431
  };
897
- function extractLastUserPrompt(messages) {
898
- for (let i = messages.length - 1; i >= 0; i--) {
899
- const msg = messages[i];
900
- if (msg.role === "user") {
901
- return getTextContent(msg.content);
902
- }
903
- }
904
- return "";
905
- }
906
- function buildContextualPrompt(messages) {
907
- if (messages.length <= 1) {
908
- return extractLastUserPrompt(messages);
909
- }
910
- const history = messages.slice(0, -1).map((msg) => {
911
- const text = msg.content ? getTextContent(msg.content) : "";
912
- return msg.role === "user" ? `User: ${text}` : `Assistant: ${text}`;
913
- }).join("\n");
914
- const lastPrompt = extractLastUserPrompt(messages);
915
- return `Conversation history:
916
- ${history}
917
-
918
- User: ${lastPrompt}`;
919
- }
920
1432
  var ClaudeAgentService = class {
921
1433
  name = "claude";
922
1434
  disposed = false;
@@ -956,7 +1468,8 @@ var ClaudeAgentService = class {
956
1468
  this.cachedModels = body.data.map((m) => ({
957
1469
  id: m.id,
958
1470
  name: m.display_name,
959
- provider: "claude"
1471
+ provider: "claude",
1472
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
960
1473
  }));
961
1474
  return this.cachedModels;
962
1475
  }