@witqq/agent-sdk 0.7.0 → 0.9.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 (154) hide show
  1. package/dist/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
  2. package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -102
  3. package/dist/auth/index.cjs +72 -1
  4. package/dist/auth/index.cjs.map +1 -1
  5. package/dist/auth/index.d.cts +21 -154
  6. package/dist/auth/index.d.ts +21 -154
  7. package/dist/auth/index.js +72 -1
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/backends/claude.cjs +480 -261
  10. package/dist/backends/claude.cjs.map +1 -1
  11. package/dist/backends/claude.d.cts +3 -1
  12. package/dist/backends/claude.d.ts +3 -1
  13. package/dist/backends/claude.js +480 -261
  14. package/dist/backends/claude.js.map +1 -1
  15. package/dist/backends/copilot.cjs +337 -112
  16. package/dist/backends/copilot.cjs.map +1 -1
  17. package/dist/backends/copilot.d.cts +12 -4
  18. package/dist/backends/copilot.d.ts +12 -4
  19. package/dist/backends/copilot.js +337 -112
  20. package/dist/backends/copilot.js.map +1 -1
  21. package/dist/backends/mock-llm.cjs +719 -0
  22. package/dist/backends/mock-llm.cjs.map +1 -0
  23. package/dist/backends/mock-llm.d.cts +37 -0
  24. package/dist/backends/mock-llm.d.ts +37 -0
  25. package/dist/backends/mock-llm.js +717 -0
  26. package/dist/backends/mock-llm.js.map +1 -0
  27. package/dist/backends/vercel-ai.cjs +301 -61
  28. package/dist/backends/vercel-ai.cjs.map +1 -1
  29. package/dist/backends/vercel-ai.d.cts +3 -1
  30. package/dist/backends/vercel-ai.d.ts +3 -1
  31. package/dist/backends/vercel-ai.js +301 -61
  32. package/dist/backends/vercel-ai.js.map +1 -1
  33. package/dist/backends-Cno0gZjy.d.cts +114 -0
  34. package/dist/backends-Cno0gZjy.d.ts +114 -0
  35. package/dist/chat/accumulator.cjs +1 -1
  36. package/dist/chat/accumulator.cjs.map +1 -1
  37. package/dist/chat/accumulator.d.cts +5 -2
  38. package/dist/chat/accumulator.d.ts +5 -2
  39. package/dist/chat/accumulator.js +1 -1
  40. package/dist/chat/accumulator.js.map +1 -1
  41. package/dist/chat/backends.cjs +1084 -821
  42. package/dist/chat/backends.cjs.map +1 -1
  43. package/dist/chat/backends.d.cts +10 -6
  44. package/dist/chat/backends.d.ts +10 -6
  45. package/dist/chat/backends.js +1082 -800
  46. package/dist/chat/backends.js.map +1 -1
  47. package/dist/chat/context.cjs +50 -0
  48. package/dist/chat/context.cjs.map +1 -1
  49. package/dist/chat/context.d.cts +27 -3
  50. package/dist/chat/context.d.ts +27 -3
  51. package/dist/chat/context.js +50 -0
  52. package/dist/chat/context.js.map +1 -1
  53. package/dist/chat/core.cjs +60 -27
  54. package/dist/chat/core.cjs.map +1 -1
  55. package/dist/chat/core.d.cts +41 -382
  56. package/dist/chat/core.d.ts +41 -382
  57. package/dist/chat/core.js +58 -28
  58. package/dist/chat/core.js.map +1 -1
  59. package/dist/chat/errors.cjs +48 -26
  60. package/dist/chat/errors.cjs.map +1 -1
  61. package/dist/chat/errors.d.cts +6 -31
  62. package/dist/chat/errors.d.ts +6 -31
  63. package/dist/chat/errors.js +48 -25
  64. package/dist/chat/errors.js.map +1 -1
  65. package/dist/chat/events.cjs.map +1 -1
  66. package/dist/chat/events.d.cts +6 -2
  67. package/dist/chat/events.d.ts +6 -2
  68. package/dist/chat/events.js.map +1 -1
  69. package/dist/chat/index.cjs +1612 -1125
  70. package/dist/chat/index.cjs.map +1 -1
  71. package/dist/chat/index.d.cts +35 -10
  72. package/dist/chat/index.d.ts +35 -10
  73. package/dist/chat/index.js +1600 -1097
  74. package/dist/chat/index.js.map +1 -1
  75. package/dist/chat/react/theme.css +2517 -0
  76. package/dist/chat/react.cjs +2212 -1158
  77. package/dist/chat/react.cjs.map +1 -1
  78. package/dist/chat/react.d.cts +665 -122
  79. package/dist/chat/react.d.ts +665 -122
  80. package/dist/chat/react.js +2191 -1156
  81. package/dist/chat/react.js.map +1 -1
  82. package/dist/chat/runtime.cjs +405 -186
  83. package/dist/chat/runtime.cjs.map +1 -1
  84. package/dist/chat/runtime.d.cts +92 -28
  85. package/dist/chat/runtime.d.ts +92 -28
  86. package/dist/chat/runtime.js +405 -186
  87. package/dist/chat/runtime.js.map +1 -1
  88. package/dist/chat/server.cjs +2247 -212
  89. package/dist/chat/server.cjs.map +1 -1
  90. package/dist/chat/server.d.cts +451 -90
  91. package/dist/chat/server.d.ts +451 -90
  92. package/dist/chat/server.js +2234 -213
  93. package/dist/chat/server.js.map +1 -1
  94. package/dist/chat/sessions.cjs +64 -66
  95. package/dist/chat/sessions.cjs.map +1 -1
  96. package/dist/chat/sessions.d.cts +37 -118
  97. package/dist/chat/sessions.d.ts +37 -118
  98. package/dist/chat/sessions.js +65 -67
  99. package/dist/chat/sessions.js.map +1 -1
  100. package/dist/chat/sqlite.cjs +536 -0
  101. package/dist/chat/sqlite.cjs.map +1 -0
  102. package/dist/chat/sqlite.d.cts +164 -0
  103. package/dist/chat/sqlite.d.ts +164 -0
  104. package/dist/chat/sqlite.js +527 -0
  105. package/dist/chat/sqlite.js.map +1 -0
  106. package/dist/chat/state.cjs +14 -1
  107. package/dist/chat/state.cjs.map +1 -1
  108. package/dist/chat/state.d.cts +5 -2
  109. package/dist/chat/state.d.ts +5 -2
  110. package/dist/chat/state.js +14 -1
  111. package/dist/chat/state.js.map +1 -1
  112. package/dist/chat/storage.cjs +58 -33
  113. package/dist/chat/storage.cjs.map +1 -1
  114. package/dist/chat/storage.d.cts +18 -8
  115. package/dist/chat/storage.d.ts +18 -8
  116. package/dist/chat/storage.js +59 -34
  117. package/dist/chat/storage.js.map +1 -1
  118. package/dist/errors-C-so0M4t.d.cts +33 -0
  119. package/dist/errors-C-so0M4t.d.ts +33 -0
  120. package/dist/errors-CmVvczxZ.d.cts +28 -0
  121. package/dist/errors-CmVvczxZ.d.ts +28 -0
  122. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-7EIit9Xk.d.ts} +72 -33
  123. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
  124. package/dist/index.cjs +354 -60
  125. package/dist/index.cjs.map +1 -1
  126. package/dist/index.d.cts +294 -123
  127. package/dist/index.d.ts +294 -123
  128. package/dist/index.js +347 -60
  129. package/dist/index.js.map +1 -1
  130. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  131. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  132. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  133. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  134. package/dist/testing.cjs +1107 -0
  135. package/dist/testing.cjs.map +1 -0
  136. package/dist/testing.d.cts +144 -0
  137. package/dist/testing.d.ts +144 -0
  138. package/dist/testing.js +1101 -0
  139. package/dist/testing.js.map +1 -0
  140. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  141. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  142. package/dist/{transport-DX1Nhm4N.d.cts → transport-DLWCN18G.d.cts} +5 -4
  143. package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.d.ts} +5 -4
  144. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  145. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  146. package/dist/types-DgtI1hzh.d.ts +364 -0
  147. package/dist/types-DkSXALKg.d.cts +364 -0
  148. package/package.json +41 -5
  149. package/LICENSE +0 -21
  150. package/README.md +0 -948
  151. package/dist/errors-BDLbNu9w.d.cts +0 -13
  152. package/dist/errors-BDLbNu9w.d.ts +0 -13
  153. package/dist/types-DLZzlJxt.d.ts +0 -39
  154. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,6 +1,44 @@
