@witqq/agent-sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +140 -34
  2. package/dist/{types-CqvUAYxt.d.cts → agent-CW9XbmG_.d.ts} +137 -102
  3. package/dist/{types-CqvUAYxt.d.ts → agent-DxY68NZL.d.cts} +137 -102
  4. package/dist/auth/index.cjs +72 -1
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +21 -154
  7. package/dist/auth/index.d.ts +21 -154
  8. package/dist/auth/index.js +72 -1
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/backends/claude.cjs +480 -261
  11. package/dist/backends/claude.cjs.map +1 -1
  12. package/dist/backends/claude.d.cts +3 -1
  13. package/dist/backends/claude.d.ts +3 -1
  14. package/dist/backends/claude.js +480 -261
  15. package/dist/backends/claude.js.map +1 -1
  16. package/dist/backends/copilot.cjs +329 -97
  17. package/dist/backends/copilot.cjs.map +1 -1
  18. package/dist/backends/copilot.d.cts +12 -4
  19. package/dist/backends/copilot.d.ts +12 -4
  20. package/dist/backends/copilot.js +329 -97
  21. package/dist/backends/copilot.js.map +1 -1
  22. package/dist/backends/vercel-ai.cjs +294 -61
  23. package/dist/backends/vercel-ai.cjs.map +1 -1
  24. package/dist/backends/vercel-ai.d.cts +3 -1
  25. package/dist/backends/vercel-ai.d.ts +3 -1
  26. package/dist/backends/vercel-ai.js +294 -61
  27. package/dist/backends/vercel-ai.js.map +1 -1
  28. package/dist/backends-BSrsBYFn.d.cts +39 -0
  29. package/dist/backends-BSrsBYFn.d.ts +39 -0
  30. package/dist/chat/accumulator.cjs +1 -1
  31. package/dist/chat/accumulator.cjs.map +1 -1
  32. package/dist/chat/accumulator.d.cts +5 -2
  33. package/dist/chat/accumulator.d.ts +5 -2
  34. package/dist/chat/accumulator.js +1 -1
  35. package/dist/chat/accumulator.js.map +1 -1
  36. package/dist/chat/backends.cjs +736 -746
  37. package/dist/chat/backends.cjs.map +1 -1
  38. package/dist/chat/backends.d.cts +10 -6
  39. package/dist/chat/backends.d.ts +10 -6
  40. package/dist/chat/backends.js +736 -725
  41. package/dist/chat/backends.js.map +1 -1
  42. package/dist/chat/context.cjs +50 -0
  43. package/dist/chat/context.cjs.map +1 -1
  44. package/dist/chat/context.d.cts +27 -3
  45. package/dist/chat/context.d.ts +27 -3
  46. package/dist/chat/context.js +50 -0
  47. package/dist/chat/context.js.map +1 -1
  48. package/dist/chat/core.cjs +25 -2
  49. package/dist/chat/core.cjs.map +1 -1
  50. package/dist/chat/core.d.cts +30 -381
  51. package/dist/chat/core.d.ts +30 -381
  52. package/dist/chat/core.js +24 -3
  53. package/dist/chat/core.js.map +1 -1
  54. package/dist/chat/errors.cjs +48 -26
  55. package/dist/chat/errors.cjs.map +1 -1
  56. package/dist/chat/errors.d.cts +6 -31
  57. package/dist/chat/errors.d.ts +6 -31
  58. package/dist/chat/errors.js +48 -25
  59. package/dist/chat/errors.js.map +1 -1
  60. package/dist/chat/events.cjs.map +1 -1
  61. package/dist/chat/events.d.cts +6 -2
  62. package/dist/chat/events.d.ts +6 -2
  63. package/dist/chat/events.js.map +1 -1
  64. package/dist/chat/index.cjs +1199 -1008
  65. package/dist/chat/index.cjs.map +1 -1
  66. package/dist/chat/index.d.cts +35 -10
  67. package/dist/chat/index.d.ts +35 -10
  68. package/dist/chat/index.js +1196 -987
  69. package/dist/chat/index.js.map +1 -1
  70. package/dist/chat/react/theme.css +2517 -0
  71. package/dist/chat/react.cjs +2003 -1153
  72. package/dist/chat/react.cjs.map +1 -1
  73. package/dist/chat/react.d.cts +590 -121
  74. package/dist/chat/react.d.ts +590 -121
  75. package/dist/chat/react.js +1984 -1151
  76. package/dist/chat/react.js.map +1 -1
  77. package/dist/chat/runtime.cjs +401 -186
  78. package/dist/chat/runtime.cjs.map +1 -1
  79. package/dist/chat/runtime.d.cts +92 -28
  80. package/dist/chat/runtime.d.ts +92 -28
  81. package/dist/chat/runtime.js +401 -186
  82. package/dist/chat/runtime.js.map +1 -1
  83. package/dist/chat/server.cjs +2234 -209
  84. package/dist/chat/server.cjs.map +1 -1
  85. package/dist/chat/server.d.cts +451 -90
  86. package/dist/chat/server.d.ts +451 -90
  87. package/dist/chat/server.js +2221 -210
  88. package/dist/chat/server.js.map +1 -1
  89. package/dist/chat/sessions.cjs +25 -43
  90. package/dist/chat/sessions.cjs.map +1 -1
  91. package/dist/chat/sessions.d.cts +37 -118
  92. package/dist/chat/sessions.d.ts +37 -118
  93. package/dist/chat/sessions.js +25 -43
  94. package/dist/chat/sessions.js.map +1 -1
  95. package/dist/chat/sqlite.cjs +441 -0
  96. package/dist/chat/sqlite.cjs.map +1 -0
  97. package/dist/chat/sqlite.d.cts +128 -0
  98. package/dist/chat/sqlite.d.ts +128 -0
  99. package/dist/chat/sqlite.js +435 -0
  100. package/dist/chat/sqlite.js.map +1 -0
  101. package/dist/chat/state.cjs +14 -1
  102. package/dist/chat/state.cjs.map +1 -1
  103. package/dist/chat/state.d.cts +5 -2
  104. package/dist/chat/state.d.ts +5 -2
  105. package/dist/chat/state.js +14 -1
  106. package/dist/chat/state.js.map +1 -1
  107. package/dist/chat/storage.cjs +19 -10
  108. package/dist/chat/storage.cjs.map +1 -1
  109. package/dist/chat/storage.d.cts +11 -5
  110. package/dist/chat/storage.d.ts +11 -5
  111. package/dist/chat/storage.js +19 -10
  112. package/dist/chat/storage.js.map +1 -1
  113. package/dist/errors-C-so0M4t.d.cts +33 -0
  114. package/dist/errors-C-so0M4t.d.ts +33 -0
  115. package/dist/errors-CmVvczxZ.d.cts +28 -0
  116. package/dist/errors-CmVvczxZ.d.ts +28 -0
  117. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-C1JnJGVR.d.ts} +28 -23
  118. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-C7DSqPyX.d.cts} +28 -23
  119. package/dist/index.cjs +340 -46
  120. package/dist/index.cjs.map +1 -1
  121. package/dist/index.d.cts +292 -123
  122. package/dist/index.d.ts +292 -123
  123. package/dist/index.js +334 -47
  124. package/dist/index.js.map +1 -1
  125. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  126. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  127. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  128. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  129. package/dist/testing.cjs +383 -0
  130. package/dist/testing.cjs.map +1 -0
  131. package/dist/testing.d.cts +132 -0
  132. package/dist/testing.d.ts +132 -0
  133. package/dist/testing.js +377 -0
  134. package/dist/testing.js.map +1 -0
  135. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  136. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  137. package/dist/{transport-DX1Nhm4N.d.cts → transport-Cdh3M0tS.d.cts} +5 -4
  138. package/dist/{transport-D1OaUgRk.d.ts → transport-Ciap4PWK.d.ts} +5 -4
  139. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  140. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  141. package/dist/types-DRgd_9R7.d.cts +363 -0
  142. package/dist/types-ajANVzf7.d.ts +363 -0
  143. package/package.json +31 -6
  144. package/dist/errors-BDLbNu9w.d.cts +0 -13
  145. package/dist/errors-BDLbNu9w.d.ts +0 -13
  146. package/dist/types-DLZzlJxt.d.ts +0 -39
  147. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,4 +1,42 @@
