@witqq/agent-sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +140 -34
  2. package/dist/{types-CqvUAYxt.d.cts → agent-CW9XbmG_.d.ts} +137 -102
  3. package/dist/{types-CqvUAYxt.d.ts → agent-DxY68NZL.d.cts} +137 -102
  4. package/dist/auth/index.cjs +72 -1
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +21 -154
  7. package/dist/auth/index.d.ts +21 -154
  8. package/dist/auth/index.js +72 -1
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/backends/claude.cjs +480 -261
  11. package/dist/backends/claude.cjs.map +1 -1
  12. package/dist/backends/claude.d.cts +3 -1
  13. package/dist/backends/claude.d.ts +3 -1
  14. package/dist/backends/claude.js +480 -261
  15. package/dist/backends/claude.js.map +1 -1
  16. package/dist/backends/copilot.cjs +329 -97
  17. package/dist/backends/copilot.cjs.map +1 -1
  18. package/dist/backends/copilot.d.cts +12 -4
  19. package/dist/backends/copilot.d.ts +12 -4
  20. package/dist/backends/copilot.js +329 -97
  21. package/dist/backends/copilot.js.map +1 -1
  22. package/dist/backends/vercel-ai.cjs +294 -61
  23. package/dist/backends/vercel-ai.cjs.map +1 -1
  24. package/dist/backends/vercel-ai.d.cts +3 -1
  25. package/dist/backends/vercel-ai.d.ts +3 -1
  26. package/dist/backends/vercel-ai.js +294 -61
  27. package/dist/backends/vercel-ai.js.map +1 -1
  28. package/dist/backends-BSrsBYFn.d.cts +39 -0
  29. package/dist/backends-BSrsBYFn.d.ts +39 -0
  30. package/dist/chat/accumulator.cjs +1 -1
  31. package/dist/chat/accumulator.cjs.map +1 -1
  32. package/dist/chat/accumulator.d.cts +5 -2
  33. package/dist/chat/accumulator.d.ts +5 -2
  34. package/dist/chat/accumulator.js +1 -1
  35. package/dist/chat/accumulator.js.map +1 -1
  36. package/dist/chat/backends.cjs +736 -746
  37. package/dist/chat/backends.cjs.map +1 -1
  38. package/dist/chat/backends.d.cts +10 -6
  39. package/dist/chat/backends.d.ts +10 -6
  40. package/dist/chat/backends.js +736 -725
  41. package/dist/chat/backends.js.map +1 -1
  42. package/dist/chat/context.cjs +50 -0
  43. package/dist/chat/context.cjs.map +1 -1
  44. package/dist/chat/context.d.cts +27 -3
  45. package/dist/chat/context.d.ts +27 -3
  46. package/dist/chat/context.js +50 -0
  47. package/dist/chat/context.js.map +1 -1
  48. package/dist/chat/core.cjs +25 -2
  49. package/dist/chat/core.cjs.map +1 -1
  50. package/dist/chat/core.d.cts +30 -381
  51. package/dist/chat/core.d.ts +30 -381
  52. package/dist/chat/core.js +24 -3
  53. package/dist/chat/core.js.map +1 -1
  54. package/dist/chat/errors.cjs +48 -26
  55. package/dist/chat/errors.cjs.map +1 -1
  56. package/dist/chat/errors.d.cts +6 -31
  57. package/dist/chat/errors.d.ts +6 -31
  58. package/dist/chat/errors.js +48 -25
  59. package/dist/chat/errors.js.map +1 -1
  60. package/dist/chat/events.cjs.map +1 -1
  61. package/dist/chat/events.d.cts +6 -2
  62. package/dist/chat/events.d.ts +6 -2
  63. package/dist/chat/events.js.map +1 -1
  64. package/dist/chat/index.cjs +1199 -1008
  65. package/dist/chat/index.cjs.map +1 -1
  66. package/dist/chat/index.d.cts +35 -10
  67. package/dist/chat/index.d.ts +35 -10
  68. package/dist/chat/index.js +1196 -987
  69. package/dist/chat/index.js.map +1 -1
  70. package/dist/chat/react/theme.css +2517 -0
  71. package/dist/chat/react.cjs +2003 -1153
  72. package/dist/chat/react.cjs.map +1 -1
  73. package/dist/chat/react.d.cts +590 -121
  74. package/dist/chat/react.d.ts +590 -121
  75. package/dist/chat/react.js +1984 -1151
  76. package/dist/chat/react.js.map +1 -1
  77. package/dist/chat/runtime.cjs +401 -186
  78. package/dist/chat/runtime.cjs.map +1 -1
  79. package/dist/chat/runtime.d.cts +92 -28
  80. package/dist/chat/runtime.d.ts +92 -28
  81. package/dist/chat/runtime.js +401 -186
  82. package/dist/chat/runtime.js.map +1 -1
  83. package/dist/chat/server.cjs +2234 -209
  84. package/dist/chat/server.cjs.map +1 -1
  85. package/dist/chat/server.d.cts +451 -90
  86. package/dist/chat/server.d.ts +451 -90
  87. package/dist/chat/server.js +2221 -210
  88. package/dist/chat/server.js.map +1 -1
  89. package/dist/chat/sessions.cjs +25 -43
  90. package/dist/chat/sessions.cjs.map +1 -1
  91. package/dist/chat/sessions.d.cts +37 -118
  92. package/dist/chat/sessions.d.ts +37 -118
  93. package/dist/chat/sessions.js +25 -43
  94. package/dist/chat/sessions.js.map +1 -1
  95. package/dist/chat/sqlite.cjs +441 -0
  96. package/dist/chat/sqlite.cjs.map +1 -0
  97. package/dist/chat/sqlite.d.cts +128 -0
  98. package/dist/chat/sqlite.d.ts +128 -0
  99. package/dist/chat/sqlite.js +435 -0
  100. package/dist/chat/sqlite.js.map +1 -0
  101. package/dist/chat/state.cjs +14 -1
  102. package/dist/chat/state.cjs.map +1 -1
  103. package/dist/chat/state.d.cts +5 -2
  104. package/dist/chat/state.d.ts +5 -2
  105. package/dist/chat/state.js +14 -1
  106. package/dist/chat/state.js.map +1 -1
  107. package/dist/chat/storage.cjs +19 -10
  108. package/dist/chat/storage.cjs.map +1 -1
  109. package/dist/chat/storage.d.cts +11 -5
  110. package/dist/chat/storage.d.ts +11 -5
  111. package/dist/chat/storage.js +19 -10
  112. package/dist/chat/storage.js.map +1 -1
  113. package/dist/errors-C-so0M4t.d.cts +33 -0
  114. package/dist/errors-C-so0M4t.d.ts +33 -0
  115. package/dist/errors-CmVvczxZ.d.cts +28 -0
  116. package/dist/errors-CmVvczxZ.d.ts +28 -0
  117. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-C1JnJGVR.d.ts} +28 -23
  118. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-C7DSqPyX.d.cts} +28 -23
  119. package/dist/index.cjs +340 -46
  120. package/dist/index.cjs.map +1 -1
  121. package/dist/index.d.cts +292 -123
  122. package/dist/index.d.ts +292 -123
  123. package/dist/index.js +334 -47
  124. package/dist/index.js.map +1 -1
  125. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  126. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  127. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  128. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  129. package/dist/testing.cjs +383 -0
  130. package/dist/testing.cjs.map +1 -0
  131. package/dist/testing.d.cts +132 -0
  132. package/dist/testing.d.ts +132 -0
  133. package/dist/testing.js +377 -0
  134. package/dist/testing.js.map +1 -0
  135. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  136. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  137. package/dist/{transport-DX1Nhm4N.d.cts → transport-Cdh3M0tS.d.cts} +5 -4
  138. package/dist/{transport-D1OaUgRk.d.ts → transport-Ciap4PWK.d.ts} +5 -4
  139. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  140. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  141. package/dist/types-DRgd_9R7.d.cts +363 -0
  142. package/dist/types-ajANVzf7.d.ts +363 -0
  143. package/package.json +31 -6
  144. package/dist/errors-BDLbNu9w.d.cts +0 -13
  145. package/dist/errors-BDLbNu9w.d.ts +0 -13
  146. package/dist/types-DLZzlJxt.d.ts +0 -39
  147. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,4 +1,42 @@
