@witqq/agent-sdk 0.7.0 → 0.8.0

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