@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,7 +1,3 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
-
5
1
  var __defProp = Object.defineProperty;
6
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -23,16 +19,69 @@ var __copyProps = (to, from, except, desc) => {
23
19
  };
24
20
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
21
 
26
- // src/errors.ts
27
- var AgentSDKError, ReentrancyError, DisposedError, BackendNotFoundError, BackendAlreadyRegisteredError, SubprocessError, DependencyError, AbortError, ToolExecutionError, StructuredOutputError;
22
+ // src/types/errors.ts
23
+ function isRecoverableErrorCode(code) {
24
+ return RECOVERABLE_CODES.has(code);
25
+ }
26
+ function classifyAgentError(error) {
27
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
28
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
29
+ return "TIMEOUT" /* TIMEOUT */;
30
+ }
31
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
32
+ return "RATE_LIMIT" /* RATE_LIMIT */;
33
+ }
34
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
35
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
36
+ }
37
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
38
+ return "NETWORK" /* NETWORK */;
39
+ }
40
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
41
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
42
+ }
43
+ if (msg.includes("abort") || msg.includes("cancel")) {
44
+ return "ABORTED" /* ABORTED */;
45
+ }
46
+ 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")) {
47
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
48
+ }
49
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
50
+ }
51
+ var RECOVERABLE_CODES;
28
52
  var init_errors = __esm({
53
+ "src/types/errors.ts"() {
54
+ RECOVERABLE_CODES = /* @__PURE__ */ new Set([
55
+ "TIMEOUT" /* TIMEOUT */,
56
+ "RATE_LIMIT" /* RATE_LIMIT */,
57
+ "NETWORK" /* NETWORK */,
58
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
59
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
60
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
61
+ ]);
62
+ }
63
+ });
64
+
65
+ // src/errors.ts
66
+ var AgentSDKError, ReentrancyError, DisposedError, SubprocessError, DependencyError, AbortError, ToolExecutionError, ActivityTimeoutError;
67
+ var init_errors2 = __esm({
29
68
  "src/errors.ts"() {
69
+ init_errors();
30
70
  AgentSDKError = class extends Error {
31
71
  /** @internal Marker for cross-bundle identity checks */
32
72
  _agentSDKError = true;
73
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
74
+ code;
75
+ /** Whether this error is safe to retry */
76
+ retryable;
77
+ /** HTTP status code hint for error classification */
78
+ httpStatus;
33
79
  constructor(message, options) {
34
80
  super(message, options);
35
81
  this.name = "AgentSDKError";
82
+ this.code = options?.code;
83
+ this.retryable = options?.retryable ?? false;
84
+ this.httpStatus = options?.httpStatus;
36
85
  }
37
86
  /** Check if an error is an AgentSDKError (works across bundled copies) */
38
87
  static is(error) {
@@ -41,83 +90,84 @@ var init_errors = __esm({
41
90
  };
42
91
  ReentrancyError = class extends AgentSDKError {
43
92
  constructor() {
44
- super("Agent is already running. Await the current run before starting another.");
93
+ super("Agent is already running. Await the current run before starting another.", {
94
+ code: "REENTRANCY" /* REENTRANCY */
95
+ });
45
96
  this.name = "ReentrancyError";
46
97
  }
47
98
  };
48
99
  DisposedError = class extends AgentSDKError {
49
100
  constructor(entity) {
50
- super(`${entity} has been disposed and cannot be used.`);
101
+ super(`${entity} has been disposed and cannot be used.`, {
102
+ code: "DISPOSED" /* DISPOSED */
103
+ });
51
104
  this.name = "DisposedError";
52
105
  }
53
106
  };
54
- BackendNotFoundError = class extends AgentSDKError {
55
- constructor(backend) {
56
- super(
57
- `Unknown backend: "${backend}". Built-in: copilot, claude, vercel-ai. Custom: use registerBackend() first.`
58
- );
59
- this.name = "BackendNotFoundError";
60
- }
61
- };
62
- BackendAlreadyRegisteredError = class extends AgentSDKError {
63
- constructor(backend) {
64
- super(`Backend "${backend}" is already registered. Use a different name or unregister first.`);
65
- this.name = "BackendAlreadyRegisteredError";
66
- }
67
- };
68
107
  SubprocessError = class extends AgentSDKError {
69
108
  constructor(message, options) {
70
- super(message, options);
109
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
71
110
  this.name = "SubprocessError";
72
111
  }
73
112
  };
74
113
  DependencyError = class extends AgentSDKError {
75
114
  packageName;
76
115
  constructor(packageName) {
77
- super(`${packageName} is not installed. Install it: npm install ${packageName}`);
116
+ super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
117
+ code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
118
+ });
78
119
  this.name = "DependencyError";
79
120
  this.packageName = packageName;
80
121
  }
81
122
  };
82
123
  AbortError = class extends AgentSDKError {
83
124
  constructor() {
84
- super("Agent run was aborted.");
125
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
85
126
  this.name = "AbortError";
86
127
  }
87
128
  };
88
129
  ToolExecutionError = class extends AgentSDKError {
89
130
  toolName;
90
131
  constructor(toolName, message, options) {
91
- super(`Tool "${toolName}" failed: ${message}`, options);
132
+ super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
92
133
  this.name = "ToolExecutionError";
93
134
  this.toolName = toolName;
94
135
  }
95
136
  };
96
- StructuredOutputError = class extends AgentSDKError {
97
- constructor(message, options) {
98
- super(`Structured output error: ${message}`, options);
99
- this.name = "StructuredOutputError";
137
+ ActivityTimeoutError = class extends AgentSDKError {
138
+ constructor(timeoutMs) {
139
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
140
+ code: "TIMEOUT" /* TIMEOUT */,
141
+ retryable: true
142
+ });
143
+ this.name = "ActivityTimeoutError";
100
144
  }
101
145
  };
102
146
  }
103
147
  });
104
148
 
105
- // src/types.ts
106
- function isToolDefinition(tool) {
107
- return "execute" in tool && typeof tool.execute === "function";
108
- }
109
- function isTextContent(content) {
110
- return typeof content === "string";
111
- }
112
- function isMultiPartContent(content) {
113
- return Array.isArray(content);
114
- }
149
+ // src/types/guards.ts
115
150
  function getTextContent(content) {
116
151
  if (typeof content === "string") return content;
117
152
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
118
153
  }
154
+ var init_guards = __esm({
155
+ "src/types/guards.ts"() {
156
+ }
157
+ });
158
+
159
+ // src/types/index.ts
119
160
  var init_types = __esm({
161
+ "src/types/index.ts"() {
162
+ init_errors();
163
+ init_guards();
164
+ }
165
+ });
166
+
167
+ // src/types.ts
168
+ var init_types2 = __esm({
120
169
  "src/types.ts"() {
170
+ init_types();
121
171
  }
122
172
  });
123
173
 
@@ -125,12 +175,15 @@ var init_types = __esm({
125
175
  var BaseAgent;
126
176
  var init_base_agent = __esm({
127
177
  "src/base-agent.ts"() {
178
+ init_errors2();
179
+ init_errors2();
128
180
  init_errors();
129
181
  BaseAgent = class {
130
182
  state = "idle";
131
183
  abortController = null;
132
184
  config;
133
185
  _cleanupExternalSignal = null;
186
+ _streamMiddleware = [];
134
187
  /** CLI session ID for persistent mode. Override in backends that support it. */
135
188
  get sessionId() {
136
189
  return void 0;
@@ -146,8 +199,11 @@ var init_base_agent = __esm({
146
199
  this.state = "running";
147
200
  try {
148
201
  const messages = [{ role: "user", content: prompt }];
149
- const result = await this.executeRun(messages, options, ac.signal);
150
- this.enrichAndNotifyUsage(result);
202
+ const result = await this.withRetry(
203
+ () => this.executeRun(messages, options, ac.signal),
204
+ options
205
+ );
206
+ this.enrichAndNotifyUsage(result, options);
151
207
  return result;
152
208
  } finally {
153
209
  this.cleanupRun();
@@ -159,8 +215,11 @@ var init_base_agent = __esm({
159
215
  const ac = this.createAbortController(options?.signal);
160
216
  this.state = "running";
161
217
  try {
162
- const result = await this.executeRun(messages, options, ac.signal);
163
- this.enrichAndNotifyUsage(result);
218
+ const result = await this.withRetry(
219
+ () => this.executeRun(messages, options, ac.signal),
220
+ options
221
+ );
222
+ this.enrichAndNotifyUsage(result, options);
164
223
  return result;
165
224
  } finally {
166
225
  this.cleanupRun();
@@ -173,13 +232,11 @@ var init_base_agent = __esm({
173
232
  this.state = "running";
174
233
  try {
175
234
  const messages = [{ role: "user", content: prompt }];
176
- const result = await this.executeRunStructured(
177
- messages,
178
- schema,
179
- options,
180
- ac.signal
235
+ const result = await this.withRetry(
236
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
237
+ options
181
238
  );
182
- this.enrichAndNotifyUsage(result);
239
+ this.enrichAndNotifyUsage(result, options);
183
240
  return result;
184
241
  } finally {
185
242
  this.cleanupRun();
@@ -192,8 +249,10 @@ var init_base_agent = __esm({
192
249
  this.state = "streaming";
193
250
  try {
194
251
  const messages = [{ role: "user", content: prompt }];
195
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
196
- yield* this.heartbeatStream(enriched);
252
+ yield* this.streamWithRetry(
253
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
254
+ options
255
+ );
197
256
  } finally {
198
257
  this.cleanupRun();
199
258
  }
@@ -204,12 +263,37 @@ var init_base_agent = __esm({
204
263
  const ac = this.createAbortController(options?.signal);
205
264
  this.state = "streaming";
206
265
  try {
207
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
208
- yield* this.heartbeatStream(enriched);
266
+ yield* this.streamWithRetry(
267
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
268
+ options
269
+ );
209
270
  } finally {
210
271
  this.cleanupRun();
211
272
  }
212
273
  }
274
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
275
+ addStreamMiddleware(middleware) {
276
+ this.guardDisposed();
277
+ this._streamMiddleware.push(middleware);
278
+ }
279
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
280
+ async *applyStreamPipeline(source, options, ac) {
281
+ let stream = this.enrichStream(source, options);
282
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
283
+ stream = this.heartbeatStream(stream);
284
+ if (this._streamMiddleware.length > 0) {
285
+ const ctx = {
286
+ model: options.model,
287
+ backend: this.backendName,
288
+ abortController: ac,
289
+ config: Object.freeze({ ...this.config })
290
+ };
291
+ for (const mw of this._streamMiddleware) {
292
+ stream = mw(stream, ctx);
293
+ }
294
+ }
295
+ yield* stream;
296
+ }
213
297
  abort() {
214
298
  if (this.abortController) {
215
299
  this.abortController.abort();
@@ -232,26 +316,109 @@ var init_base_agent = __esm({
232
316
  this.abort();
233
317
  this.state = "disposed";
234
318
  }
319
+ // ─── Retry Logic ─────────────────────────────────────────────
320
+ /** Check if an error should be retried given the retry configuration. */
321
+ isRetryableError(error, retry) {
322
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
323
+ return false;
324
+ }
325
+ if (AgentSDKError.is(error)) {
326
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
327
+ return retry.retryableErrors.includes(error.code);
328
+ }
329
+ if (error.retryable) return true;
330
+ if (error.code) return isRecoverableErrorCode(error.code);
331
+ }
332
+ return false;
333
+ }
334
+ /** Execute a function with retry logic per RetryConfig. */
335
+ async withRetry(fn, options) {
336
+ const retry = options?.retry;
337
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
338
+ return fn();
339
+ }
340
+ const maxRetries = retry.maxRetries;
341
+ const initialDelay = retry.initialDelayMs ?? 1e3;
342
+ const multiplier = retry.backoffMultiplier ?? 2;
343
+ let lastError;
344
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
345
+ try {
346
+ return await fn();
347
+ } catch (err) {
348
+ lastError = err;
349
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
350
+ throw err;
351
+ }
352
+ const delay = initialDelay * Math.pow(multiplier, attempt);
353
+ await new Promise((resolve) => setTimeout(resolve, delay));
354
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
355
+ throw err;
356
+ }
357
+ }
358
+ }
359
+ throw lastError;
360
+ }
361
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
362
+ async *streamWithRetry(factory, options) {
363
+ const retry = options?.retry;
364
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
365
+ yield* factory();
366
+ return;
367
+ }
368
+ const maxRetries = retry.maxRetries;
369
+ const initialDelay = retry.initialDelayMs ?? 1e3;
370
+ const multiplier = retry.backoffMultiplier ?? 2;
371
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
372
+ try {
373
+ const stream = factory();
374
+ const iterator = stream[Symbol.asyncIterator]();
375
+ const first = await iterator.next();
376
+ if (first.done) return;
377
+ yield first.value;
378
+ while (true) {
379
+ const next = await iterator.next();
380
+ if (next.done) break;
381
+ yield next.value;
382
+ }
383
+ return;
384
+ } catch (err) {
385
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
386
+ throw err;
387
+ }
388
+ const delay = initialDelay * Math.pow(multiplier, attempt);
389
+ await new Promise((resolve) => setTimeout(resolve, delay));
390
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
391
+ throw err;
392
+ }
393
+ }
394
+ }
395
+ }
396
+ // ─── CallOptions Resolution ──────────────────────────────────
397
+ /** Resolve tools to use for this call (per-call override > config default) */
398
+ resolveTools(options) {
399
+ return options?.tools ?? this.config.tools ?? [];
400
+ }
235
401
  // ─── Usage Enrichment ───────────────────────────────────────────
236
402
  /** Enrich result usage with model/backend and fire onUsage callback */
237
- enrichAndNotifyUsage(result) {
403
+ enrichAndNotifyUsage(result, options) {
238
404
  if (result.usage) {
239
405
  result.usage = {
240
406
  ...result.usage,
241
- model: this.config.model,
407
+ model: options.model,
242
408
  backend: this.backendName
243
409
  };
244
410
  this.callOnUsage(result.usage);
245
411
  }
246
412
  }
247
413
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
248
- async *enrichStream(source) {
414
+ async *enrichStream(source, options) {
415
+ const model = options.model;
249
416
  for await (const event of source) {
250
417
  if (event.type === "usage_update") {
251
418
  const usage = {
252
419
  promptTokens: event.promptTokens,
253
420
  completionTokens: event.completionTokens,
254
- model: this.config.model,
421
+ model,
255
422
  backend: this.backendName
256
423
  };
257
424
  this.callOnUsage(usage);
@@ -287,9 +454,9 @@ var init_base_agent = __esm({
287
454
  let heartbeatResolve = null;
288
455
  const timer = setInterval(() => {
289
456
  if (heartbeatResolve) {
290
- const resolve2 = heartbeatResolve;
457
+ const resolve = heartbeatResolve;
291
458
  heartbeatResolve = null;
292
- resolve2();
459
+ resolve();
293
460
  }
294
461
  }, interval);
295
462
  try {
@@ -297,8 +464,8 @@ var init_base_agent = __esm({
297
464
  if (!pendingEvent) {
298
465
  pendingEvent = iterator.next();
299
466
  }
300
- const heartbeatPromise = new Promise((resolve2) => {
301
- heartbeatResolve = resolve2;
467
+ const heartbeatPromise = new Promise((resolve) => {
468
+ heartbeatResolve = resolve;
302
469
  });
303
470
  const eventDone = pendingEvent.then(
304
471
  (r) => ({ kind: "event", result: r })
@@ -321,6 +488,35 @@ var init_base_agent = __esm({
321
488
  heartbeatResolve = null;
322
489
  }
323
490
  }
491
+ // ─── Activity Timeout ────────────────────────────────────────
492
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
493
+ * When timeoutMs is not set, passes through directly. */
494
+ async *activityTimeoutStream(source, timeoutMs, ac) {
495
+ if (!timeoutMs || timeoutMs <= 0) {
496
+ yield* source;
497
+ return;
498
+ }
499
+ const iterator = source[Symbol.asyncIterator]();
500
+ let timerId;
501
+ try {
502
+ while (true) {
503
+ const timeoutPromise = new Promise((_, reject) => {
504
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
505
+ });
506
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
507
+ clearTimeout(timerId);
508
+ if (result.done) break;
509
+ yield result.value;
510
+ }
511
+ } catch (err) {
512
+ if (err instanceof ActivityTimeoutError) {
513
+ ac.abort(err);
514
+ }
515
+ throw err;
516
+ } finally {
517
+ clearTimeout(timerId);
518
+ }
519
+ }
324
520
  // ─── Guards ───────────────────────────────────────────────────
325
521
  guardReentrancy() {
326
522
  if (this.state === "running" || this.state === "streaming") {
@@ -425,6 +621,66 @@ var init_schema = __esm({
425
621
  }
426
622
  });
427
623
 
624
+ // src/backends/shared.ts
625
+ function extractLastUserPrompt(messages) {
626
+ for (let i = messages.length - 1; i >= 0; i--) {
627
+ const msg = messages[i];
628
+ if (msg.role === "user") {
629
+ return getTextContent(msg.content);
630
+ }
631
+ }
632
+ return "";
633
+ }
634
+ function serializeToolCall(tc) {
635
+ const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
636
+ return ` Tool call: ${tc.name}(${args})`;
637
+ }
638
+ function serializeToolResult(tr) {
639
+ const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
640
+ const prefix = tr.isError ? "[ERROR] " : "";
641
+ return ` ${tr.name} \u2192 ${prefix}${result}`;
642
+ }
643
+ function buildContextualPrompt(messages) {
644
+ if (messages.length <= 1) {
645
+ return extractLastUserPrompt(messages);
646
+ }
647
+ const history = messages.slice(0, -1).map((msg) => {
648
+ if (msg.role === "user") {
649
+ return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
650
+ }
651
+ if (msg.role === "tool" && msg.toolResults) {
652
+ const results = msg.toolResults.map(serializeToolResult).join("\n");
653
+ return `Tool results:
654
+ ${results}`;
655
+ }
656
+ if (msg.role === "assistant") {
657
+ const parts = [];
658
+ const thinking = msg.thinking;
659
+ if (thinking) {
660
+ parts.push(`[reasoning: ${thinking}]`);
661
+ }
662
+ const text2 = msg.content ? getTextContent(msg.content) : "";
663
+ if (text2) parts.push(text2);
664
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
665
+ parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
666
+ }
667
+ return `Assistant: ${parts.join("\n")}`;
668
+ }
669
+ const text = msg.content ? getTextContent(msg.content) : "";
670
+ return `${msg.role}: ${text}`;
671
+ }).join("\n");
672
+ const lastPrompt = extractLastUserPrompt(messages);
673
+ return `Conversation history:
674
+ ${history}
675
+
676
+ User: ${lastPrompt}`;
677
+ }
678
+ var init_shared = __esm({
679
+ "src/backends/shared.ts"() {
680
+ init_types2();
681
+ }
682
+ });
683
+
428
684
  // src/backends/copilot.ts
429
685
  var copilot_exports = {};
430
686
  __export(copilot_exports, {
@@ -433,10 +689,9 @@ __export(copilot_exports, {
433
689
  createCopilotService: () => createCopilotService
434
690
  });
435
691
  async function loadSDK() {
436
- if (sdkModule) return sdkModule;
692
+ if (_sdkMock) return _sdkMock;
437
693
  try {
438
- sdkModule = await import('@github/copilot-sdk');
439
- return sdkModule;
694
+ return await import('@github/copilot-sdk');
440
695
  } catch {
441
696
  throw new SubprocessError(
442
697
  "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
@@ -444,10 +699,10 @@ async function loadSDK() {
444
699
  }
445
700
  }
446
701
  function _injectSDK(mock) {
447
- sdkModule = mock;
702
+ _sdkMock = mock;
448
703
  }
449
704
  function _resetSDK() {
450
- sdkModule = null;
705
+ _sdkMock = null;
451
706
  }
452
707
  function mapToolsToSDK(tools) {
453
708
  return tools.map((tool) => ({
@@ -492,6 +747,7 @@ function buildPermissionHandler(config) {
492
747
  const unifiedRequest = {
493
748
  toolName,
494
749
  toolArgs: { ...request },
750
+ toolCallId: request.toolCallId,
495
751
  rawSDKRequest: request
496
752
  };
497
753
  const ac = new AbortController();
@@ -596,15 +852,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
596
852
  };
597
853
  case "session.error":
598
854
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
599
- return {
600
- type: "error",
601
- error: String(data.message ?? "Unknown error"),
602
- recoverable: false
603
- };
855
+ {
856
+ const errorMsg = String(data.message ?? "Unknown error");
857
+ const code = classifyAgentError(errorMsg);
858
+ return {
859
+ type: "error",
860
+ error: errorMsg,
861
+ recoverable: isRecoverableErrorCode(code),
862
+ code
863
+ };
864
+ }
604
865
  case "assistant.message": {
605
866
  const doneEvent = {
606
867
  type: "done",
607
- finalOutput: data.content ? String(data.content) : null
868
+ finalOutput: null,
869
+ streamed: true
608
870
  };
609
871
  if (thinkingTracker.endThinking()) {
610
872
  return [{ type: "thinking_end" }, doneEvent];
@@ -615,66 +877,13 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
615
877
  return null;
616
878
  }
617
879
  }
618
- function extractLastUserPrompt(messages) {
619
- for (let i = messages.length - 1; i >= 0; i--) {
620
- const msg = messages[i];
621
- if (msg.role === "user") {
622
- return getTextContent(msg.content);
623
- }
624
- }
625
- return "";
626
- }
627
- function serializeToolCall(tc) {
628
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
629
- return ` Tool call: ${tc.name}(${args})`;
630
- }
631
- function serializeToolResult(tr) {
632
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
633
- const prefix = tr.isError ? "[ERROR] " : "";
634
- return ` ${tr.name} \u2192 ${prefix}${result}`;
635
- }
636
- function buildContextualPrompt(messages) {
637
- if (messages.length <= 1) {
638
- return extractLastUserPrompt(messages);
639
- }
640
- const history = messages.slice(0, -1).map((msg) => {
641
- if (msg.role === "user") {
642
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
643
- }
644
- if (msg.role === "tool" && msg.toolResults) {
645
- const results = msg.toolResults.map(serializeToolResult).join("\n");
646
- return `Tool results:
647
- ${results}`;
648
- }
649
- if (msg.role === "assistant") {
650
- const parts = [];
651
- const thinking = msg.thinking;
652
- if (thinking) {
653
- parts.push(`[reasoning: ${thinking}]`);
654
- }
655
- const text2 = msg.content ? getTextContent(msg.content) : "";
656
- if (text2) parts.push(text2);
657
- if (msg.toolCalls && msg.toolCalls.length > 0) {
658
- parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
659
- }
660
- return `Assistant: ${parts.join("\n")}`;
661
- }
662
- const text = msg.content ? getTextContent(msg.content) : "";
663
- return `${msg.role}: ${text}`;
664
- }).join("\n");
665
- const lastPrompt = extractLastUserPrompt(messages);
666
- return `Conversation history:
667
- ${history}
668
-
669
- User: ${lastPrompt}`;
670
- }
671
880
  function withTimeout(promise, ms, message) {
672
- return new Promise((resolve2, reject) => {
881
+ return new Promise((resolve, reject) => {
673
882
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
674
883
  promise.then(
675
884
  (val) => {
676
885
  clearTimeout(timer);
677
- resolve2(val);
886
+ resolve(val);
678
887
  },
679
888
  (err) => {
680
889
  clearTimeout(timer);
@@ -686,14 +895,15 @@ function withTimeout(promise, ms, message) {
686
895
  function createCopilotService(options) {
687
896
  return new CopilotAgentService(options);
688
897
  }
689
- var sdkModule, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
898
+ var _sdkMock, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
690
899
  var init_copilot = __esm({
691
900
  "src/backends/copilot.ts"() {
692
- init_types();
901
+ init_types2();
693
902
  init_base_agent();
694
- init_errors();
903
+ init_errors2();
695
904
  init_schema();
696
- sdkModule = null;
905
+ init_shared();
906
+ _sdkMock = null;
697
907
  ToolCallTracker = class {
698
908
  map = /* @__PURE__ */ new Map();
699
909
  trackStart(toolCallId, toolName, args) {
@@ -741,6 +951,7 @@ var init_copilot = __esm({
741
951
  isPersistent;
742
952
  persistentSession = null;
743
953
  _sessionId;
954
+ _persistentModel;
744
955
  activeSession = null;
745
956
  _resumeSessionId;
746
957
  _toolsReady = null;
@@ -791,47 +1002,63 @@ var init_copilot = __esm({
791
1002
  });
792
1003
  this.persistentSession = null;
793
1004
  this._sessionId = void 0;
1005
+ this._persistentModel = void 0;
794
1006
  }
795
1007
  }
796
- async getOrCreateSession(streaming) {
1008
+ async getOrCreateSession(streaming, options) {
797
1009
  if (this.isPersistent && this.persistentSession) {
798
- return { session: this.persistentSession, isNew: false };
1010
+ if (options.model !== this._persistentModel) {
1011
+ this.persistentSession.destroy().catch(() => {
1012
+ });
1013
+ this.persistentSession = null;
1014
+ this._sessionId = void 0;
1015
+ } else {
1016
+ return { session: this.persistentSession, isNew: false };
1017
+ }
799
1018
  }
800
1019
  if (this._toolsReady) {
801
1020
  await this._toolsReady;
802
1021
  this._toolsReady = null;
803
1022
  }
1023
+ const sessionConfig = { ...this.sessionConfig };
1024
+ sessionConfig.model = options.model;
1025
+ const resolvedTools = this.resolveTools(options);
1026
+ if (options?.tools) {
1027
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
1028
+ }
804
1029
  const client = await this.getClient();
805
1030
  if (this._resumeSessionId) {
806
1031
  const storedId = this._resumeSessionId;
807
1032
  this._resumeSessionId = void 0;
808
1033
  try {
809
1034
  const session2 = await client.resumeSession(storedId, {
810
- ...this.sessionConfig,
1035
+ ...sessionConfig,
811
1036
  streaming: this.isPersistent ? true : streaming
812
1037
  });
813
1038
  if (this.isPersistent) {
814
1039
  this.persistentSession = session2;
815
1040
  this._sessionId = session2.sessionId;
1041
+ this._persistentModel = options.model;
816
1042
  }
817
1043
  return { session: session2, isNew: false };
818
1044
  } catch {
819
1045
  }
820
1046
  }
821
1047
  const session = await client.createSession({
822
- ...this.sessionConfig,
1048
+ ...sessionConfig,
823
1049
  streaming: this.isPersistent ? true : streaming
824
1050
  });
825
1051
  if (this.isPersistent) {
826
1052
  this.persistentSession = session;
827
1053
  this._sessionId = session.sessionId;
1054
+ this._persistentModel = options.model;
828
1055
  }
829
1056
  return { session, isNew: true };
830
1057
  }
831
1058
  // ─── executeRun ─────────────────────────────────────────────────
832
- async executeRun(messages, _options, signal) {
1059
+ async executeRun(messages, options, signal) {
833
1060
  this.checkAbort(signal);
834
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
1061
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
835
1062
  this.activeSession = session;
836
1063
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
837
1064
  const tracker = new ToolCallTracker();
@@ -928,9 +1155,9 @@ You MUST respond with ONLY valid JSON matching this schema:
928
1155
  };
929
1156
  }
930
1157
  // ─── executeStream ──────────────────────────────────────────────
931
- async *executeStream(messages, _options, signal) {
1158
+ async *executeStream(messages, options, signal) {
932
1159
  this.checkAbort(signal);
933
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1160
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
934
1161
  this.activeSession = session;
935
1162
  if (isNewSession) {
936
1163
  yield this.emitSessionInfo(session.sessionId);
@@ -947,8 +1174,8 @@ You MUST respond with ONLY valid JSON matching this schema:
947
1174
  notify = null;
948
1175
  }
949
1176
  };
950
- const waitForItem = () => new Promise((resolve2) => {
951
- notify = resolve2;
1177
+ const waitForItem = () => new Promise((resolve) => {
1178
+ notify = resolve;
952
1179
  });
953
1180
  const unsubscribe = session.on((event) => {
954
1181
  const mapped = mapSessionEvent(event, tracker, thinkingTracker);
@@ -1076,7 +1303,10 @@ You MUST respond with ONLY valid JSON matching this schema:
1076
1303
  return models.map((m) => ({
1077
1304
  id: m.id,
1078
1305
  name: m.name,
1079
- provider: "copilot"
1306
+ provider: "copilot",
1307
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1308
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1309
+ }
1080
1310
  }));
1081
1311
  }
1082
1312
  async validate() {
@@ -1129,10 +1359,9 @@ function stripMcpPrefix(name) {
1129
1359
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
1130
1360
  }
1131
1361
  async function loadSDK2() {
1132
- if (sdkModule2) return sdkModule2;
1362
+ if (_sdkMock2) return _sdkMock2;
1133
1363
  try {
1134
- sdkModule2 = await import('@anthropic-ai/claude-agent-sdk');
1135
- return sdkModule2;
1364
+ return await import('@anthropic-ai/claude-agent-sdk');
1136
1365
  } catch {
1137
1366
  throw new SubprocessError(
1138
1367
  "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
@@ -1140,13 +1369,32 @@ async function loadSDK2() {
1140
1369
  }
1141
1370
  }
1142
1371
  function _injectSDK2(mock) {
1143
- sdkModule2 = mock;
1372
+ _sdkMock2 = mock;
1144
1373
  }
1145
1374
  function _resetSDK2() {
1146
- sdkModule2 = null;
1375
+ _sdkMock2 = null;
1376
+ }
1377
+ function normalizeAskUserInput(args) {
1378
+ if (typeof args.question === "string") {
1379
+ return {
1380
+ question: args.question,
1381
+ choices: Array.isArray(args.choices) ? args.choices : void 0,
1382
+ allowFreeform: args.allowFreeform !== false
1383
+ };
1384
+ }
1385
+ const questions = args.questions;
1386
+ if (questions && questions.length > 0) {
1387
+ const first = questions[0];
1388
+ return {
1389
+ question: first.question,
1390
+ choices: first.options?.map((o) => o.label),
1391
+ allowFreeform: true
1392
+ };
1393
+ }
1394
+ return { question: JSON.stringify(args), allowFreeform: true };
1147
1395
  }
1148
- function buildMcpServer(sdk, tools, toolResultCapture) {
1149
- if (tools.length === 0) return void 0;
1396
+ function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
1397
+ if (tools.length === 0 && !onAskUser) return void 0;
1150
1398
  const mcpTools = tools.map((tool) => {
1151
1399
  const zodSchema = tool.parameters;
1152
1400
  const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
@@ -1170,6 +1418,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
1170
1418
  }
1171
1419
  );
1172
1420
  });
1421
+ if (onAskUser) {
1422
+ const askUserTool = sdk.tool(
1423
+ "ask_user",
1424
+ "Ask the user a question and wait for their response",
1425
+ {
1426
+ question: { type: "string", description: "The question to ask the user" },
1427
+ choices: {
1428
+ type: "array",
1429
+ items: { type: "string" },
1430
+ description: "Optional list of choices for multiple choice"
1431
+ },
1432
+ questions: {
1433
+ type: "array",
1434
+ items: {
1435
+ type: "object",
1436
+ properties: {
1437
+ question: { type: "string" },
1438
+ options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
1439
+ }
1440
+ },
1441
+ description: "Alternative nested question format"
1442
+ }
1443
+ },
1444
+ async (args) => {
1445
+ const normalized = normalizeAskUserInput(args);
1446
+ const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
1447
+ return {
1448
+ content: [{ type: "text", text: response.answer }]
1449
+ };
1450
+ }
1451
+ );
1452
+ mcpTools.push(askUserTool);
1453
+ }
1173
1454
  return sdk.createSdkMcpServer({
1174
1455
  name: MCP_SERVER_NAME,
1175
1456
  version: "1.0.0",
@@ -1219,6 +1500,7 @@ function buildCanUseTool(config) {
1219
1500
  const unifiedRequest = {
1220
1501
  toolName,
1221
1502
  toolArgs: input,
1503
+ toolCallId: options.toolUseID,
1222
1504
  suggestedScope: extractSuggestedScope(options.suggestions),
1223
1505
  rawSDKRequest: { toolName, input, ...options }
1224
1506
  };
@@ -1271,6 +1553,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1271
1553
  if (block.type === "tool_use") {
1272
1554
  const toolCallId = String(block.id ?? "");
1273
1555
  const toolName = stripMcpPrefix(block.name ?? "unknown");
1556
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1274
1557
  if (toolCallTracker) {
1275
1558
  toolCallTracker.trackStart(toolCallId, toolName);
1276
1559
  }
@@ -1294,6 +1577,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1294
1577
  case "tool_use_summary": {
1295
1578
  const summary = msg.summary;
1296
1579
  const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
1580
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
1297
1581
  const precedingIds = msg.preceding_tool_use_ids;
1298
1582
  let toolCallId = "";
1299
1583
  if (precedingIds && precedingIds.length > 0) {
@@ -1347,10 +1631,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1347
1631
  }
1348
1632
  if (msg.is_error) {
1349
1633
  const r = msg;
1634
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
1635
+ const code = classifyAgentError(errorMsg);
1350
1636
  return {
1351
1637
  type: "error",
1352
- error: r.errors?.join("; ") ?? "Unknown error",
1353
- recoverable: false
1638
+ error: errorMsg,
1639
+ recoverable: isRecoverableErrorCode(code),
1640
+ code
1354
1641
  };
1355
1642
  }
1356
1643
  return null;
@@ -1359,72 +1646,21 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1359
1646
  return null;
1360
1647
  }
1361
1648
  }
1362
- function extractLastUserPrompt2(messages) {
1363
- for (let i = messages.length - 1; i >= 0; i--) {
1364
- const msg = messages[i];
1365
- if (msg.role === "user") {
1366
- return getTextContent(msg.content);
1367
- }
1368
- }
1369
- return "";
1370
- }
1371
- function serializeToolCall2(tc) {
1372
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
1373
- return ` Tool call: ${tc.name}(${args})`;
1374
- }
1375
- function serializeToolResult2(tr) {
1376
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
1377
- const prefix = tr.isError ? "[ERROR] " : "";
1378
- return ` ${tr.name} \u2192 ${prefix}${result}`;
1379
- }
1380
- function buildContextualPrompt2(messages) {
1381
- if (messages.length <= 1) {
1382
- return extractLastUserPrompt2(messages);
1383
- }
1384
- const history = messages.slice(0, -1).map((msg) => {
1385
- if (msg.role === "user") {
1386
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
1387
- }
1388
- if (msg.role === "tool" && msg.toolResults) {
1389
- const results = msg.toolResults.map(serializeToolResult2).join("\n");
1390
- return `Tool results:
1391
- ${results}`;
1392
- }
1393
- if (msg.role === "assistant") {
1394
- const parts = [];
1395
- const thinking = msg.thinking;
1396
- if (thinking) {
1397
- parts.push(`[reasoning: ${thinking}]`);
1398
- }
1399
- const text2 = msg.content ? getTextContent(msg.content) : "";
1400
- if (text2) parts.push(text2);
1401
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1402
- parts.push(msg.toolCalls.map(serializeToolCall2).join("\n"));
1403
- }
1404
- return `Assistant: ${parts.join("\n")}`;
1405
- }
1406
- const text = msg.content ? getTextContent(msg.content) : "";
1407
- return `${msg.role}: ${text}`;
1408
- }).join("\n");
1409
- const lastPrompt = extractLastUserPrompt2(messages);
1410
- return `Conversation history:
1411
- ${history}
1412
-
1413
- User: ${lastPrompt}`;
1414
- }
1415
1649
  function createClaudeService(options) {
1416
1650
  return new ClaudeAgentService(options);
1417
1651
  }
1418
- var MCP_SERVER_NAME, MCP_TOOL_PREFIX, sdkModule2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1652
+ var MCP_SERVER_NAME, MCP_TOOL_PREFIX, CLAUDE_INTERNAL_TOOL_NAMES, _sdkMock2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1419
1653
  var init_claude = __esm({
1420
1654
  "src/backends/claude.ts"() {
1421
- init_types();
1655
+ init_types2();
1422
1656
  init_base_agent();
1423
- init_errors();
1657
+ init_errors2();
1424
1658
  init_schema();
1659
+ init_shared();
1425
1660
  MCP_SERVER_NAME = "agent-sdk-tools";
1426
1661
  MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
1427
- sdkModule2 = null;
1662
+ CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1663
+ _sdkMock2 = null;
1428
1664
  ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
1429
1665
  ANTHROPIC_API_VERSION = "2023-06-01";
1430
1666
  ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
@@ -1469,11 +1705,6 @@ var init_claude = __esm({
1469
1705
  if (options.resumeSessionId) {
1470
1706
  this._sessionId = options.resumeSessionId;
1471
1707
  }
1472
- if (config.supervisor?.onAskUser) {
1473
- console.warn(
1474
- "[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
1475
- );
1476
- }
1477
1708
  }
1478
1709
  get sessionId() {
1479
1710
  return this._sessionId;
@@ -1497,12 +1728,12 @@ var init_claude = __esm({
1497
1728
  const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
1498
1729
  return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
1499
1730
  }
1500
- buildQueryOptions(signal) {
1731
+ buildQueryOptions(signal, options) {
1501
1732
  const ac = new AbortController();
1502
1733
  signal.addEventListener("abort", () => ac.abort(), { once: true });
1503
1734
  const opts = {
1504
1735
  abortController: ac,
1505
- model: this.config.model,
1736
+ model: options.model,
1506
1737
  maxTurns: this.options.maxTurns,
1507
1738
  cwd: this.options.workingDirectory,
1508
1739
  pathToClaudeCodeExecutable: this.options.cliPath,
@@ -1532,25 +1763,85 @@ var init_claude = __esm({
1532
1763
  return opts;
1533
1764
  }
1534
1765
  async buildMcpConfig(opts, toolResultCapture) {
1535
- if (this.tools.length === 0) return opts;
1766
+ const onAskUser = this.config.supervisor?.onAskUser;
1767
+ if (this.tools.length === 0 && !onAskUser) return opts;
1536
1768
  const sdk = await loadSDK2();
1537
- const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
1769
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
1538
1770
  if (mcpServer) {
1539
1771
  opts.mcpServers = {
1540
1772
  [MCP_SERVER_NAME]: mcpServer
1541
1773
  };
1542
1774
  const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1775
+ if (onAskUser) {
1776
+ mcpToolNames.push(mcpToolName("ask_user"));
1777
+ }
1543
1778
  opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
1544
1779
  }
1780
+ if (onAskUser) {
1781
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1782
+ }
1545
1783
  return opts;
1546
1784
  }
1785
+ // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
1786
+ /** Setup a retry query: clear session, rebuild with full history */
1787
+ async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
1788
+ this.clearPersistentSession();
1789
+ const retryPrompt = buildContextualPrompt(messages);
1790
+ let retryOpts = this.buildQueryOptions(signal, options);
1791
+ toolResultCapture.clear();
1792
+ retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1793
+ modifyOpts?.(retryOpts);
1794
+ const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1795
+ this.activeQuery = retryQ;
1796
+ return retryQ;
1797
+ }
1798
+ /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
1799
+ collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
1800
+ if (msg.type !== "assistant") return;
1801
+ const betaMessage = msg.message;
1802
+ if (!betaMessage?.content) return;
1803
+ for (const block of betaMessage.content) {
1804
+ if (block.type === "tool_use") {
1805
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1806
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1807
+ toolCalls.push({
1808
+ toolName,
1809
+ args: block.input ?? {},
1810
+ result: toolResultCapture.get(toolName) ?? null,
1811
+ approved: true
1812
+ });
1813
+ }
1814
+ }
1815
+ }
1816
+ /** Back-fill tool results from capture map on summary/result messages */
1817
+ backfillToolResults(msg, toolCalls, toolResultCapture) {
1818
+ if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
1819
+ for (const tc of toolCalls) {
1820
+ if (tc.result === null) {
1821
+ const captured = toolResultCapture.get(tc.toolName);
1822
+ if (captured !== void 0) tc.result = captured;
1823
+ }
1824
+ }
1825
+ }
1826
+ /** Wrap retry inner loop with shared error handling */
1827
+ async withRetryErrorHandling(signal, fn) {
1828
+ try {
1829
+ return await fn();
1830
+ } catch (retryError) {
1831
+ if (this.isPersistent) this.clearPersistentSession();
1832
+ if (signal.aborted) throw new AbortError();
1833
+ throw retryError;
1834
+ } finally {
1835
+ this.activeQuery = null;
1836
+ }
1837
+ }
1547
1838
  // ─── executeRun ─────────────────────────────────────────────────
1548
- async executeRun(messages, _options, signal) {
1839
+ async executeRun(messages, options, signal) {
1549
1840
  this.checkAbort(signal);
1550
1841
  const sdk = await loadSDK2();
1551
1842
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1552
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1553
- let opts = this.buildQueryOptions(signal);
1843
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1844
+ let opts = this.buildQueryOptions(signal, options);
1554
1845
  const toolResultCapture = /* @__PURE__ */ new Map();
1555
1846
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1556
1847
  const q = sdk.query({ prompt, options: opts });
@@ -1560,30 +1851,8 @@ var init_claude = __esm({
1560
1851
  let usage;
1561
1852
  try {
1562
1853
  for await (const msg of q) {
1563
- if (msg.type === "assistant") {
1564
- const betaMessage = msg.message;
1565
- if (betaMessage?.content) {
1566
- for (const block of betaMessage.content) {
1567
- if (block.type === "tool_use") {
1568
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1569
- toolCalls.push({
1570
- toolName,
1571
- args: block.input ?? {},
1572
- result: toolResultCapture.get(toolName) ?? null,
1573
- approved: true
1574
- });
1575
- }
1576
- }
1577
- }
1578
- }
1579
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1580
- for (const tc of toolCalls) {
1581
- if (tc.result === null) {
1582
- const captured = toolResultCapture.get(tc.toolName);
1583
- if (captured !== void 0) tc.result = captured;
1584
- }
1585
- }
1586
- }
1854
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1855
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1587
1856
  if (msg.type === "result") {
1588
1857
  if (msg.subtype === "success") {
1589
1858
  const r = msg;
@@ -1603,41 +1872,13 @@ var init_claude = __esm({
1603
1872
  } catch (e) {
1604
1873
  if (signal.aborted) throw new AbortError();
1605
1874
  if (isResuming && this.isPersistent) {
1606
- this.clearPersistentSession();
1607
- const retryPrompt = buildContextualPrompt2(messages);
1608
- let retryOpts = this.buildQueryOptions(signal);
1609
- toolResultCapture.clear();
1610
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1611
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1612
- this.activeQuery = retryQ;
1875
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1613
1876
  toolCalls.length = 0;
1614
1877
  output = null;
1615
- try {
1878
+ return this.withRetryErrorHandling(signal, async () => {
1616
1879
  for await (const msg of retryQ) {
1617
- if (msg.type === "assistant") {
1618
- const betaMessage = msg.message;
1619
- if (betaMessage?.content) {
1620
- for (const block of betaMessage.content) {
1621
- if (block.type === "tool_use") {
1622
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1623
- toolCalls.push({
1624
- toolName,
1625
- args: block.input ?? {},
1626
- result: toolResultCapture.get(toolName) ?? null,
1627
- approved: true
1628
- });
1629
- }
1630
- }
1631
- }
1632
- }
1633
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1634
- for (const tc of toolCalls) {
1635
- if (tc.result === null) {
1636
- const captured = toolResultCapture.get(tc.toolName);
1637
- if (captured !== void 0) tc.result = captured;
1638
- }
1639
- }
1640
- }
1880
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1881
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1641
1882
  if (msg.type === "result") {
1642
1883
  if (msg.subtype === "success") {
1643
1884
  const r = msg;
@@ -1654,23 +1895,17 @@ var init_claude = __esm({
1654
1895
  }
1655
1896
  }
1656
1897
  }
1657
- } catch (retryError) {
1658
- if (this.isPersistent) this.clearPersistentSession();
1659
- if (signal.aborted) throw new AbortError();
1660
- throw retryError;
1661
- } finally {
1662
- this.activeQuery = null;
1663
- }
1664
- return {
1665
- output,
1666
- structuredOutput: void 0,
1667
- toolCalls,
1668
- messages: [
1669
- ...messages,
1670
- ...output !== null ? [{ role: "assistant", content: output }] : []
1671
- ],
1672
- usage
1673
- };
1898
+ return {
1899
+ output,
1900
+ structuredOutput: void 0,
1901
+ toolCalls,
1902
+ messages: [
1903
+ ...messages,
1904
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1905
+ ],
1906
+ usage
1907
+ };
1908
+ });
1674
1909
  }
1675
1910
  if (this.isPersistent) this.clearPersistentSession();
1676
1911
  throw e;
@@ -1689,12 +1924,12 @@ var init_claude = __esm({
1689
1924
  };
1690
1925
  }
1691
1926
  // ─── executeRunStructured ───────────────────────────────────────
1692
- async executeRunStructured(messages, schema, _options, signal) {
1927
+ async executeRunStructured(messages, schema, options, signal) {
1693
1928
  this.checkAbort(signal);
1694
1929
  const sdk = await loadSDK2();
1695
1930
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1696
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1697
- let opts = this.buildQueryOptions(signal);
1931
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1932
+ let opts = this.buildQueryOptions(signal, options);
1698
1933
  const toolResultCapture = /* @__PURE__ */ new Map();
1699
1934
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1700
1935
  const jsonSchema = zodToJsonSchema(schema.schema);
@@ -1710,30 +1945,8 @@ var init_claude = __esm({
1710
1945
  let usage;
1711
1946
  try {
1712
1947
  for await (const msg of q) {
1713
- if (msg.type === "assistant") {
1714
- const betaMessage = msg.message;
1715
- if (betaMessage?.content) {
1716
- for (const block of betaMessage.content) {
1717
- if (block.type === "tool_use") {
1718
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1719
- toolCalls.push({
1720
- toolName,
1721
- args: block.input ?? {},
1722
- result: toolResultCapture.get(toolName) ?? null,
1723
- approved: true
1724
- });
1725
- }
1726
- }
1727
- }
1728
- }
1729
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1730
- for (const tc of toolCalls) {
1731
- if (tc.result === null) {
1732
- const captured = toolResultCapture.get(tc.toolName);
1733
- if (captured !== void 0) tc.result = captured;
1734
- }
1735
- }
1736
- }
1948
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1949
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1737
1950
  if (msg.type === "result" && msg.subtype === "success") {
1738
1951
  const r = msg;
1739
1952
  output = r.result;
@@ -1768,46 +1981,23 @@ var init_claude = __esm({
1768
1981
  } catch (e) {
1769
1982
  if (signal.aborted) throw new AbortError();
1770
1983
  if (isResuming && this.isPersistent) {
1771
- this.clearPersistentSession();
1772
- const retryPrompt = buildContextualPrompt2(messages);
1773
- let retryOpts = this.buildQueryOptions(signal);
1774
- toolResultCapture.clear();
1775
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1776
- retryOpts.outputFormat = {
1777
- type: "json_schema",
1778
- schema: jsonSchema
1779
- };
1780
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1781
- this.activeQuery = retryQ;
1984
+ const retryQ = await this.prepareRetryQuery(
1985
+ sdk,
1986
+ messages,
1987
+ signal,
1988
+ options,
1989
+ toolResultCapture,
1990
+ (opts2) => {
1991
+ opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
1992
+ }
1993
+ );
1782
1994
  toolCalls.length = 0;
1783
1995
  output = null;
1784
1996
  structuredOutput = void 0;
1785
- try {
1997
+ return this.withRetryErrorHandling(signal, async () => {
1786
1998
  for await (const msg of retryQ) {
1787
- if (msg.type === "assistant") {
1788
- const betaMessage = msg.message;
1789
- if (betaMessage?.content) {
1790
- for (const block of betaMessage.content) {
1791
- if (block.type === "tool_use") {
1792
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1793
- toolCalls.push({
1794
- toolName,
1795
- args: block.input ?? {},
1796
- result: toolResultCapture.get(toolName) ?? null,
1797
- approved: true
1798
- });
1799
- }
1800
- }
1801
- }
1802
- }
1803
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1804
- for (const tc of toolCalls) {
1805
- if (tc.result === null) {
1806
- const captured = toolResultCapture.get(tc.toolName);
1807
- if (captured !== void 0) tc.result = captured;
1808
- }
1809
- }
1810
- }
1999
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
2000
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1811
2001
  if (msg.type === "result" && msg.subtype === "success") {
1812
2002
  const r = msg;
1813
2003
  output = r.result;
@@ -1839,23 +2029,17 @@ var init_claude = __esm({
1839
2029
  );
1840
2030
  }
1841
2031
  }
1842
- } catch (retryError) {
1843
- if (this.isPersistent) this.clearPersistentSession();
1844
- if (signal.aborted) throw new AbortError();
1845
- throw retryError;
1846
- } finally {
1847
- this.activeQuery = null;
1848
- }
1849
- return {
1850
- output,
1851
- structuredOutput,
1852
- toolCalls,
1853
- messages: [
1854
- ...messages,
1855
- ...output !== null ? [{ role: "assistant", content: output }] : []
1856
- ],
1857
- usage
1858
- };
2032
+ return {
2033
+ output,
2034
+ structuredOutput,
2035
+ toolCalls,
2036
+ messages: [
2037
+ ...messages,
2038
+ ...output !== null ? [{ role: "assistant", content: output }] : []
2039
+ ],
2040
+ usage
2041
+ };
2042
+ });
1859
2043
  }
1860
2044
  if (this.isPersistent) this.clearPersistentSession();
1861
2045
  throw e;
@@ -1874,12 +2058,12 @@ var init_claude = __esm({
1874
2058
  };
1875
2059
  }
1876
2060
  // ─── executeStream ──────────────────────────────────────────────
1877
- async *executeStream(messages, _options, signal) {
2061
+ async *executeStream(messages, options, signal) {
1878
2062
  this.checkAbort(signal);
1879
2063
  const sdk = await loadSDK2();
1880
2064
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1881
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1882
- let opts = this.buildQueryOptions(signal);
2065
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
2066
+ let opts = this.buildQueryOptions(signal, options);
1883
2067
  const toolResultCapture = /* @__PURE__ */ new Map();
1884
2068
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1885
2069
  const q = sdk.query({ prompt, options: opts });
@@ -1887,6 +2071,7 @@ var init_claude = __esm({
1887
2071
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
1888
2072
  const toolCallTracker = new ClaudeToolCallTracker();
1889
2073
  const pendingStreamToolCalls = /* @__PURE__ */ new Map();
2074
+ let hasStreamedText = false;
1890
2075
  try {
1891
2076
  for await (const msg of q) {
1892
2077
  if (signal.aborted) throw new AbortError();
@@ -1904,6 +2089,7 @@ var init_claude = __esm({
1904
2089
  } else if (e.type === "tool_call_end") {
1905
2090
  pendingStreamToolCalls.delete(e.toolCallId);
1906
2091
  }
2092
+ if (e.type === "text_delta") hasStreamedText = true;
1907
2093
  yield e;
1908
2094
  }
1909
2095
  }
@@ -1927,22 +2113,21 @@ var init_claude = __esm({
1927
2113
  }
1928
2114
  yield this.emitSessionInfo(r.session_id);
1929
2115
  }
1930
- yield { type: "done", finalOutput: r.result };
2116
+ yield {
2117
+ type: "done",
2118
+ finalOutput: hasStreamedText ? null : r.result,
2119
+ ...hasStreamedText ? { streamed: true } : {}
2120
+ };
1931
2121
  }
1932
2122
  }
1933
2123
  } catch (e) {
1934
2124
  if (signal.aborted) throw new AbortError();
1935
2125
  if (isResuming && this.isPersistent) {
1936
- this.clearPersistentSession();
1937
- const retryPrompt = buildContextualPrompt2(messages);
1938
- let retryOpts = this.buildQueryOptions(signal);
1939
- toolResultCapture.clear();
1940
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1941
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1942
- this.activeQuery = retryQ;
2126
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1943
2127
  const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
1944
2128
  const retryToolCallTracker = new ClaudeToolCallTracker();
1945
2129
  const retryPendingToolCalls = /* @__PURE__ */ new Map();
2130
+ let retryHasStreamedText = false;
1946
2131
  try {
1947
2132
  for await (const msg of retryQ) {
1948
2133
  if (signal.aborted) throw new AbortError();
@@ -1960,6 +2145,7 @@ var init_claude = __esm({
1960
2145
  } else if (ev.type === "tool_call_end") {
1961
2146
  retryPendingToolCalls.delete(ev.toolCallId);
1962
2147
  }
2148
+ if (ev.type === "text_delta") retryHasStreamedText = true;
1963
2149
  yield ev;
1964
2150
  }
1965
2151
  }
@@ -1983,7 +2169,11 @@ var init_claude = __esm({
1983
2169
  }
1984
2170
  yield this.emitSessionInfo(r.session_id);
1985
2171
  }
1986
- yield { type: "done", finalOutput: r.result };
2172
+ yield {
2173
+ type: "done",
2174
+ finalOutput: retryHasStreamedText ? null : r.result,
2175
+ ...retryHasStreamedText ? { streamed: true } : {}
2176
+ };
1987
2177
  }
1988
2178
  }
1989
2179
  } catch (retryError) {
@@ -2045,7 +2235,8 @@ var init_claude = __esm({
2045
2235
  this.cachedModels = body.data.map((m) => ({
2046
2236
  id: m.id,
2047
2237
  name: m.display_name,
2048
- provider: "claude"
2238
+ provider: "claude",
2239
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
2049
2240
  }));
2050
2241
  return this.cachedModels;
2051
2242
  }
@@ -2103,32 +2294,30 @@ __export(vercel_ai_exports, {
2103
2294
  createVercelAIService: () => createVercelAIService
2104
2295
  });
2105
2296
  async function loadSDK3() {
2106
- if (sdkModule3) return sdkModule3;
2297
+ if (_sdkMock3) return _sdkMock3;
2107
2298
  try {
2108
- sdkModule3 = await import('ai');
2109
- return sdkModule3;
2299
+ return await import('ai');
2110
2300
  } catch {
2111
2301
  throw new DependencyError("ai");
2112
2302
  }
2113
2303
  }
2114
2304
  async function loadCompat() {
2115
- if (compatModule) return compatModule;
2305
+ if (_compatMock) return _compatMock;
2116
2306
  try {
2117
- compatModule = await import('@ai-sdk/openai-compatible');
2118
- return compatModule;
2307
+ return await import('@ai-sdk/openai-compatible');
2119
2308
  } catch {
2120
2309
  throw new DependencyError("@ai-sdk/openai-compatible");
2121
2310
  }
2122
2311
  }
2123
2312
  function _injectSDK3(mock) {
2124
- sdkModule3 = mock;
2313
+ _sdkMock3 = mock;
2125
2314
  }
2126
2315
  function _injectCompat(mock) {
2127
- compatModule = mock;
2316
+ _compatMock = mock;
2128
2317
  }
2129
2318
  function _resetSDK3() {
2130
- sdkModule3 = null;
2131
- compatModule = null;
2319
+ _sdkMock3 = null;
2320
+ _compatMock = null;
2132
2321
  }
2133
2322
  function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, signal) {
2134
2323
  const toolMap = {};
@@ -2171,13 +2360,14 @@ function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, s
2171
2360
  return toolMap;
2172
2361
  }
2173
2362
  function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
2174
- return async (args) => {
2363
+ return async (args, options) => {
2175
2364
  if (ourTool.needsApproval && supervisor?.onPermission) {
2176
2365
  const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
2177
2366
  if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
2178
2367
  const request = {
2179
2368
  toolName: ourTool.name,
2180
- toolArgs: args ?? {}
2369
+ toolArgs: args ?? {},
2370
+ toolCallId: options?.toolCallId
2181
2371
  };
2182
2372
  const decision = await supervisor.onPermission(
2183
2373
  request,
@@ -2284,7 +2474,8 @@ function mapStreamPart(part) {
2284
2474
  return {
2285
2475
  type: "error",
2286
2476
  error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
2287
- recoverable: true
2477
+ recoverable: true,
2478
+ code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
2288
2479
  };
2289
2480
  }
2290
2481
  case "reasoning-start":
@@ -2305,10 +2496,13 @@ function mapStreamPart(part) {
2305
2496
  }
2306
2497
  case "error": {
2307
2498
  const p = part;
2499
+ const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
2500
+ const code = classifyAgentError(errorMsg);
2308
2501
  return {
2309
2502
  type: "error",
2310
- error: p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error"),
2311
- recoverable: false
2503
+ error: errorMsg,
2504
+ recoverable: isRecoverableErrorCode(code),
2505
+ code
2312
2506
  };
2313
2507
  }
2314
2508
  default:
@@ -2318,15 +2512,15 @@ function mapStreamPart(part) {
2318
2512
  function createVercelAIService(options) {
2319
2513
  return new VercelAIAgentService(options);
2320
2514
  }
2321
- var sdkModule3, compatModule, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2515
+ var _sdkMock3, _compatMock, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2322
2516
  var init_vercel_ai = __esm({
2323
2517
  "src/backends/vercel-ai.ts"() {
2324
- init_types();
2518
+ init_types2();
2325
2519
  init_base_agent();
2326
- init_errors();
2520
+ init_errors2();
2327
2521
  init_schema();
2328
- sdkModule3 = null;
2329
- compatModule = null;
2522
+ _sdkMock3 = null;
2523
+ _compatMock = null;
2330
2524
  DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2331
2525
  DEFAULT_PROVIDER = "openrouter";
2332
2526
  DEFAULT_MAX_TURNS = 10;
@@ -2339,28 +2533,33 @@ var init_vercel_ai = __esm({
2339
2533
  super(config);
2340
2534
  this.backendOptions = backendOptions;
2341
2535
  }
2342
- async getModel() {
2343
- if (this.model) return this.model;
2536
+ async getModel(options) {
2537
+ const requestedModel = options.model;
2538
+ const defaultModel = this.config.model;
2539
+ if (requestedModel === defaultModel && this.model) return this.model;
2344
2540
  const compat = await loadCompat();
2345
2541
  const provider = compat.createOpenAICompatible({
2346
2542
  name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
2347
2543
  baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
2348
2544
  apiKey: this.backendOptions.apiKey
2349
2545
  });
2350
- const modelId = this.config.model ?? "anthropic/claude-sonnet-4-5";
2351
- this.model = provider.chatModel(modelId);
2352
- return this.model;
2546
+ const model = provider.chatModel(requestedModel);
2547
+ if (requestedModel === defaultModel) {
2548
+ this.model = model;
2549
+ }
2550
+ return model;
2353
2551
  }
2354
- async getSDKTools(signal) {
2552
+ async getSDKTools(signal, options) {
2355
2553
  const sdk = await loadSDK3();
2356
- return mapToolsToSDK2(sdk, this.config.tools ?? [], this.config, this.sessionApprovals, this.config.permissionStore, signal);
2554
+ const tools = this.resolveTools(options);
2555
+ return mapToolsToSDK2(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
2357
2556
  }
2358
2557
  // ─── executeRun ─────────────────────────────────────────────────
2359
- async executeRun(messages, _options, signal) {
2558
+ async executeRun(messages, options, signal) {
2360
2559
  this.checkAbort(signal);
2361
2560
  const sdk = await loadSDK3();
2362
- const model = await this.getModel();
2363
- const tools = await this.getSDKTools(signal);
2561
+ const model = await this.getModel(options);
2562
+ const tools = await this.getSDKTools(signal, options);
2364
2563
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2365
2564
  const sdkMessages = messagesToSDK(messages);
2366
2565
  const hasTools = Object.keys(tools).length > 0;
@@ -2416,10 +2615,10 @@ var init_vercel_ai = __esm({
2416
2615
  };
2417
2616
  }
2418
2617
  // ─── executeRunStructured ───────────────────────────────────────
2419
- async executeRunStructured(messages, schema, _options, signal) {
2618
+ async executeRunStructured(messages, schema, options, signal) {
2420
2619
  this.checkAbort(signal);
2421
2620
  const sdk = await loadSDK3();
2422
- const model = await this.getModel();
2621
+ const model = await this.getModel(options);
2423
2622
  const sdkMessages = messagesToSDK(messages);
2424
2623
  const jsonSchema = zodToJsonSchema(schema.schema);
2425
2624
  const result = await sdk.generateObject({
@@ -2461,11 +2660,11 @@ var init_vercel_ai = __esm({
2461
2660
  };
2462
2661
  }
2463
2662
  // ─── executeStream ──────────────────────────────────────────────
2464
- async *executeStream(messages, _options, signal) {
2663
+ async *executeStream(messages, options, signal) {
2465
2664
  this.checkAbort(signal);
2466
2665
  const sdk = await loadSDK3();
2467
- const model = await this.getModel();
2468
- const tools = await this.getSDKTools(signal);
2666
+ const model = await this.getModel(options);
2667
+ const tools = await this.getSDKTools(signal, options);
2469
2668
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2470
2669
  const sdkMessages = messagesToSDK(messages);
2471
2670
  const hasTools = Object.keys(tools).length > 0;
@@ -2511,9 +2710,11 @@ var init_vercel_ai = __esm({
2511
2710
  promptTokens: Number(totalUsage?.inputTokens ?? 0),
2512
2711
  completionTokens: Number(totalUsage?.outputTokens ?? 0)
2513
2712
  };
2713
+ const hasStreamed = finalText.length > 0;
2514
2714
  yield {
2515
2715
  type: "done",
2516
- finalOutput: finalText || null
2716
+ finalOutput: hasStreamed ? null : finalText || null,
2717
+ ...hasStreamed ? { streamed: true } : {}
2517
2718
  };
2518
2719
  } catch (e) {
2519
2720
  if (signal.aborted) throw new AbortError();
@@ -2542,16 +2743,33 @@ var init_vercel_ai = __esm({
2542
2743
  const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
2543
2744
  try {
2544
2745
  const res = await globalThis.fetch(`${baseUrl}/models`, {
2545
- headers: { Authorization: `Bearer ${this.options.apiKey}` }
2746
+ headers: {
2747
+ Authorization: `Bearer ${this.options.apiKey}`,
2748
+ // OpenRouter requires HTTP-Referer for API access
2749
+ "HTTP-Referer": "https://github.com/nicepkg/agent-sdk"
2750
+ }
2546
2751
  });
2547
2752
  if (!res.ok) {
2548
2753
  return [];
2549
2754
  }
2550
2755
  const body = await res.json();
2551
- if (!body.data || body.data.length === 0) {
2552
- return [];
2756
+ if (body.data && Array.isArray(body.data)) {
2757
+ return body.data.filter((m) => typeof m.id === "string").map((m) => ({
2758
+ id: m.id,
2759
+ ...typeof m.name === "string" && { name: m.name },
2760
+ ...typeof m.description === "string" && { description: m.description },
2761
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
2762
+ }));
2763
+ }
2764
+ if (Array.isArray(body)) {
2765
+ return body.filter((m) => typeof m.id === "string").map((m) => ({
2766
+ id: m.id,
2767
+ ...typeof m.name === "string" && { name: m.name },
2768
+ ...typeof m.description === "string" && { description: m.description },
2769
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
2770
+ }));
2553
2771
  }
2554
- return body.data.map((m) => ({ id: m.id }));
2772
+ return [];
2555
2773
  } catch {
2556
2774
  return [];
2557
2775
  }
@@ -2582,272 +2800,25 @@ var init_vercel_ai = __esm({
2582
2800
  }
2583
2801
  });
2584
2802
 
2585
- // src/registry.ts
2586
- function registerBackend(name, factory) {
2587
- if (registry.has(name)) {
2588
- throw new BackendAlreadyRegisteredError(name);
2589
- }
2590
- registry.set(name, { factory, builtin: false });
2591
- }
2592
- function unregisterBackend(name) {
2593
- return registry.delete(name);
2594
- }
2595
- function hasBackend(name) {
2596
- return registry.has(name) || isBuiltinName(name);
2597
- }
2598
- function listBackends() {
2599
- const names = new Set(registry.keys());
2600
- for (const builtin of BUILTIN_BACKENDS) {
2601
- names.add(builtin);
2602
- }
2603
- return [...names];
2604
- }
2605
- function resetRegistry() {
2606
- registry.clear();
2607
- }
2608
- function isBuiltinName(name) {
2609
- return BUILTIN_BACKENDS.has(name);
2610
- }
2611
- async function loadBuiltinFactory(name) {
2612
- switch (name) {
2613
- case "copilot": {
2614
- const mod = await Promise.resolve().then(() => (init_copilot(), copilot_exports));
2615
- return (opts) => mod.createCopilotService(opts);
2616
- }
2617
- case "claude": {
2618
- const mod = await Promise.resolve().then(() => (init_claude(), claude_exports));
2619
- return (opts) => mod.createClaudeService(opts);
2620
- }
2621
- case "vercel-ai": {
2622
- const mod = await Promise.resolve().then(() => (init_vercel_ai(), vercel_ai_exports));
2623
- return (opts) => mod.createVercelAIService(opts);
2624
- }
2625
- }
2626
- }
2627
- async function createAgentService(name, options) {
2628
- const entry = registry.get(name);
2629
- if (entry) {
2630
- return entry.factory(options);
2631
- }
2632
- if (isBuiltinName(name)) {
2633
- const factory = await loadBuiltinFactory(name);
2634
- registry.set(name, { factory, builtin: true });
2635
- return factory(options);
2636
- }
2637
- throw new BackendNotFoundError(name);
2638
- }
2639
- var registry, BUILTIN_BACKENDS;
2640
- var init_registry = __esm({
2641
- "src/registry.ts"() {
2642
- init_errors();
2643
- registry = /* @__PURE__ */ new Map();
2644
- BUILTIN_BACKENDS = /* @__PURE__ */ new Set([
2645
- "copilot",
2646
- "claude",
2647
- "vercel-ai"
2648
- ]);
2649
- }
2650
- });
2651
-
2652
- // src/utils/messages.ts
2653
- function messagesToPrompt(messages) {
2654
- return messages.map((msg) => {
2655
- switch (msg.role) {
2656
- case "user":
2657
- return contentToText(msg.content);
2658
- case "assistant":
2659
- return contentToText(msg.content);
2660
- case "system":
2661
- return msg.content;
2662
- case "tool":
2663
- return msg.content ?? "";
2664
- }
2665
- }).filter(Boolean).join("\n\n");
2666
- }
2667
- function contentToText(content) {
2668
- return getTextContent(content);
2669
- }
2670
- function buildSystemPrompt(base, schemaInstruction) {
2671
- if (!schemaInstruction) return base;
2672
- return `${base}
2673
-
2674
- ${schemaInstruction}`;
2675
- }
2676
- var init_messages = __esm({
2677
- "src/utils/messages.ts"() {
2678
- init_types();
2679
- }
2680
- });
2681
- function createDefaultPermissionStore(projectDir) {
2682
- const sessionStore = new InMemoryPermissionStore();
2683
- const projectPath = projectDir ? path.join(projectDir, ".agent-sdk", "permissions.json") : path.join(process.cwd(), ".agent-sdk", "permissions.json");
2684
- const userPath = path.join(os.homedir(), ".agent-sdk", "permissions.json");
2685
- const projectStore = new FilePermissionStore(projectPath);
2686
- const userStore = new FilePermissionStore(userPath);
2687
- return new CompositePermissionStore(sessionStore, projectStore, userStore);
2803
+ // src/chat/backends/types.ts
2804
+ function isResumableBackend(adapter) {
2805
+ return "canResume" in adapter && typeof adapter.canResume === "function";
2688
2806
  }
2689
- var InMemoryPermissionStore, FilePermissionStore, CompositePermissionStore;
2690
- var init_permission_store = __esm({
2691
- "src/permission-store.ts"() {
2692
- InMemoryPermissionStore = class {
2693
- approvals = /* @__PURE__ */ new Map();
2694
- async isApproved(toolName) {
2695
- return this.approvals.has(toolName);
2696
- }
2697
- async approve(toolName, scope) {
2698
- if (scope === "once") return;
2699
- this.approvals.set(toolName, scope);
2700
- }
2701
- async revoke(toolName) {
2702
- this.approvals.delete(toolName);
2703
- }
2704
- async clear() {
2705
- this.approvals.clear();
2706
- }
2707
- async dispose() {
2708
- this.approvals.clear();
2709
- }
2710
- };
2711
- FilePermissionStore = class {
2712
- filePath;
2713
- constructor(filePath) {
2714
- this.filePath = path.resolve(filePath);
2715
- }
2716
- async isApproved(toolName) {
2717
- const data = this.readFile();
2718
- return toolName in data.approvals;
2719
- }
2720
- async approve(toolName, scope) {
2721
- if (scope === "once") return;
2722
- const data = this.readFile();
2723
- data.approvals[toolName] = { scope, timestamp: Date.now() };
2724
- this.writeFileAtomic(data);
2725
- }
2726
- async revoke(toolName) {
2727
- const data = this.readFile();
2728
- delete data.approvals[toolName];
2729
- this.writeFileAtomic(data);
2730
- }
2731
- async clear() {
2732
- this.writeFileAtomic({ approvals: {} });
2733
- }
2734
- async dispose() {
2735
- }
2736
- readFile() {
2737
- try {
2738
- const raw = fs.readFileSync(this.filePath, "utf-8");
2739
- const parsed = JSON.parse(raw);
2740
- if (parsed && typeof parsed.approvals === "object") return parsed;
2741
- } catch {
2742
- }
2743
- return { approvals: {} };
2744
- }
2745
- writeFileAtomic(data) {
2746
- const dir = path.dirname(this.filePath);
2747
- fs.mkdirSync(dir, { recursive: true });
2748
- const tmpPath = this.filePath + `.tmp.${process.pid}.${Date.now()}`;
2749
- fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
2750
- fs.renameSync(tmpPath, this.filePath);
2751
- }
2752
- };
2753
- CompositePermissionStore = class {
2754
- sessionStore;
2755
- projectStore;
2756
- userStore;
2757
- constructor(sessionStore, projectStore, userStore) {
2758
- this.sessionStore = sessionStore;
2759
- this.projectStore = projectStore;
2760
- this.userStore = userStore ?? projectStore;
2761
- }
2762
- async isApproved(toolName) {
2763
- return await this.sessionStore.isApproved(toolName) || await this.projectStore.isApproved(toolName) || await this.userStore.isApproved(toolName);
2764
- }
2765
- async approve(toolName, scope) {
2766
- if (scope === "once") return;
2767
- if (scope === "session") {
2768
- await this.sessionStore.approve(toolName, scope);
2769
- } else if (scope === "project") {
2770
- await this.projectStore.approve(toolName, scope);
2771
- } else {
2772
- await this.userStore.approve(toolName, scope);
2773
- }
2774
- }
2775
- async revoke(toolName) {
2776
- await this.sessionStore.revoke(toolName);
2777
- await this.projectStore.revoke(toolName);
2778
- await this.userStore.revoke(toolName);
2779
- }
2780
- async clear() {
2781
- await this.sessionStore.clear();
2782
- await this.projectStore.clear();
2783
- await this.userStore.clear();
2784
- }
2785
- async dispose() {
2786
- await this.sessionStore.dispose();
2787
- await this.projectStore.dispose();
2788
- if (this.userStore !== this.projectStore) {
2789
- await this.userStore.dispose();
2790
- }
2791
- }
2792
- };
2793
- }
2794
- });
2795
-
2796
- // src/index.ts
2797
- var src_exports = {};
2798
- __export(src_exports, {
2799
- AbortError: () => AbortError,
2800
- AgentSDKError: () => AgentSDKError,
2801
- BackendAlreadyRegisteredError: () => BackendAlreadyRegisteredError,
2802
- BackendNotFoundError: () => BackendNotFoundError,
2803
- BaseAgent: () => BaseAgent,
2804
- CompositePermissionStore: () => CompositePermissionStore,
2805
- DependencyError: () => DependencyError,
2806
- DisposedError: () => DisposedError,
2807
- FilePermissionStore: () => FilePermissionStore,
2808
- InMemoryPermissionStore: () => InMemoryPermissionStore,
2809
- ReentrancyError: () => ReentrancyError,
2810
- StructuredOutputError: () => StructuredOutputError,
2811
- SubprocessError: () => SubprocessError,
2812
- ToolExecutionError: () => ToolExecutionError,
2813
- buildSystemPrompt: () => buildSystemPrompt,
2814
- contentToText: () => contentToText,
2815
- createAgentService: () => createAgentService,
2816
- createDefaultPermissionStore: () => createDefaultPermissionStore,
2817
- getTextContent: () => getTextContent,
2818
- hasBackend: () => hasBackend,
2819
- isMultiPartContent: () => isMultiPartContent,
2820
- isTextContent: () => isTextContent,
2821
- isToolDefinition: () => isToolDefinition,
2822
- listBackends: () => listBackends,
2823
- messagesToPrompt: () => messagesToPrompt,
2824
- registerBackend: () => registerBackend,
2825
- resetRegistry: () => resetRegistry,
2826
- unregisterBackend: () => unregisterBackend,
2827
- zodToJsonSchema: () => zodToJsonSchema
2828
- });
2829
- var init_src = __esm({
2830
- "src/index.ts"() {
2831
- init_types();
2832
- init_errors();
2833
- init_registry();
2834
- init_base_agent();
2835
- init_schema();
2836
- init_messages();
2837
- init_permission_store();
2838
- }
2839
- });
2840
2807
 
2841
- // src/chat/core.ts
2808
+ // src/chat/types.ts
2842
2809
  function createChatId() {
2843
2810
  return crypto.randomUUID();
2844
2811
  }
2812
+
2813
+ // src/chat/chat-utils.ts
2845
2814
  function getMessageText(message) {
2846
2815
  return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
2847
2816
  }
2848
2817
  function getMessageToolCalls(message) {
2849
2818
  return message.parts.filter((p) => p.type === "tool_call");
2850
2819
  }
2820
+
2821
+ // src/chat/bridge.ts
2851
2822
  function agentEventToChatEvent(event, messageId) {
2852
2823
  switch (event.type) {
2853
2824
  case "text_delta":
@@ -2900,6 +2871,7 @@ function agentEventToChatEvent(event, messageId) {
2900
2871
  type: "error",
2901
2872
  error: event.error,
2902
2873
  recoverable: event.recoverable,
2874
+ code: event.code,
2903
2875
  messageId
2904
2876
  };
2905
2877
  case "heartbeat":
@@ -2921,6 +2893,8 @@ async function* adaptAgentEvents(events, messageId) {
2921
2893
  }
2922
2894
  }
2923
2895
  }
2896
+
2897
+ // src/chat/conversion.ts
2924
2898
  function toAgentMessage(message) {
2925
2899
  const textContent = getMessageText(message);
2926
2900
  const toolCallParts = getMessageToolCalls(message);
@@ -2941,6 +2915,7 @@ function toAgentMessage(message) {
2941
2915
  }
2942
2916
 
2943
2917
  // src/chat/errors.ts
2918
+ init_errors2();
2944
2919
  init_errors();
2945
2920
  var ChatError = class extends AgentSDKError {
2946
2921
  code;
@@ -2948,7 +2923,11 @@ var ChatError = class extends AgentSDKError {
2948
2923
  retryAfter;
2949
2924
  timestamp;
2950
2925
  constructor(message, options) {
2951
- super(message, { cause: options.cause });
2926
+ super(message, {
2927
+ cause: options.cause,
2928
+ code: options.code,
2929
+ retryable: options.retryable
2930
+ });
2952
2931
  this.name = "ChatError";
2953
2932
  this.code = options.code;
2954
2933
  this.retryable = options.retryable ?? false;
@@ -2960,25 +2939,51 @@ var ChatError = class extends AgentSDKError {
2960
2939
  // src/chat/backends/base.ts
2961
2940
  var BaseBackendAdapter = class {
2962
2941
  name;
2963
- _agentService;
2964
- _agent = null;
2942
+ _agentService = null;
2943
+ _agentServiceFactory = null;
2965
2944
  _disposed = false;
2966
2945
  _agentConfig;
2967
2946
  _ownsService;
2947
+ // Agent lifecycle: tracks current agent and the model it was created with.
2948
+ // For persistent sessions, reused across calls when model matches.
2949
+ // For non-persistent, recreated every call.
2950
+ _currentAgent = null;
2968
2951
  constructor(name, options) {
2969
2952
  this.name = name;
2970
2953
  this._agentConfig = options.agentConfig;
2971
2954
  if (options.agentService) {
2972
2955
  this._agentService = options.agentService;
2973
2956
  this._ownsService = false;
2957
+ } else if (options.agentServiceFactory) {
2958
+ this._agentServiceFactory = options.agentServiceFactory;
2959
+ this._ownsService = true;
2974
2960
  } else {
2975
2961
  this._agentService = this.createService();
2976
2962
  this._ownsService = true;
2977
2963
  }
2978
2964
  }
2979
2965
  get agentService() {
2966
+ if (!this._agentService) {
2967
+ if (this._agentServiceFactory) {
2968
+ this._agentService = this._agentServiceFactory();
2969
+ this._agentServiceFactory = null;
2970
+ } else {
2971
+ throw new ChatError("Agent service not available", {
2972
+ code: "BACKEND_NOT_INSTALLED" /* BACKEND_NOT_INSTALLED */
2973
+ });
2974
+ }
2975
+ }
2980
2976
  return this._agentService;
2981
2977
  }
2978
+ get currentModel() {
2979
+ return this._agentConfig.model;
2980
+ }
2981
+ /**
2982
+ * @deprecated No-op. Tools are passed per-call via SendMessageOptions.tools.
2983
+ * Kept for backward compatibility with code that calls setTools() directly.
2984
+ */
2985
+ setTools() {
2986
+ }
2982
2987
  async sendMessage(session, message, options) {
2983
2988
  this.assertNotDisposed();
2984
2989
  const events = this.streamMessage(session, message, options);
@@ -3016,9 +3021,13 @@ var BaseBackendAdapter = class {
3016
3021
  */
3017
3022
  async *streamAgentEvents(agent, messages, options) {
3018
3023
  const messageId = createChatId();
3024
+ const model = options?.model ?? this._agentConfig.model ?? "";
3019
3025
  const agentEvents = agent.streamWithContext(messages, {
3026
+ model,
3020
3027
  signal: options?.signal,
3021
- context: options?.context
3028
+ context: options?.context,
3029
+ tools: options?.tools,
3030
+ ...options?.systemPrompt ? { systemMessage: options.systemPrompt } : {}
3022
3031
  });
3023
3032
  yield { type: "message:start", messageId, role: "assistant" };
3024
3033
  let text = "";
@@ -3044,31 +3053,45 @@ var BaseBackendAdapter = class {
3044
3053
  }
3045
3054
  async listModels() {
3046
3055
  this.assertNotDisposed();
3047
- return this._agentService.listModels();
3056
+ return this.agentService.listModels();
3048
3057
  }
3049
3058
  async validate() {
3050
3059
  this.assertNotDisposed();
3051
- return this._agentService.validate();
3060
+ return this.agentService.validate();
3052
3061
  }
3053
3062
  async dispose() {
3054
3063
  if (this._disposed) return;
3055
3064
  this._disposed = true;
3056
- this._agent?.dispose();
3057
- this._agent = null;
3058
- if (this._ownsService) {
3065
+ if (this._currentAgent) {
3066
+ this._currentAgent.instance.dispose();
3067
+ this._currentAgent = null;
3068
+ }
3069
+ if (this._ownsService && this._agentService && typeof this._agentService.dispose === "function") {
3059
3070
  await this._agentService.dispose();
3060
3071
  }
3061
3072
  }
3062
- /** Get or create an agent, applying model override from options */
3073
+ /** Get or create an agent. Model is passed per-call via RunOptions.
3074
+ * Tools are passed per-call via SendMessageOptions — not baked into config.
3075
+ * For persistent sessions, reuses agent when model matches. */
3063
3076
  getOrCreateAgent(options) {
3064
- const config = options?.model ? { ...this._agentConfig, model: options.model } : this._agentConfig;
3065
- if (this._agentConfig.sessionMode === "persistent" && this._agent) {
3066
- return this._agent;
3077
+ const model = options?.model ?? this._agentConfig.model;
3078
+ if (this._agentConfig.sessionMode === "persistent" && this._currentAgent) {
3079
+ if (this._currentAgent.model === model) {
3080
+ return this._currentAgent.instance;
3081
+ }
3082
+ this._currentAgent.instance.dispose();
3083
+ this._currentAgent = null;
3067
3084
  }
3068
- const agent = this._agentService.createAgent(config);
3069
- if (this._agentConfig.sessionMode === "persistent") {
3070
- this._agent = agent;
3085
+ if (this._currentAgent) {
3086
+ this._currentAgent.instance.dispose();
3087
+ this._currentAgent = null;
3071
3088
  }
3089
+ const config = {
3090
+ ...this._agentConfig,
3091
+ ...model !== void 0 && { model }
3092
+ };
3093
+ const agent = this.agentService.createAgent(config);
3094
+ this._currentAgent = { instance: agent, model };
3072
3095
  return agent;
3073
3096
  }
3074
3097
  assertNotDisposed() {
@@ -3093,8 +3116,8 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
3093
3116
  this._copilotOptions = options.copilotOptions;
3094
3117
  }
3095
3118
  createService() {
3096
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3097
- return createAgentService2("copilot", this._copilotOptions);
3119
+ const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
3120
+ return createCopilotService2(this._copilotOptions || {});
3098
3121
  }
3099
3122
  get backendSessionId() {
3100
3123
  return this._backendSessionId;
@@ -3146,8 +3169,8 @@ var ClaudeChatAdapter = class extends BaseBackendAdapter {
3146
3169
  this._claudeOptions = options.claudeOptions;
3147
3170
  }
3148
3171
  createService() {
3149
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3150
- return createAgentService2("claude", this._claudeOptions);
3172
+ const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
3173
+ return createClaudeService2(this._claudeOptions || {});
3151
3174
  }
3152
3175
  get backendSessionId() {
3153
3176
  return this._backendSessionId;
@@ -3194,20 +3217,8 @@ var VercelAIChatAdapter = class extends BaseBackendAdapter {
3194
3217
  this._vercelOptions = options.vercelOptions;
3195
3218
  }
3196
3219
  createService() {
3197
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3198
- return createAgentService2("vercel-ai", this._vercelOptions);
3199
- }
3200
- get backendSessionId() {
3201
- return null;
3202
- }
3203
- canResume() {
3204
- return false;
3205
- }
3206
- async *resume(_session, _backendSessionId, _options) {
3207
- throw new ChatError(
3208
- "Vercel AI adapter does not support session resume (stateless)",
3209
- { code: "PROVIDER_ERROR" /* PROVIDER_ERROR */ }
3210
- );
3220
+ const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
3221
+ return createVercelAIService2(this._vercelOptions || {});
3211
3222
  }
3212
3223
  captureSessionId(_agent) {
3213
3224
  }
@@ -3388,9 +3399,9 @@ var InProcessChatTransport = class {
3388
3399
  send(event) {
3389
3400
  if (!this._open) return;
3390
3401
  if (this._resolve) {
3391
- const resolve2 = this._resolve;
3402
+ const resolve = this._resolve;
3392
3403
  this._resolve = null;
3393
- resolve2({ value: event, done: false });
3404
+ resolve({ value: event, done: false });
3394
3405
  } else {
3395
3406
  this._buffer.push(event);
3396
3407
  }
@@ -3399,9 +3410,9 @@ var InProcessChatTransport = class {
3399
3410
  if (!this._open) return;
3400
3411
  this._open = false;
3401
3412
  if (this._resolve) {
3402
- const resolve2 = this._resolve;
3413
+ const resolve = this._resolve;
3403
3414
  this._resolve = null;
3404
- resolve2({ value: void 0, done: true });
3415
+ resolve({ value: void 0, done: true });
3405
3416
  }
3406
3417
  }
3407
3418
  error(err) {
@@ -3413,9 +3424,9 @@ var InProcessChatTransport = class {
3413
3424
  recoverable: false
3414
3425
  };
3415
3426
  if (this._resolve) {
3416
- const resolve2 = this._resolve;
3427
+ const resolve = this._resolve;
3417
3428
  this._resolve = null;
3418
- resolve2({ value: errorEvent, done: false });
3429
+ resolve({ value: errorEvent, done: false });
3419
3430
  } else {
3420
3431
  this._error = err;
3421
3432
  }
@@ -3440,8 +3451,8 @@ var InProcessChatTransport = class {
3440
3451
  if (!this._open) {
3441
3452
  return Promise.resolve({ value: void 0, done: true });
3442
3453
  }
3443
- return new Promise((resolve2) => {
3444
- this._resolve = resolve2;
3454
+ return new Promise((resolve) => {
3455
+ this._resolve = resolve;
3445
3456
  });
3446
3457
  }
3447
3458
  };
@@ -3496,6 +3507,6 @@ function withInterceptors(transport, interceptors) {
3496
3507
  return new InterceptedTransport(transport, interceptors);
3497
3508
  }
3498
3509
 
3499
- export { BaseBackendAdapter, ClaudeChatAdapter, CopilotChatAdapter, InProcessChatTransport, SSEChatTransport, VercelAIChatAdapter, WS_READY_STATE, WsChatTransport, streamToTransport, withInterceptors };
3510
+ export { BaseBackendAdapter, ClaudeChatAdapter, CopilotChatAdapter, InProcessChatTransport, SSEChatTransport, VercelAIChatAdapter, WS_READY_STATE, WsChatTransport, isResumableBackend, streamToTransport, withInterceptors };
3500
3511
  //# sourceMappingURL=backends.js.map
3501
3512
  //# sourceMappingURL=backends.js.map