1
- // src/types.ts
1
+ // src/types/errors.ts
2
+ var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
3
+ "TIMEOUT" /* TIMEOUT */,
4
+ "RATE_LIMIT" /* RATE_LIMIT */,
5
+ "NETWORK" /* NETWORK */,
6
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
7
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
8
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
9
+ ]);
10
+ function isRecoverableErrorCode(code) {
11
+ return RECOVERABLE_CODES.has(code);
12
+ }
13
+ function classifyAgentError(error) {
14
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
15
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
16
+ return "TIMEOUT" /* TIMEOUT */;
17
+ }
18
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
19
+ return "RATE_LIMIT" /* RATE_LIMIT */;
20
+ }
21
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
22
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
23
+ }
24
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
25
+ return "NETWORK" /* NETWORK */;
26
+ }
27
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
28
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
29
+ }
30
+ if (msg.includes("abort") || msg.includes("cancel")) {
31
+ return "ABORTED" /* ABORTED */;
32
+ }
33
+ if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
34
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
35
+ }
36
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
37
+ }
38
+
39
+ // src/types/guards.ts
2
40
  function getTextContent(content) {
3
41
  if (typeof content === "string") return content;
4
42
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
@@ -8,9 +46,18 @@ function getTextContent(content) {
8
46
  var AgentSDKError = class extends Error {
9
47
  /** @internal Marker for cross-bundle identity checks */
10
48
  _agentSDKError = true;
49
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
50
+ code;
51
+ /** Whether this error is safe to retry */
52
+ retryable;
53
+ /** HTTP status code hint for error classification */
54
+ httpStatus;
11
55
  constructor(message, options) {
12
56
  super(message, options);
13
57
  this.name = "AgentSDKError";
58
+ this.code = options?.code;
59
+ this.retryable = options?.retryable ?? false;
60
+ this.httpStatus = options?.httpStatus;
14
61
  }
15
62
  /** Check if an error is an AgentSDKError (works across bundled copies) */
16
63
  static is(error) {
@@ -19,38 +66,53 @@ var AgentSDKError = class extends Error {
19
66
  };
20
67
  var ReentrancyError = class extends AgentSDKError {
21
68
  constructor() {
22
- super("Agent is already running. Await the current run before starting another.");
69
+ super("Agent is already running. Await the current run before starting another.", {
70
+ code: "REENTRANCY" /* REENTRANCY */
71
+ });
23
72
  this.name = "ReentrancyError";
24
73
  }
25
74
  };
26
75
  var DisposedError = class extends AgentSDKError {
27
76
  constructor(entity) {
28
- super(`${entity} has been disposed and cannot be used.`);
77
+ super(`${entity} has been disposed and cannot be used.`, {
78
+ code: "DISPOSED" /* DISPOSED */
79
+ });
29
80
  this.name = "DisposedError";
30
81
  }
31
82
  };
32
83
  var DependencyError = class extends AgentSDKError {
33
84
  packageName;
34
85
  constructor(packageName) {
35
- super(`${packageName} is not installed. Install it: npm install ${packageName}`);
86
+ super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
87
+ code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
88
+ });
36
89
  this.name = "DependencyError";
37
90
  this.packageName = packageName;
38
91
  }
39
92
  };
40
93
  var AbortError = class extends AgentSDKError {
41
94
  constructor() {
42
- super("Agent run was aborted.");
95
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
43
96
  this.name = "AbortError";
44
97
  }
45
98
  };
46
99
  var ToolExecutionError = class extends AgentSDKError {
47
100
  toolName;
48
101
  constructor(toolName, message, options) {
49
- super(`Tool "${toolName}" failed: ${message}`, options);
102
+ super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
50
103
  this.name = "ToolExecutionError";
51
104
  this.toolName = toolName;
52
105
  }
53
106
  };
107
+ var ActivityTimeoutError = class extends AgentSDKError {
108
+ constructor(timeoutMs) {
109
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
110
+ code: "TIMEOUT" /* TIMEOUT */,
111
+ retryable: true
112
+ });
113
+ this.name = "ActivityTimeoutError";
114
+ }
115
+ };
54
116
 