1
1
  'use strict';
2
2
 
3
- // src/types.ts
3
+ // src/types/errors.ts
4
+ var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
5
+ "TIMEOUT" /* TIMEOUT */,
6
+ "RATE_LIMIT" /* RATE_LIMIT */,
7
+ "NETWORK" /* NETWORK */,
8
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
9
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
10
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
11
+ ]);
12
+ function isRecoverableErrorCode(code) {
13
+ return RECOVERABLE_CODES.has(code);
14
+ }
15
+ function classifyAgentError(error) {
16
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
17
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
18
+ return "TIMEOUT" /* TIMEOUT */;
19
+ }
20
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
21
+ return "RATE_LIMIT" /* RATE_LIMIT */;
22
+ }
23
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
24
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
25
+ }
26
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
27
+ return "NETWORK" /* NETWORK */;
28
+ }
29
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
30
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
31
+ }
32
+ if (msg.includes("abort") || msg.includes("cancel")) {
33
+ return "ABORTED" /* ABORTED */;
34
+ }
35
+ if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
36
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
37
+ }
38
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
39
+ }
40
+
41
+ // src/types/guards.ts
4
42
  function getTextContent(content) {
5
43
  if (typeof content === "string") return content;
6
44
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
@@ -10,9 +48,18 @@ function getTextContent(content) {
10
48
  var AgentSDKError = class extends Error {
11
49
  /** @internal Marker for cross-bundle identity checks */
12
50
  _agentSDKError = true;
51
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
52
+ code;
53
+ /** Whether this error is safe to retry */
54
+ retryable;
55
+ /** HTTP status code hint for error classification */
56
+ httpStatus;
13
57
  constructor(message, options) {
14
58
  super(message, options);
15
59
  this.name = "AgentSDKError";
60
+ this.code = options?.code;
61
+ this.retryable = options?.retryable ?? false;
62
+ this.httpStatus = options?.httpStatus;
16
63
  }
17
64
  /** Check if an error is an AgentSDKError (works across bundled copies) */
18
65
  static is(error) {
@@ -21,28 +68,41 @@ var AgentSDKError = class extends Error {
21
68
  };
22
69
  var ReentrancyError = class extends AgentSDKError {
23
70
  constructor() {
24
- super("Agent is already running. Await the current run before starting another.");
71
+ super("Agent is already running. Await the current run before starting another.", {
72
+ code: "REENTRANCY" /* REENTRANCY */
73
+ });
25
74
  this.name = "ReentrancyError";
26
75
  }
27
76
  };
28
77
  var DisposedError = class extends AgentSDKError {
29
78
  constructor(entity) {
30
- super(`${entity} has been disposed and cannot be used.`);
79
+ super(`${entity} has been disposed and cannot be used.`, {
80
+ code: "DISPOSED" /* DISPOSED */
81
+ });
31
82
  this.name = "DisposedError";
32
83
  }
33
84
  };
34
85
  var SubprocessError = class extends AgentSDKError {
35
86
  constructor(message, options) {
36
- super(message, options);
87
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
37
88
  this.name = "SubprocessError";
38
89
  }
39
90
  };
40
91
  var AbortError = class extends AgentSDKError {
41
92
  constructor() {
42
- super("Agent run was aborted.");
93
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
43
94
  this.name = "AbortError";
44
95
  }
45
96
  };
97
+ var ActivityTimeoutError = class extends AgentSDKError {
98
+ constructor(timeoutMs) {
99
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
100
+ code: "TIMEOUT" /* TIMEOUT */,
101
+ retryable: true
102
+ });
103
+ this.name = "ActivityTimeoutError";
104
+ }
105
+ };
46
106
 
47
107
  // src/base-agent.ts
48
108
  var BaseAgent = class {
@@ -50,6 +110,7 @@ var BaseAgent = class {
50
110
  abortController = null;
51
111
  config;
52
112
  _cleanupExternalSignal = null;
113
+ _streamMiddleware = [];
53
114
  /** CLI session ID for persistent mode. Override in backends that support it. */
54
115
  get sessionId() {
55
116
  return void 0;
@@ -65,8 +126,11 @@ var BaseAgent = class {
65
126
  this.state = "running";
66
127
  try {
67
128
  const messages = [{ role: "user", content: prompt }];
68
- const result = await this.executeRun(messages, options, ac.signal);
69
- this.enrichAndNotifyUsage(result);
129
+ const result = await this.withRetry(
130
+ () => this.executeRun(messages, options, ac.signal),
131
+ options
132
+ );
133
+ this.enrichAndNotifyUsage(result, options);
70
134
  return result;
71
135
  } finally {
72
136
  this.cleanupRun();
@@ -78,8 +142,11 @@ var BaseAgent = class {
78
142
  const ac = this.createAbortController(options?.signal);
79
143
  this.state = "running";
80
144
  try {
81
- const result = await this.executeRun(messages, options, ac.signal);
82
- this.enrichAndNotifyUsage(result);
145
+ const result = await this.withRetry(
146
+ () => this.executeRun(messages, options, ac.signal),
147
+ options
148
+ );
149
+ this.enrichAndNotifyUsage(result, options);
83
150
  return result;
84
151
  } finally {
85
152
  this.cleanupRun();
@@ -92,13 +159,11 @@ var BaseAgent = class {
92
159
  this.state = "running";
93
160
  try {
94
161
  const messages = [{ role: "user", content: prompt }];
95
- const result = await this.executeRunStructured(
96
- messages,
97
- schema,
98
- options,
99
- ac.signal
162
+ const result = await this.withRetry(
163
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
164
+ options
100
165
  );
101
- this.enrichAndNotifyUsage(result);
166
+ this.enrichAndNotifyUsage(result, options);
102
167
  return result;
103
168
  } finally {
104
169
  this.cleanupRun();
@@ -111,8 +176,10 @@ var BaseAgent = class {
111
176
  this.state = "streaming";
112
177
  try {
113
178
  const messages = [{ role: "user", content: prompt }];
114
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
115
- yield* this.heartbeatStream(enriched);
179
+ yield* this.streamWithRetry(
180
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
181
+ options
182
+ );
116
183
  } finally {
117
184
  this.cleanupRun();
118
185
  }
@@ -123,12 +190,37 @@ var BaseAgent = class {
123
190
  const ac = this.createAbortController(options?.signal);
124
191
  this.state = "streaming";
125
192
  try {
126
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
127
- yield* this.heartbeatStream(enriched);
193
+ yield* this.streamWithRetry(
194
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
195
+ options
196
+ );
128
197
  } finally {
129
198
  this.cleanupRun();
130
199
  }
131
200
  }
201
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
202
+ addStreamMiddleware(middleware) {
203
+ this.guardDisposed();
204
+ this._streamMiddleware.push(middleware);
205
+ }
206
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
207
+ async *applyStreamPipeline(source, options, ac) {
208
+ let stream = this.enrichStream(source, options);
209
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
210
+ stream = this.heartbeatStream(stream);
211
+ if (this._streamMiddleware.length > 0) {
212
+ const ctx = {
213
+ model: options.model,
214
+ backend: this.backendName,
215
+ abortController: ac,
216
+ config: Object.freeze({ ...this.config })
217
+ };
218
+ for (const mw of this._streamMiddleware) {
219
+ stream = mw(stream, ctx);
220
+ }
221
+ }
222
+ yield* stream;
223
+ }
132
224
  abort() {
133
225
  if (this.abortController) {
134
226
  this.abortController.abort();
@@ -151,26 +243,109 @@ var BaseAgent = class {
151
243
  this.abort();
152
244
  this.state = "disposed";
153
245
  }
246
+ // ─── Retry Logic ─────────────────────────────────────────────
247
+ /** Check if an error should be retried given the retry configuration. */
248
+ isRetryableError(error, retry) {
249
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
250
+ return false;
251
+ }
252
+ if (AgentSDKError.is(error)) {
253
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
254
+ return retry.retryableErrors.includes(error.code);
255
+ }
256
+ if (error.retryable) return true;
257
+ if (error.code) return isRecoverableErrorCode(error.code);
258
+ }
259
+ return false;
260
+ }
261
+ /** Execute a function with retry logic per RetryConfig. */
262
+ async withRetry(fn, options) {
263
+ const retry = options?.retry;
264
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
265
+ return fn();
266
+ }
267
+ const maxRetries = retry.maxRetries;
268
+ const initialDelay = retry.initialDelayMs ?? 1e3;
269
+ const multiplier = retry.backoffMultiplier ?? 2;
270
+ let lastError;
271
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
272
+ try {
273
+ return await fn();
274
+ } catch (err) {
275
+ lastError = err;
276
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
277
+ throw err;
278
+ }
279
+ const delay = initialDelay * Math.pow(multiplier, attempt);
280
+ await new Promise((resolve) => setTimeout(resolve, delay));
281
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
282
+ throw err;
283
+ }
284
+ }
285
+ }
286
+ throw lastError;
287
+ }
288
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
289
+ async *streamWithRetry(factory, options) {
290
+ const retry = options?.retry;
291
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
292
+ yield* factory();
293
+ return;
294
+ }
295
+ const maxRetries = retry.maxRetries;
296
+ const initialDelay = retry.initialDelayMs ?? 1e3;
297
+ const multiplier = retry.backoffMultiplier ?? 2;
298
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
299
+ try {
300
+ const stream = factory();
301
+ const iterator = stream[Symbol.asyncIterator]();
302
+ const first = await iterator.next();
303
+ if (first.done) return;
304
+ yield first.value;
305
+ while (true) {
306
+ const next = await iterator.next();
307
+ if (next.done) break;
308
+ yield next.value;
309
+ }
310
+ return;
311
+ } catch (err) {
312
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
313
+ throw err;
314
+ }
315
+ const delay = initialDelay * Math.pow(multiplier, attempt);
316
+ await new Promise((resolve) => setTimeout(resolve, delay));
317
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
318
+ throw err;
319
+ }
320
+ }
321
+ }
322
+ }
323
+ // ─── CallOptions Resolution ──────────────────────────────────
324
+ /** Resolve tools to use for this call (per-call override > config default) */
325
+ resolveTools(options) {
326
+ return options?.tools ?? this.config.tools ?? [];
327
+ }
154
328
  // ─── Usage Enrichment ───────────────────────────────────────────
155
329
  /** Enrich result usage with model/backend and fire onUsage callback */
156
- enrichAndNotifyUsage(result) {
330
+ enrichAndNotifyUsage(result, options) {
157
331
  if (result.usage) {
158
332
  result.usage = {
159
333
  ...result.usage,
160
- model: this.config.model,
334
+ model: options.model,
161
335
  backend: this.backendName
162
336
  };
163
337
  this.callOnUsage(result.usage);
164
338
  }
165
339
  }
166
340
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
167
- async *enrichStream(source) {
341
+ async *enrichStream(source, options) {
342
+ const model = options.model;
168
343
  for await (const event of source) {
169
344
  if (event.type === "usage_update") {
170
345
  const usage = {
171
346
  promptTokens: event.promptTokens,
172
347
  completionTokens: event.completionTokens,
173
- model: this.config.model,
348
+ model,
174
349
  backend: this.backendName
175
350
  };
176
351
  this.callOnUsage(usage);
@@ -240,6 +415,35 @@ var BaseAgent = class {
240
415
  heartbeatResolve = null;
241
416
  }
242
417
  }
418
+ // ─── Activity Timeout ────────────────────────────────────────
419
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
420
+ * When timeoutMs is not set, passes through directly. */
421
+ async *activityTimeoutStream(source, timeoutMs, ac) {
422
+ if (!timeoutMs || timeoutMs <= 0) {
423
+ yield* source;
424
+ return;
425
+ }
426
+ const iterator = source[Symbol.asyncIterator]();
427
+ let timerId;
428
+ try {
429
+ while (true) {
430
+ const timeoutPromise = new Promise((_, reject) => {
431
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
432
+ });
433
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
434
+ clearTimeout(timerId);
435
+ if (result.done) break;
436
+ yield result.value;
437
+ }
438
+ } catch (err) {
439
+ if (err instanceof ActivityTimeoutError) {
440
+ ac.abort(err);
441
+ }
442
+ throw err;
443
+ } finally {
444
+ clearTimeout(timerId);
445
+ }
446
+ }
243
447
  // ─── Guards ───────────────────────────────────────────────────
244
448
  guardReentrancy() {
245
449
  if (this.state === "running" || this.state === "streaming") {
@@ -338,13 +542,67 @@ function extractSchemaFromDef(schema) {
338
542
  }
339
543
  }
340
544
 
545
+ // src/backends/shared.ts
546
+ function extractLastUserPrompt(messages) {
547
+ for (let i = messages.length - 1; i >= 0; i--) {
548
+ const msg = messages[i];
549
+ if (msg.role === "user") {
550
+ return getTextContent(msg.content);
551
+ }
552
+ }
553
+ return "";
554
+ }
555
+ function serializeToolCall(tc) {
556
+ const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
557
+ return ` Tool call: ${tc.name}(${args})`;
558
+ }
559
+ function serializeToolResult(tr) {
560
+ const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
561
+ const prefix = tr.isError ? "[ERROR] " : "";
562
+ return ` ${tr.name} \u2192 ${prefix}${result}`;
563
+ }
564
+ function buildContextualPrompt(messages) {
565
+ if (messages.length <= 1) {
566
+ return extractLastUserPrompt(messages);
567
+ }
568
+ const history = messages.slice(0, -1).map((msg) => {
569
+ if (msg.role === "user") {
570
+ return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
571
+ }
572
+ if (msg.role === "tool" && msg.toolResults) {
573
+ const results = msg.toolResults.map(serializeToolResult).join("\n");
574
+ return `Tool results:
575
+ ${results}`;
576
+ }
577
+ if (msg.role === "assistant") {
578
+ const parts = [];
579
+ const thinking = msg.thinking;
580
+ if (thinking) {
581
+ parts.push(`[reasoning: ${thinking}]`);
582
+ }
583
+ const text2 = msg.content ? getTextContent(msg.content) : "";
584
+ if (text2) parts.push(text2);
585
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
586
+ parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
587
+ }
588
+ return `Assistant: ${parts.join("\n")}`;
589
+ }
590
+ const text = msg.content ? getTextContent(msg.content) : "";
591
+ return `${msg.role}: ${text}`;
592
+ }).join("\n");
593
+ const lastPrompt = extractLastUserPrompt(messages);
594
+ return `Conversation history:
595
+ ${history}
596
+
597
+ User: ${lastPrompt}`;
598
+ }
599
+
341
600
  // src/backends/copilot.ts
342
- var sdkModule = null;
601
+ var _sdkMock = null;
343
602
  async function loadSDK() {
344
- if (sdkModule) return sdkModule;
603
+ if (_sdkMock) return _sdkMock;
345
604
  try {
346
- sdkModule = await import('@github/copilot-sdk');
347
- return sdkModule;
605
+ return await import('@github/copilot-sdk');
348
606
  } catch {
349
607
  throw new SubprocessError(
350
608
  "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
@@ -352,10 +610,10 @@ async function loadSDK() {
352
610
  }
353
611
  }
354
612
  function _injectSDK(mock) {
355
- sdkModule = mock;
613
+ _sdkMock = mock;
356
614
  }
357
615
  function _resetSDK() {
358
- sdkModule = null;
616
+ _sdkMock = null;
359
617
  }
360
618
  function mapToolsToSDK(tools) {
361
619
  return tools.map((tool) => ({
@@ -375,17 +633,6 @@ function convertParameters(params) {
375
633
  }
376
634
  return params;
377
635
  }
378
- async function mapToolsToSDKAsync(tools) {
379
- return tools.map((tool) => ({
380
- name: tool.name,
381
- description: tool.description,
382
- parameters: convertParameters(tool.parameters),
383
- handler: async (args) => {
384
- const result = await tool.execute(args);
385
- return typeof result === "string" ? result : JSON.stringify(result);
386
- }
387
- }));
388
- }
389
636
  function buildPermissionHandler(config) {
390
637
  const onPermission = config.supervisor?.onPermission;
391
638
  if (!onPermission) {
@@ -400,6 +647,7 @@ function buildPermissionHandler(config) {
400
647
  const unifiedRequest = {
401
648
  toolName,
402
649
  toolArgs: { ...request },
650
+ toolCallId: request.toolCallId,
403
651
  rawSDKRequest: request
404
652
  };
405
653
  const ac = new AbortController();
@@ -542,15 +790,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
542
790
  };
543
791
  case "session.error":
544
792
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
545
- return {
546
- type: "error",
547
- error: String(data.message ?? "Unknown error"),
548
- recoverable: false
549
- };
793
+ {
794
+ const errorMsg = String(data.message ?? "Unknown error");
795
+ const code = classifyAgentError(errorMsg);
796
+ return {
797
+ type: "error",
798
+ error: errorMsg,
799
+ recoverable: isRecoverableErrorCode(code),
800
+ code
801
+ };
802
+ }
550
803
  case "assistant.message": {
551
804
  const doneEvent = {
552
805
  type: "done",
553
- finalOutput: data.content ? String(data.content) : null
806
+ finalOutput: null,
807
+ streamed: true
554
808
  };
555
809
  if (thinkingTracker.endThinking()) {
556
810
  return [{ type: "thinking_end" }, doneEvent];
@@ -570,6 +824,7 @@ var CopilotAgent = class extends BaseAgent {
570
824
  isPersistent;
571
825
  persistentSession = null;
572
826
  _sessionId;
827
+ _persistentModel;
573
828
  activeSession = null;
574
829
  _resumeSessionId;
575
830
  _toolsReady = null;
@@ -588,15 +843,15 @@ var CopilotAgent = class extends BaseAgent {
588
843
  },
589
844
  onPermissionRequest: buildPermissionHandler(config),
590
845
  onUserInputRequest: buildUserInputHandler(config),
591
- ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
846
+ ...config.availableTools ? { availableTools: config.availableTools } : {}
592
847
  };
593
848
  this._toolsReady = this._initToolsAsync(config);
594
849
  this._resumeSessionId = resumeSessionId;
595
850
  }
596
- /** Pre-convert Zod schemas to JSON Schema asynchronously.
851
+ /** Pre-convert Zod schemas to JSON Schema.
597
852
  * Updates sdkTools and sessionConfig.tools before first session creation. */
598
853
  async _initToolsAsync(config) {
599
- this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
854
+ this.sdkTools = mapToolsToSDK(config.tools ?? []);
600
855
  this.sessionConfig.tools = this.sdkTools;
601
856
  }
602
857
  get sessionId() {
@@ -620,47 +875,63 @@ var CopilotAgent = class extends BaseAgent {
620
875
  });
621
876
  this.persistentSession = null;
622
877
  this._sessionId = void 0;
878
+ this._persistentModel = void 0;
623
879
  }
624
880
  }
625
- async getOrCreateSession(streaming) {
881
+ async getOrCreateSession(streaming, options) {
626
882
  if (this.isPersistent && this.persistentSession) {
627
- return { session: this.persistentSession, isNew: false };
883
+ if (options.model !== this._persistentModel) {
884
+ this.persistentSession.destroy().catch(() => {
885
+ });
886
+ this.persistentSession = null;
887
+ this._sessionId = void 0;
888
+ } else {
889
+ return { session: this.persistentSession, isNew: false };
890
+ }
628
891
  }
629
892
  if (this._toolsReady) {
630
893
  await this._toolsReady;
631
894
  this._toolsReady = null;
632
895
  }
896
+ const sessionConfig = { ...this.sessionConfig };
897
+ sessionConfig.model = options.model;
898
+ const resolvedTools = this.resolveTools(options);
899
+ if (options?.tools) {
900
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
901
+ }
633
902
  const client = await this.getClient();
634
903
  if (this._resumeSessionId) {
635
904
  const storedId = this._resumeSessionId;
636
905
  this._resumeSessionId = void 0;
637
906
  try {
638
907
  const session2 = await client.resumeSession(storedId, {
639
- ...this.sessionConfig,
908
+ ...sessionConfig,
640
909
  streaming: this.isPersistent ? true : streaming
641
910
  });
642
911
  if (this.isPersistent) {
643
912
  this.persistentSession = session2;
644
913
  this._sessionId = session2.sessionId;
914
+ this._persistentModel = options.model;
645
915
  }
646
916
  return { session: session2, isNew: false };
647
917
  } catch {
648
918
  }
649
919
  }
650
920
  const session = await client.createSession({
651
- ...this.sessionConfig,
921
+ ...sessionConfig,
652
922
  streaming: this.isPersistent ? true : streaming
653
923
  });
654
924
  if (this.isPersistent) {
655
925
  this.persistentSession = session;
656
926
  this._sessionId = session.sessionId;
927
+ this._persistentModel = options.model;
657
928
  }
658
929
  return { session, isNew: true };
659
930
  }
660
931
  // ─── executeRun ─────────────────────────────────────────────────
661
- async executeRun(messages, _options, signal) {
932
+ async executeRun(messages, options, signal) {
662
933
  this.checkAbort(signal);
663
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
934
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
664
935
  this.activeSession = session;
665
936
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
666
937
  const tracker = new ToolCallTracker();
@@ -757,9 +1028,9 @@ You MUST respond with ONLY valid JSON matching this schema:
757
1028
  };
758
1029
  }
759
1030
  // ─── executeStream ──────────────────────────────────────────────
760
- async *executeStream(messages, _options, signal) {
1031
+ async *executeStream(messages, options, signal) {
761
1032
  this.checkAbort(signal);
762
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1033
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
763
1034
  this.activeSession = session;
764
1035
  if (isNewSession) {
765
1036
  yield this.emitSessionInfo(session.sessionId);
@@ -845,59 +1116,6 @@ You MUST respond with ONLY valid JSON matching this schema:
845
1116
  super.dispose();
846
1117
  }
847
1118
  };
848
- function extractLastUserPrompt(messages) {
849
- for (let i = messages.length - 1; i >= 0; i--) {
850
- const msg = messages[i];
851
- if (msg.role === "user") {
852
- return getTextContent(msg.content);
853
- }
854
- }
855
- return "";
856
- }
857
- function serializeToolCall(tc) {
858
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
859
- return ` Tool call: ${tc.name}(${args})`;
860
- }
861
- function serializeToolResult(tr) {
862
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
863
- const prefix = tr.isError ? "[ERROR] " : "";
864
- return ` ${tr.name} \u2192 ${prefix}${result}`;
865
- }
866
- function buildContextualPrompt(messages) {
867
- if (messages.length <= 1) {
868
- return extractLastUserPrompt(messages);
869
- }
870
- const history = messages.slice(0, -1).map((msg) => {
871
- if (msg.role === "user") {
872
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
873
- }
874
- if (msg.role === "tool" && msg.toolResults) {
875
- const results = msg.toolResults.map(serializeToolResult).join("\n");
876
- return `Tool results:
877
- ${results}`;
878
- }
879
- if (msg.role === "assistant") {
880
- const parts = [];
881
- const thinking = msg.thinking;
882
- if (thinking) {
883
- parts.push(`[reasoning: ${thinking}]`);
884
- }
885
- const text2 = msg.content ? getTextContent(msg.content) : "";
886
- if (text2) parts.push(text2);
887
- if (msg.toolCalls && msg.toolCalls.length > 0) {
888
- parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
889
- }
890
- return `Assistant: ${parts.join("\n")}`;
891
- }
892
- const text = msg.content ? getTextContent(msg.content) : "";
893
- return `${msg.role}: ${text}`;
894
- }).join("\n");
895
- const lastPrompt = extractLastUserPrompt(messages);
896
- return `Conversation history:
897
- ${history}
898
-
899
- User: ${lastPrompt}`;
900
- }
901
1119
  function withTimeout(promise, ms, message) {
902
1120
  return new Promise((resolve, reject) => {
903
1121
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
@@ -939,7 +1157,11 @@ var CopilotAgentService = class {
939
1157
  githubToken: this.options.githubToken,
940
1158
  useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
941
1159
  ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
942
- ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
1160
+ env: {
1161
+ ...process.env,
1162
+ ...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
1163
+ ...this.options.env
1164
+ }
943
1165
  });
944
1166
  const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
945
1167
  await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
@@ -973,7 +1195,10 @@ var CopilotAgentService = class {
973
1195
  return models.map((m) => ({
974
1196
  id: m.id,
975
1197
  name: m.name,
976
- provider: "copilot"
1198
+ provider: "copilot",
1199
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1200
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1201
+ }
977
1202
  }));
978
1203
  }
979
1204
  async validate() {