@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,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,28 +66,41 @@ 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 SubprocessError = class extends AgentSDKError {
33
84
  constructor(message, options) {
34
- super(message, options);
85
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
35
86
  this.name = "SubprocessError";
36
87
  }
37
88
  };
38
89
  var AbortError = class extends AgentSDKError {
39
90
  constructor() {
40
- super("Agent run was aborted.");
91
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
41
92
  this.name = "AbortError";
42
93
  }
43
94
  };
95
+ var ActivityTimeoutError = class extends AgentSDKError {
96
+ constructor(timeoutMs) {
97
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
98
+ code: "TIMEOUT" /* TIMEOUT */,
99
+ retryable: true
100
+ });
101
+ this.name = "ActivityTimeoutError";
102
+ }
103
+ };
44
104
 
45
105
  // src/base-agent.ts
46
106
  var BaseAgent = class {
@@ -48,6 +108,7 @@ var BaseAgent = class {
48
108
  abortController = null;
49
109
  config;
50
110
  _cleanupExternalSignal = null;
111
+ _streamMiddleware = [];
51
112
  /** CLI session ID for persistent mode. Override in backends that support it. */
52
113
  get sessionId() {
53
114
  return void 0;
@@ -63,8 +124,11 @@ var BaseAgent = class {
63
124
  this.state = "running";
64
125
  try {
65
126
  const messages = [{ role: "user", content: prompt }];
66
- const result = await this.executeRun(messages, options, ac.signal);
67
- this.enrichAndNotifyUsage(result);
127
+ const result = await this.withRetry(
128
+ () => this.executeRun(messages, options, ac.signal),
129
+ options
130
+ );
131
+ this.enrichAndNotifyUsage(result, options);
68
132
  return result;
69
133
  } finally {
70
134
  this.cleanupRun();
@@ -76,8 +140,11 @@ var BaseAgent = class {
76
140
  const ac = this.createAbortController(options?.signal);
77
141
  this.state = "running";
78
142
  try {
79
- const result = await this.executeRun(messages, options, ac.signal);
80
- this.enrichAndNotifyUsage(result);
143
+ const result = await this.withRetry(
144
+ () => this.executeRun(messages, options, ac.signal),
145
+ options
146
+ );
147
+ this.enrichAndNotifyUsage(result, options);
81
148
  return result;
82
149
  } finally {
83
150
  this.cleanupRun();
@@ -90,13 +157,11 @@ var BaseAgent = class {
90
157
  this.state = "running";
91
158
  try {
92
159
  const messages = [{ role: "user", content: prompt }];
93
- const result = await this.executeRunStructured(
94
- messages,
95
- schema,
96
- options,
97
- ac.signal
160
+ const result = await this.withRetry(
161
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
162
+ options
98
163
  );
99
- this.enrichAndNotifyUsage(result);
164
+ this.enrichAndNotifyUsage(result, options);
100
165
  return result;
101
166
  } finally {
102
167
  this.cleanupRun();
@@ -109,8 +174,10 @@ var BaseAgent = class {
109
174
  this.state = "streaming";
110
175
  try {
111
176
  const messages = [{ role: "user", content: prompt }];
112
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
113
- yield* this.heartbeatStream(enriched);
177
+ yield* this.streamWithRetry(
178
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
179
+ options
180
+ );
114
181
  } finally {
115
182
  this.cleanupRun();
116
183
  }
@@ -121,12 +188,37 @@ var BaseAgent = class {
121
188
  const ac = this.createAbortController(options?.signal);
122
189
  this.state = "streaming";
123
190
  try {
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
  }
129
198
  }
199
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
200
+ addStreamMiddleware(middleware) {
201
+ this.guardDisposed();
202
+ this._streamMiddleware.push(middleware);
203
+ }
204
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
205
+ async *applyStreamPipeline(source, options, ac) {
206
+ let stream = this.enrichStream(source, options);
207
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
208
+ stream = this.heartbeatStream(stream);
209
+ if (this._streamMiddleware.length > 0) {
210
+ const ctx = {
211
+ model: options.model,
212
+ backend: this.backendName,
213
+ abortController: ac,
214
+ config: Object.freeze({ ...this.config })
215
+ };
216
+ for (const mw of this._streamMiddleware) {
217
+ stream = mw(stream, ctx);
218
+ }
219
+ }
220
+ yield* stream;
221
+ }
130
222
  abort() {
131
223
  if (this.abortController) {
132
224
  this.abortController.abort();
@@ -149,26 +241,109 @@ var BaseAgent = class {
149
241
  this.abort();
150
242
  this.state = "disposed";
151
243
  }
244
+ // ─── Retry Logic ─────────────────────────────────────────────
245
+ /** Check if an error should be retried given the retry configuration. */
246
+ isRetryableError(error, retry) {
247
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
248
+ return false;
249
+ }
250
+ if (AgentSDKError.is(error)) {
251
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
252
+ return retry.retryableErrors.includes(error.code);
253
+ }
254
+ if (error.retryable) return true;
255
+ if (error.code) return isRecoverableErrorCode(error.code);
256
+ }
257
+ return false;
258
+ }
259
+ /** Execute a function with retry logic per RetryConfig. */
260
+ async withRetry(fn, options) {
261
+ const retry = options?.retry;
262
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
263
+ return fn();
264
+ }
265
+ const maxRetries = retry.maxRetries;
266
+ const initialDelay = retry.initialDelayMs ?? 1e3;
267
+ const multiplier = retry.backoffMultiplier ?? 2;
268
+ let lastError;
269
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
270
+ try {
271
+ return await fn();
272
+ } catch (err) {
273
+ lastError = err;
274
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
275
+ throw err;
276
+ }
277
+ const delay = initialDelay * Math.pow(multiplier, attempt);
278
+ await new Promise((resolve) => setTimeout(resolve, delay));
279
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
280
+ throw err;
281
+ }
282
+ }
283
+ }
284
+ throw lastError;
285
+ }
286
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
287
+ async *streamWithRetry(factory, options) {
288
+ const retry = options?.retry;
289
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
290
+ yield* factory();
291
+ return;
292
+ }
293
+ const maxRetries = retry.maxRetries;
294
+ const initialDelay = retry.initialDelayMs ?? 1e3;
295
+ const multiplier = retry.backoffMultiplier ?? 2;
296
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
297
+ try {
298
+ const stream = factory();
299
+ const iterator = stream[Symbol.asyncIterator]();
300
+ const first = await iterator.next();
301
+ if (first.done) return;
302
+ yield first.value;
303
+ while (true) {
304
+ const next = await iterator.next();
305
+ if (next.done) break;
306
+ yield next.value;
307
+ }
308
+ return;
309
+ } catch (err) {
310
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
311
+ throw err;
312
+ }
313
+ const delay = initialDelay * Math.pow(multiplier, attempt);
314
+ await new Promise((resolve) => setTimeout(resolve, delay));
315
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
316
+ throw err;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ // ─── CallOptions Resolution ──────────────────────────────────
322
+ /** Resolve tools to use for this call (per-call override > config default) */
323
+ resolveTools(options) {
324
+ return options?.tools ?? this.config.tools ?? [];
325
+ }
152
326
  // ─── Usage Enrichment ───────────────────────────────────────────
153
327
  /** Enrich result usage with model/backend and fire onUsage callback */
154
- enrichAndNotifyUsage(result) {
328
+ enrichAndNotifyUsage(result, options) {
155
329
  if (result.usage) {
156
330
  result.usage = {
157
331
  ...result.usage,
158
- model: this.config.model,
332
+ model: options.model,
159
333
  backend: this.backendName
160
334
  };
161
335
  this.callOnUsage(result.usage);
162
336
  }
163
337
  }
164
338
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
165
- async *enrichStream(source) {
339
+ async *enrichStream(source, options) {
340
+ const model = options.model;
166
341
  for await (const event of source) {
167
342
  if (event.type === "usage_update") {
168
343
  const usage = {
169
344
  promptTokens: event.promptTokens,
170
345
  completionTokens: event.completionTokens,
171
- model: this.config.model,
346
+ model,
172
347
  backend: this.backendName
173
348
  };
174
349
  this.callOnUsage(usage);
@@ -238,6 +413,35 @@ var BaseAgent = class {
238
413
  heartbeatResolve = null;
239
414
  }
240
415
  }
416
+ // ─── Activity Timeout ────────────────────────────────────────
417
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
418
+ * When timeoutMs is not set, passes through directly. */
419
+ async *activityTimeoutStream(source, timeoutMs, ac) {
420
+ if (!timeoutMs || timeoutMs <= 0) {
421
+ yield* source;
422
+ return;
423
+ }
424
+ const iterator = source[Symbol.asyncIterator]();
425
+ let timerId;
426
+ try {
427
+ while (true) {
428
+ const timeoutPromise = new Promise((_, reject) => {
429
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
430
+ });
431
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
432
+ clearTimeout(timerId);
433
+ if (result.done) break;
434
+ yield result.value;
435
+ }
436
+ } catch (err) {
437
+ if (err instanceof ActivityTimeoutError) {
438
+ ac.abort(err);
439
+ }
440
+ throw err;
441
+ } finally {
442
+ clearTimeout(timerId);
443
+ }
444
+ }
241
445
  // ─── Guards ───────────────────────────────────────────────────
242
446
  guardReentrancy() {
243
447
  if (this.state === "running" || this.state === "streaming") {
@@ -336,13 +540,67 @@ function extractSchemaFromDef(schema) {
336
540
  }
337
541
  }
338
542
 
543
+ // src/backends/shared.ts
544
+ function extractLastUserPrompt(messages) {
545
+ for (let i = messages.length - 1; i >= 0; i--) {
546
+ const msg = messages[i];
547
+ if (msg.role === "user") {
548
+ return getTextContent(msg.content);
549
+ }
550
+ }
551
+ return "";
552
+ }
553
+ function serializeToolCall(tc) {
554
+ const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
555
+ return ` Tool call: ${tc.name}(${args})`;
556
+ }
557
+ function serializeToolResult(tr) {
558
+ const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
559
+ const prefix = tr.isError ? "[ERROR] " : "";
560
+ return ` ${tr.name} \u2192 ${prefix}${result}`;
561
+ }
562
+ function buildContextualPrompt(messages) {
563
+ if (messages.length <= 1) {
564
+ return extractLastUserPrompt(messages);
565
+ }
566
+ const history = messages.slice(0, -1).map((msg) => {
567
+ if (msg.role === "user") {
568
+ return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
569
+ }
570
+ if (msg.role === "tool" && msg.toolResults) {
571
+ const results = msg.toolResults.map(serializeToolResult).join("\n");
572
+ return `Tool results:
573
+ ${results}`;
574
+ }
575
+ if (msg.role === "assistant") {
576
+ const parts = [];
577
+ const thinking = msg.thinking;
578
+ if (thinking) {
579
+ parts.push(`[reasoning: ${thinking}]`);
580
+ }
581
+ const text2 = msg.content ? getTextContent(msg.content) : "";
582
+ if (text2) parts.push(text2);
583
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
584
+ parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
585
+ }
586
+ return `Assistant: ${parts.join("\n")}`;
587
+ }
588
+ const text = msg.content ? getTextContent(msg.content) : "";
589
+ return `${msg.role}: ${text}`;
590
+ }).join("\n");
591
+ const lastPrompt = extractLastUserPrompt(messages);
592
+ return `Conversation history:
593
+ ${history}
594
+
595
+ User: ${lastPrompt}`;
596
+ }
597
+
339
598
  // src/backends/copilot.ts
340
- var sdkModule = null;
599
+ var _sdkMock = null;
341
600
  async function loadSDK() {
342
- if (sdkModule) return sdkModule;
601
+ if (_sdkMock) return _sdkMock;
343
602
  try {
344
- sdkModule = await import('@github/copilot-sdk');
345
- return sdkModule;
603
+ return await import('@github/copilot-sdk');
346
604
  } catch {
347
605
  throw new SubprocessError(
348
606
  "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
@@ -350,10 +608,10 @@ async function loadSDK() {
350
608
  }
351
609
  }
352
610
  function _injectSDK(mock) {
353
- sdkModule = mock;
611
+ _sdkMock = mock;
354
612
  }
355
613
  function _resetSDK() {
356
- sdkModule = null;
614
+ _sdkMock = null;
357
615
  }
358
616
  function mapToolsToSDK(tools) {
359
617
  return tools.map((tool) => ({
@@ -373,17 +631,6 @@ function convertParameters(params) {
373
631
  }
374
632
  return params;
375
633
  }
376
- async function mapToolsToSDKAsync(tools) {
377
- return tools.map((tool) => ({
378
- name: tool.name,
379
- description: tool.description,
380
- parameters: convertParameters(tool.parameters),
381
- handler: async (args) => {
382
- const result = await tool.execute(args);
383
- return typeof result === "string" ? result : JSON.stringify(result);
384
- }
385
- }));
386
- }
387
634
  function buildPermissionHandler(config) {
388
635
  const onPermission = config.supervisor?.onPermission;
389
636
  if (!onPermission) {
@@ -398,6 +645,7 @@ function buildPermissionHandler(config) {
398
645
  const unifiedRequest = {
399
646
  toolName,
400
647
  toolArgs: { ...request },
648
+ toolCallId: request.toolCallId,
401
649
  rawSDKRequest: request
402
650
  };
403
651
  const ac = new AbortController();
@@ -540,15 +788,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
540
788
  };
541
789
  case "session.error":
542
790
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
543
- return {
544
- type: "error",
545
- error: String(data.message ?? "Unknown error"),
546
- recoverable: false
547
- };
791
+ {
792
+ const errorMsg = String(data.message ?? "Unknown error");
793
+ const code = classifyAgentError(errorMsg);
794
+ return {
795
+ type: "error",
796
+ error: errorMsg,
797
+ recoverable: isRecoverableErrorCode(code),
798
+ code
799
+ };
800
+ }
548
801
  case "assistant.message": {
549
802
  const doneEvent = {
550
803
  type: "done",
551
- finalOutput: data.content ? String(data.content) : null
804
+ finalOutput: null,
805
+ streamed: true
552
806
  };
553
807
  if (thinkingTracker.endThinking()) {
554
808
  return [{ type: "thinking_end" }, doneEvent];
@@ -568,6 +822,7 @@ var CopilotAgent = class extends BaseAgent {
568
822
  isPersistent;
569
823
  persistentSession = null;
570
824
  _sessionId;
825
+ _persistentModel;
571
826
  activeSession = null;
572
827
  _resumeSessionId;
573
828
  _toolsReady = null;
@@ -586,15 +841,15 @@ var CopilotAgent = class extends BaseAgent {
586
841
  },
587
842
  onPermissionRequest: buildPermissionHandler(config),
588
843
  onUserInputRequest: buildUserInputHandler(config),
589
- ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
844
+ ...config.availableTools ? { availableTools: config.availableTools } : {}
590
845
  };
591
846
  this._toolsReady = this._initToolsAsync(config);
592
847
  this._resumeSessionId = resumeSessionId;
593
848
  }
594
- /** Pre-convert Zod schemas to JSON Schema asynchronously.
849
+ /** Pre-convert Zod schemas to JSON Schema.
595
850
  * Updates sdkTools and sessionConfig.tools before first session creation. */
596
851
  async _initToolsAsync(config) {
597
- this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
852
+ this.sdkTools = mapToolsToSDK(config.tools ?? []);
598
853
  this.sessionConfig.tools = this.sdkTools;
599
854
  }
600
855
  get sessionId() {
@@ -618,47 +873,63 @@ var CopilotAgent = class extends BaseAgent {
618
873
  });
619
874
  this.persistentSession = null;
620
875
  this._sessionId = void 0;
876
+ this._persistentModel = void 0;
621
877
  }
622
878
  }
623
- async getOrCreateSession(streaming) {
879
+ async getOrCreateSession(streaming, options) {
624
880
  if (this.isPersistent && this.persistentSession) {
625
- return { session: this.persistentSession, isNew: false };
881
+ if (options.model !== this._persistentModel) {
882
+ this.persistentSession.destroy().catch(() => {
883
+ });
884
+ this.persistentSession = null;
885
+ this._sessionId = void 0;
886
+ } else {
887
+ return { session: this.persistentSession, isNew: false };
888
+ }
626
889
  }
627
890
  if (this._toolsReady) {
628
891
  await this._toolsReady;
629
892
  this._toolsReady = null;
630
893
  }
894
+ const sessionConfig = { ...this.sessionConfig };
895
+ sessionConfig.model = options.model;
896
+ const resolvedTools = this.resolveTools(options);
897
+ if (options?.tools) {
898
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
899
+ }
631
900
  const client = await this.getClient();
632
901
  if (this._resumeSessionId) {
633
902
  const storedId = this._resumeSessionId;
634
903
  this._resumeSessionId = void 0;
635
904
  try {
636
905
  const session2 = await client.resumeSession(storedId, {
637
- ...this.sessionConfig,
906
+ ...sessionConfig,
638
907
  streaming: this.isPersistent ? true : streaming
639
908
  });
640
909
  if (this.isPersistent) {
641
910
  this.persistentSession = session2;
642
911
  this._sessionId = session2.sessionId;
912
+ this._persistentModel = options.model;
643
913
  }
644
914
  return { session: session2, isNew: false };
645
915
  } catch {
646
916
  }
647
917
  }
648
918
  const session = await client.createSession({
649
- ...this.sessionConfig,
919
+ ...sessionConfig,
650
920
  streaming: this.isPersistent ? true : streaming
651
921
  });
652
922
  if (this.isPersistent) {
653
923
  this.persistentSession = session;
654
924
  this._sessionId = session.sessionId;
925
+ this._persistentModel = options.model;
655
926
  }
656
927
  return { session, isNew: true };
657
928
  }
658
929
  // ─── executeRun ─────────────────────────────────────────────────
659
- async executeRun(messages, _options, signal) {
930
+ async executeRun(messages, options, signal) {
660
931
  this.checkAbort(signal);
661
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
932
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
662
933
  this.activeSession = session;
663
934
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
664
935
  const tracker = new ToolCallTracker();
@@ -755,9 +1026,9 @@ You MUST respond with ONLY valid JSON matching this schema:
755
1026
  };
756
1027
  }
757
1028
  // ─── executeStream ──────────────────────────────────────────────
758
- async *executeStream(messages, _options, signal) {
1029
+ async *executeStream(messages, options, signal) {
759
1030
  this.checkAbort(signal);
760
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1031
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
761
1032
  this.activeSession = session;
762
1033
  if (isNewSession) {
763
1034
  yield this.emitSessionInfo(session.sessionId);
@@ -843,59 +1114,6 @@ You MUST respond with ONLY valid JSON matching this schema:
843
1114
  super.dispose();
844
1115
  }
845
1116
  };
846
- function extractLastUserPrompt(messages) {
847
- for (let i = messages.length - 1; i >= 0; i--) {
848
- const msg = messages[i];
849
- if (msg.role === "user") {
850
- return getTextContent(msg.content);
851
- }
852
- }
853
- return "";
854
- }
855
- function serializeToolCall(tc) {
856
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
857
- return ` Tool call: ${tc.name}(${args})`;
858
- }
859
- function serializeToolResult(tr) {
860
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
861
- const prefix = tr.isError ? "[ERROR] " : "";
862
- return ` ${tr.name} \u2192 ${prefix}${result}`;
863
- }
864
- function buildContextualPrompt(messages) {
865
- if (messages.length <= 1) {
866
- return extractLastUserPrompt(messages);
867
- }
868
- const history = messages.slice(0, -1).map((msg) => {
869
- if (msg.role === "user") {
870
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
871
- }
872
- if (msg.role === "tool" && msg.toolResults) {
873
- const results = msg.toolResults.map(serializeToolResult).join("\n");
874
- return `Tool results:
875
- ${results}`;
876
- }
877
- if (msg.role === "assistant") {
878
- const parts = [];
879
- const thinking = msg.thinking;
880
- if (thinking) {
881
- parts.push(`[reasoning: ${thinking}]`);
882
- }
883
- const text2 = msg.content ? getTextContent(msg.content) : "";
884
- if (text2) parts.push(text2);
885
- if (msg.toolCalls && msg.toolCalls.length > 0) {
886
- parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
887
- }
888
- return `Assistant: ${parts.join("\n")}`;
889
- }
890
- const text = msg.content ? getTextContent(msg.content) : "";
891
- return `${msg.role}: ${text}`;
892
- }).join("\n");
893
- const lastPrompt = extractLastUserPrompt(messages);
894
- return `Conversation history:
895
- ${history}
896
-
897
- User: ${lastPrompt}`;
898
- }
899
1117
  function withTimeout(promise, ms, message) {
900
1118
  return new Promise((resolve, reject) => {
901
1119
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
@@ -937,7 +1155,11 @@ var CopilotAgentService = class {
937
1155
  githubToken: this.options.githubToken,
938
1156
  useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
939
1157
  ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
940
- ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
1158
+ env: {
1159
+ ...process.env,
1160
+ ...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
1161
+ ...this.options.env
1162
+ }
941
1163
  });
942
1164
  const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
943
1165
  await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
@@ -971,7 +1193,10 @@ var CopilotAgentService = class {
971
1193
  return models.map((m) => ({
972
1194
  id: m.id,
973
1195
  name: m.name,
974
- provider: "copilot"
1196
+ provider: "copilot",
1197
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1198
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1199
+ }
975
1200
  }));
976
1201
  }
977
1202
  async validate() {