1
- // src/types.ts
1
+ // src/types/errors.ts
2
+ var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
3
+ "TIMEOUT" /* TIMEOUT */,
4
+ "RATE_LIMIT" /* RATE_LIMIT */,
5
+ "NETWORK" /* NETWORK */,
6
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
7
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
8
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
9
+ ]);
10
+ function isRecoverableErrorCode(code) {
11
+ return RECOVERABLE_CODES.has(code);
12
+ }
13
+ function classifyAgentError(error) {
14
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
15
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
16
+ return "TIMEOUT" /* TIMEOUT */;
17
+ }
18
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
19
+ return "RATE_LIMIT" /* RATE_LIMIT */;
20
+ }
21
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
22
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
23
+ }
24
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
25
+ return "NETWORK" /* NETWORK */;
26
+ }
27
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
28
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
29
+ }
30
+ if (msg.includes("abort") || msg.includes("cancel")) {
31
+ return "ABORTED" /* ABORTED */;
32
+ }
33
+ if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
34
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
35
+ }
36
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
37
+ }
38
+
39
+ // src/types/guards.ts
2
40
  function getTextContent(content) {
3
41
  if (typeof content === "string") return content;
4
42
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
@@ -8,9 +46,18 @@ function getTextContent(content) {
8
46
  var AgentSDKError = class extends Error {
9
47
  /** @internal Marker for cross-bundle identity checks */
10
48
  _agentSDKError = true;
49
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
50
+ code;
51
+ /** Whether this error is safe to retry */
52
+ retryable;
53
+ /** HTTP status code hint for error classification */
54
+ httpStatus;
11
55
  constructor(message, options) {
12
56
  super(message, options);
13
57
  this.name = "AgentSDKError";
58
+ this.code = options?.code;
59
+ this.retryable = options?.retryable ?? false;
60
+ this.httpStatus = options?.httpStatus;
14
61
  }
15
62
  /** Check if an error is an AgentSDKError (works across bundled copies) */
16
63
  static is(error) {
@@ -19,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) => ({
@@ -398,6 +656,7 @@ function buildPermissionHandler(config) {
398
656
  const unifiedRequest = {
399
657
  toolName,
400
658
  toolArgs: { ...request },
659
+ toolCallId: request.toolCallId,
401
660
  rawSDKRequest: request
402
661
  };
403
662
  const ac = new AbortController();
@@ -540,15 +799,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
540
799
  };
541
800
  case "session.error":
542
801
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
543
- return {
544
- type: "error",
545
- error: String(data.message ?? "Unknown error"),
546
- recoverable: false
547
- };
802
+ {
803
+ const errorMsg = String(data.message ?? "Unknown error");
804
+ const code = classifyAgentError(errorMsg);
805
+ return {
806
+ type: "error",
807
+ error: errorMsg,
808
+ recoverable: isRecoverableErrorCode(code),
809
+ code
810
+ };
811
+ }
548
812
  case "assistant.message": {
549
813
  const doneEvent = {
550
814
  type: "done",
551
- finalOutput: data.content ? String(data.content) : null
815
+ finalOutput: null,
816
+ streamed: true
552
817
  };
553
818
  if (thinkingTracker.endThinking()) {
554
819
  return [{ type: "thinking_end" }, doneEvent];
@@ -568,6 +833,7 @@ var CopilotAgent = class extends BaseAgent {
568
833
  isPersistent;
569
834
  persistentSession = null;
570
835
  _sessionId;
836
+ _persistentModel;
571
837
  activeSession = null;
572
838
  _resumeSessionId;
573
839
  _toolsReady = null;
@@ -618,47 +884,63 @@ var CopilotAgent = class extends BaseAgent {
618
884
  });
619
885
  this.persistentSession = null;
620
886
  this._sessionId = void 0;
887
+ this._persistentModel = void 0;
621
888
  }
622
889
  }
623
- async getOrCreateSession(streaming) {
890
+ async getOrCreateSession(streaming, options) {
624
891
  if (this.isPersistent && this.persistentSession) {
625
- return { session: this.persistentSession, isNew: false };
892
+ if (options.model !== this._persistentModel) {
893
+ this.persistentSession.destroy().catch(() => {
894
+ });
895
+ this.persistentSession = null;
896
+ this._sessionId = void 0;
897
+ } else {
898
+ return { session: this.persistentSession, isNew: false };
899
+ }
626
900
  }
627
901
  if (this._toolsReady) {
628
902
  await this._toolsReady;
629
903
  this._toolsReady = null;
630
904
  }
905
+ const sessionConfig = { ...this.sessionConfig };
906
+ sessionConfig.model = options.model;
907
+ const resolvedTools = this.resolveTools(options);
908
+ if (options?.tools) {
909
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
910
+ }
631
911
  const client = await this.getClient();
632
912
  if (this._resumeSessionId) {
633
913
  const storedId = this._resumeSessionId;
634
914
  this._resumeSessionId = void 0;
635
915
  try {
636
916
  const session2 = await client.resumeSession(storedId, {
637
- ...this.sessionConfig,
917
+ ...sessionConfig,
638
918
  streaming: this.isPersistent ? true : streaming
639
919
  });
640
920
  if (this.isPersistent) {
641
921
  this.persistentSession = session2;
642
922
  this._sessionId = session2.sessionId;
923
+ this._persistentModel = options.model;
643
924
  }
644
925
  return { session: session2, isNew: false };
645
926
  } catch {
646
927
  }
647
928
  }
648
929
  const session = await client.createSession({
649
- ...this.sessionConfig,
930
+ ...sessionConfig,
650
931
  streaming: this.isPersistent ? true : streaming
651
932
  });
652
933
  if (this.isPersistent) {
653
934
  this.persistentSession = session;
654
935
  this._sessionId = session.sessionId;
936
+ this._persistentModel = options.model;
655
937
  }
656
938
  return { session, isNew: true };
657
939
  }
658
940
  // ─── executeRun ─────────────────────────────────────────────────
659
- async executeRun(messages, _options, signal) {
941
+ async executeRun(messages, options, signal) {
660
942
  this.checkAbort(signal);
661
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
943
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
662
944
  this.activeSession = session;
663
945
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
664
946
  const tracker = new ToolCallTracker();
@@ -755,9 +1037,9 @@ You MUST respond with ONLY valid JSON matching this schema:
755
1037
  };
756
1038
  }
757
1039
  // ─── executeStream ──────────────────────────────────────────────
758
- async *executeStream(messages, _options, signal) {
1040
+ async *executeStream(messages, options, signal) {
759
1041
  this.checkAbort(signal);
760
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1042
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
761
1043
  this.activeSession = session;
762
1044
  if (isNewSession) {
763
1045
  yield this.emitSessionInfo(session.sessionId);
@@ -843,59 +1125,6 @@ You MUST respond with ONLY valid JSON matching this schema:
843
1125
  super.dispose();
844
1126
  }
845
1127
  };
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
1128
  function withTimeout(promise, ms, message) {
900
1129
  return new Promise((resolve, reject) => {
901
1130
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
@@ -971,7 +1200,10 @@ var CopilotAgentService = class {
971
1200
  return models.map((m) => ({
972
1201
  id: m.id,
973
1202
  name: m.name,
974
- provider: "copilot"
1203
+ provider: "copilot",
1204
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1205
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1206
+ }
975
1207
  }));
976
1208
  }
977
1209
  async validate() {