55
117
  // src/base-agent.ts
56
118
  var BaseAgent = class {
@@ -58,6 +120,7 @@ var BaseAgent = class {
58
120
  abortController = null;
59
121
  config;
60
122
  _cleanupExternalSignal = null;
123
+ _streamMiddleware = [];
61
124
  /** CLI session ID for persistent mode. Override in backends that support it. */
62
125
  get sessionId() {
63
126
  return void 0;
@@ -73,8 +136,11 @@ var BaseAgent = class {
73
136
  this.state = "running";
74
137
  try {
75
138
  const messages = [{ role: "user", content: prompt }];
76
- const result = await this.executeRun(messages, options, ac.signal);
77
- this.enrichAndNotifyUsage(result);
139
+ const result = await this.withRetry(
140
+ () => this.executeRun(messages, options, ac.signal),
141
+ options
142
+ );
143
+ this.enrichAndNotifyUsage(result, options);
78
144
  return result;
79
145
  } finally {
80
146
  this.cleanupRun();
@@ -86,8 +152,11 @@ var BaseAgent = class {
86
152
  const ac = this.createAbortController(options?.signal);
87
153
  this.state = "running";
88
154
  try {
89
- const result = await this.executeRun(messages, options, ac.signal);
90
- this.enrichAndNotifyUsage(result);
155
+ const result = await this.withRetry(
156
+ () => this.executeRun(messages, options, ac.signal),
157
+ options
158
+ );
159
+ this.enrichAndNotifyUsage(result, options);
91
160
  return result;
92
161
  } finally {
93
162
  this.cleanupRun();
@@ -100,13 +169,11 @@ var BaseAgent = class {
100
169
  this.state = "running";
101
170
  try {
102
171
  const messages = [{ role: "user", content: prompt }];
103
- const result = await this.executeRunStructured(
104
- messages,
105
- schema,
106
- options,
107
- ac.signal
172
+ const result = await this.withRetry(
173
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
174
+ options
108
175
  );
109
- this.enrichAndNotifyUsage(result);
176
+ this.enrichAndNotifyUsage(result, options);
110
177
  return result;
111
178
  } finally {
112
179
  this.cleanupRun();
@@ -119,8 +186,10 @@ var BaseAgent = class {
119
186
  this.state = "streaming";
120
187
  try {
121
188
  const messages = [{ role: "user", content: prompt }];
122
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
123
- yield* this.heartbeatStream(enriched);
189
+ yield* this.streamWithRetry(
190
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
191
+ options
192
+ );
124
193
  } finally {
125
194
  this.cleanupRun();
126
195
  }
@@ -131,12 +200,37 @@ var BaseAgent = class {
131
200
  const ac = this.createAbortController(options?.signal);
132
201
  this.state = "streaming";
133
202
  try {
134
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
135
- yield* this.heartbeatStream(enriched);
203
+ yield* this.streamWithRetry(
204
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
205
+ options
206
+ );
136
207
  } finally {
137
208
  this.cleanupRun();
138
209
  }
139
210
  }
211
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
212
+ addStreamMiddleware(middleware) {
213
+ this.guardDisposed();
214
+ this._streamMiddleware.push(middleware);
215
+ }
216
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
217
+ async *applyStreamPipeline(source, options, ac) {
218
+ let stream = this.enrichStream(source, options);
219
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
220
+ stream = this.heartbeatStream(stream);
221
+ if (this._streamMiddleware.length > 0) {
222
+ const ctx = {
223
+ model: options.model,
224
+ backend: this.backendName,
225
+ abortController: ac,
226
+ config: Object.freeze({ ...this.config })
227
+ };
228
+ for (const mw of this._streamMiddleware) {
229
+ stream = mw(stream, ctx);
230
+ }
231
+ }
232
+ yield* stream;
233
+ }
140
234
  abort() {
141
235
  if (this.abortController) {
142
236
  this.abortController.abort();
@@ -159,26 +253,109 @@ var BaseAgent = class {
159
253
  this.abort();
160
254
  this.state = "disposed";
161
255
  }
256
+ // ─── Retry Logic ─────────────────────────────────────────────
257
+ /** Check if an error should be retried given the retry configuration. */
258
+ isRetryableError(error, retry) {
259
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
260
+ return false;
261
+ }
262
+ if (AgentSDKError.is(error)) {
263
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
264
+ return retry.retryableErrors.includes(error.code);
265
+ }
266
+ if (error.retryable) return true;
267
+ if (error.code) return isRecoverableErrorCode(error.code);
268
+ }
269
+ return false;
270
+ }
271
+ /** Execute a function with retry logic per RetryConfig. */
272
+ async withRetry(fn, options) {
273
+ const retry = options?.retry;
274
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
275
+ return fn();
276
+ }
277
+ const maxRetries = retry.maxRetries;
278
+ const initialDelay = retry.initialDelayMs ?? 1e3;
279
+ const multiplier = retry.backoffMultiplier ?? 2;
280
+ let lastError;
281
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
282
+ try {
283
+ return await fn();
284
+ } catch (err) {
285
+ lastError = err;
286
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
287
+ throw err;
288
+ }
289
+ const delay = initialDelay * Math.pow(multiplier, attempt);
290
+ await new Promise((resolve) => setTimeout(resolve, delay));
291
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
292
+ throw err;
293
+ }
294
+ }
295
+ }
296
+ throw lastError;
297
+ }
298
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
299
+ async *streamWithRetry(factory, options) {
300
+ const retry = options?.retry;
301
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
302
+ yield* factory();
303
+ return;
304
+ }
305
+ const maxRetries = retry.maxRetries;
306
+ const initialDelay = retry.initialDelayMs ?? 1e3;
307
+ const multiplier = retry.backoffMultiplier ?? 2;
308
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
309
+ try {
310
+ const stream = factory();
311
+ const iterator = stream[Symbol.asyncIterator]();
312
+ const first = await iterator.next();
313
+ if (first.done) return;
314
+ yield first.value;
315
+ while (true) {
316
+ const next = await iterator.next();
317
+ if (next.done) break;
318
+ yield next.value;
319
+ }
320
+ return;
321
+ } catch (err) {
322
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
323
+ throw err;
324
+ }
325
+ const delay = initialDelay * Math.pow(multiplier, attempt);
326
+ await new Promise((resolve) => setTimeout(resolve, delay));
327
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
328
+ throw err;
329
+ }
330
+ }
331
+ }
332
+ }
333
+ // ─── CallOptions Resolution ──────────────────────────────────
334
+ /** Resolve tools to use for this call (per-call override > config default) */
335
+ resolveTools(options) {
336
+ return options?.tools ?? this.config.tools ?? [];
337
+ }
162
338
  // ─── Usage Enrichment ───────────────────────────────────────────
163
339
  /** Enrich result usage with model/backend and fire onUsage callback */
164
- enrichAndNotifyUsage(result) {
340
+ enrichAndNotifyUsage(result, options) {
165
341
  if (result.usage) {
166
342
  result.usage = {
167
343
  ...result.usage,
168
- model: this.config.model,
344
+ model: options.model,
169
345
  backend: this.backendName
170
346
  };
171
347
  this.callOnUsage(result.usage);
172
348
  }
173
349
  }
174
350
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
175
- async *enrichStream(source) {
351
+ async *enrichStream(source, options) {
352
+ const model = options.model;
176
353
  for await (const event of source) {
177
354
  if (event.type === "usage_update") {
178
355
  const usage = {
179
356
  promptTokens: event.promptTokens,
180
357
  completionTokens: event.completionTokens,
181
- model: this.config.model,
358
+ model,
182
359
  backend: this.backendName
183
360
  };
184
361
  this.callOnUsage(usage);
@@ -248,6 +425,35 @@ var BaseAgent = class {
248
425
  heartbeatResolve = null;
249
426
  }
250
427
  }
428
+ // ─── Activity Timeout ────────────────────────────────────────
429
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
430
+ * When timeoutMs is not set, passes through directly. */
431
+ async *activityTimeoutStream(source, timeoutMs, ac) {
432
+ if (!timeoutMs || timeoutMs <= 0) {
433
+ yield* source;
434
+ return;
435
+ }
436
+ const iterator = source[Symbol.asyncIterator]();
437
+ let timerId;
438
+ try {
439
+ while (true) {
440
+ const timeoutPromise = new Promise((_, reject) => {
441
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
442
+ });
443
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
444
+ clearTimeout(timerId);
445
+ if (result.done) break;
446
+ yield result.value;
447
+ }
448
+ } catch (err) {
449
+ if (err instanceof ActivityTimeoutError) {
450
+ ac.abort(err);
451
+ }
452
+ throw err;
453
+ } finally {
454
+ clearTimeout(timerId);
455
+ }
456
+ }
251
457
  // ─── Guards ───────────────────────────────────────────────────
252
458
  guardReentrancy() {
253
459
  if (this.state === "running" || this.state === "streaming") {
@@ -347,35 +553,33 @@ function extractSchemaFromDef(schema) {
347
553
  }
348
554
 
349
555
  // src/backends/vercel-ai.ts
350
- var sdkModule = null;
351
- var compatModule = null;
556
+ var _sdkMock = null;
557
+ var _compatMock = null;
352
558
  async function loadSDK() {
353
- if (sdkModule) return sdkModule;
559
+ if (_sdkMock) return _sdkMock;
354
560
  try {
355
- sdkModule = await import('ai');
356
- return sdkModule;
561
+ return await import('ai');
357
562
  } catch {
358
563
  throw new DependencyError("ai");
359
564
  }
360
565
  }
361
566
  async function loadCompat() {
362
- if (compatModule) return compatModule;
567
+ if (_compatMock) return _compatMock;
363
568
  try {
364
- compatModule = await import('@ai-sdk/openai-compatible');
365
- return compatModule;
569
+ return await import('@ai-sdk/openai-compatible');
366
570
  } catch {
367
571
  throw new DependencyError("@ai-sdk/openai-compatible");
368
572
  }
369
573
  }
370
574
  function _injectSDK(mock) {
371
- sdkModule = mock;
575
+ _sdkMock = mock;
372
576
  }
373
577
  function _injectCompat(mock) {
374
- compatModule = mock;
578
+ _compatMock = mock;
375
579
  }
376
580
  function _resetSDK() {
377
- sdkModule = null;
378
- compatModule = null;
581
+ _sdkMock = null;
582
+ _compatMock = null;
379
583
  }
380
584
  var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
381
585
  var DEFAULT_PROVIDER = "openrouter";
@@ -421,13 +625,14 @@ function mapToolsToSDK(sdk, tools, config, sessionApprovals, permissionStore, si
421
625
  return toolMap;
422
626
  }
423
627
  function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
424
- return async (args) => {
628
+ return async (args, options) => {
425
629
  if (ourTool.needsApproval && supervisor?.onPermission) {
426
630
  const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
427
631
  if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
428
632
  const request = {
429
633
  toolName: ourTool.name,
430
- toolArgs: args ?? {}
634
+ toolArgs: args ?? {},
635
+ toolCallId: options?.toolCallId
431
636
  };
432
637
  const decision = await supervisor.onPermission(
433
638
  request,
@@ -534,7 +739,8 @@ function mapStreamPart(part) {
534
739
  return {
535
740
  type: "error",
536
741
  error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
537
- recoverable: true
742
+ recoverable: true,
743
+ code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
538
744
  };
539
745
  }
540
746
  case "reasoning-start":
@@ -555,10 +761,13 @@ function mapStreamPart(part) {
555
761
  }
556
762
  case "error": {
557
763
  const p = part;
764
+ const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
765
+ const code = classifyAgentError(errorMsg);
558
766
  return {
559
767
  type: "error",
560
- error: p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error"),
561
- recoverable: false
768
+ error: errorMsg,
769
+ recoverable: isRecoverableErrorCode(code),
770
+ code
562
771
  };
563
772
  }
564
773
  default:
@@ -574,28 +783,33 @@ var VercelAIAgent = class extends BaseAgent {
574
783
  super(config);
575
784
  this.backendOptions = backendOptions;
576
785
  }
577
- async getModel() {
578
- if (this.model) return this.model;
786
+ async getModel(options) {
787
+ const requestedModel = options.model;
788
+ const defaultModel = this.config.model;
789
+ if (requestedModel === defaultModel && this.model) return this.model;
579
790
  const compat = await loadCompat();
580
791
  const provider = compat.createOpenAICompatible({
581
792
  name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
582
793
  baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
583
794
  apiKey: this.backendOptions.apiKey
584
795
  });
585
- const modelId = this.config.model ?? "anthropic/claude-sonnet-4-5";
586
- this.model = provider.chatModel(modelId);
587
- return this.model;
796
+ const model = provider.chatModel(requestedModel);
797
+ if (requestedModel === defaultModel) {
798
+ this.model = model;
799
+ }
800
+ return model;
588
801
  }
589
- async getSDKTools(signal) {
802
+ async getSDKTools(signal, options) {
590
803
  const sdk = await loadSDK();
591
- return mapToolsToSDK(sdk, this.config.tools ?? [], this.config, this.sessionApprovals, this.config.permissionStore, signal);
804
+ const tools = this.resolveTools(options);
805
+ return mapToolsToSDK(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
592
806
  }
593
807
  // ─── executeRun ─────────────────────────────────────────────────
594
- async executeRun(messages, _options, signal) {
808
+ async executeRun(messages, options, signal) {
595
809
  this.checkAbort(signal);
596
810
  const sdk = await loadSDK();
597
- const model = await this.getModel();
598
- const tools = await this.getSDKTools(signal);
811
+ const model = await this.getModel(options);
812
+ const tools = await this.getSDKTools(signal, options);
599
813
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
600
814
  const sdkMessages = messagesToSDK(messages);
601
815
  const hasTools = Object.keys(tools).length > 0;
@@ -651,10 +865,10 @@ var VercelAIAgent = class extends BaseAgent {
651
865
  };
652
866
  }
653
867
  // ─── executeRunStructured ───────────────────────────────────────
654
- async executeRunStructured(messages, schema, _options, signal) {
868
+ async executeRunStructured(messages, schema, options, signal) {
655
869
  this.checkAbort(signal);
656
870
  const sdk = await loadSDK();
657
- const model = await this.getModel();
871
+ const model = await this.getModel(options);
658
872
  const sdkMessages = messagesToSDK(messages);
659
873
  const jsonSchema = zodToJsonSchema(schema.schema);
660
874
  const result = await sdk.generateObject({
@@ -696,11 +910,11 @@ var VercelAIAgent = class extends BaseAgent {
696
910
  };
697
911
  }
698
912
  // ─── executeStream ──────────────────────────────────────────────
699
- async *executeStream(messages, _options, signal) {
913
+ async *executeStream(messages, options, signal) {
700
914
  this.checkAbort(signal);
701
915
  const sdk = await loadSDK();
702
- const model = await this.getModel();
703
- const tools = await this.getSDKTools(signal);
916
+ const model = await this.getModel(options);
917
+ const tools = await this.getSDKTools(signal, options);
704
918
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
705
919
  const sdkMessages = messagesToSDK(messages);
706
920
  const hasTools = Object.keys(tools).length > 0;
@@ -746,9 +960,11 @@ var VercelAIAgent = class extends BaseAgent {
746
960
  promptTokens: Number(totalUsage?.inputTokens ?? 0),
747
961
  completionTokens: Number(totalUsage?.outputTokens ?? 0)
748
962
  };
963
+ const hasStreamed = finalText.length > 0;
749
964
  yield {
750
965
  type: "done",
751
- finalOutput: finalText || null
966
+ finalOutput: hasStreamed ? null : finalText || null,
967
+ ...hasStreamed ? { streamed: true } : {}
752
968
  };
753
969
  } catch (e) {
754
970
  if (signal.aborted) throw new AbortError();
@@ -777,16 +993,33 @@ var VercelAIAgentService = class {
777
993
  const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
778
994
  try {
779
995
  const res = await globalThis.fetch(`${baseUrl}/models`, {
780
- headers: { Authorization: `Bearer ${this.options.apiKey}` }
996
+ headers: {
997
+ Authorization: `Bearer ${this.options.apiKey}`,
998
+ // OpenRouter requires HTTP-Referer for API access
999
+ "HTTP-Referer": "https://github.com/nicepkg/agent-sdk"
1000
+ }
781
1001
  });
782
1002
  if (!res.ok) {
783
1003
  return [];
784
1004
  }
785
1005
  const body = await res.json();
786
- if (!body.data || body.data.length === 0) {
787
- return [];
1006
+ if (body.data && Array.isArray(body.data)) {
1007
+ return body.data.filter((m) => typeof m.id === "string").map((m) => ({
1008
+ id: m.id,
1009
+ ...typeof m.name === "string" && { name: m.name },
1010
+ ...typeof m.description === "string" && { description: m.description },
1011
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
1012
+ }));
1013
+ }
1014
+ if (Array.isArray(body)) {
1015
+ return body.filter((m) => typeof m.id === "string").map((m) => ({
1016
+ id: m.id,
1017
+ ...typeof m.name === "string" && { name: m.name },
1018
+ ...typeof m.description === "string" && { description: m.description },
1019
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
1020
+ }));
788
1021
  }
789
- return body.data.map((m) => ({ id: m.id }));
1022
+ return [];
790
1023
  } catch {
791
1024
  return [];
792
1025
  }