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