@witqq/agent-sdk 0.7.0 → 0.8.0

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