@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,31 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var path = require('path');
5
- var os = require('os');
6
-
7
- function _interopNamespace(e) {
8
- if (e && e.__esModule) return e;
9
- var n = Object.create(null);
10
- if (e) {
11
- Object.keys(e).forEach(function (k) {
12
- if (k !== 'default') {
13
- var d = Object.getOwnPropertyDescriptor(e, k);
14
- Object.defineProperty(n, k, d.get ? d : {
15
- enumerable: true,
16
- get: function () { return e[k]; }
17
- });
18
- }
19
- });
20
- }
21
- n.default = e;
22
- return Object.freeze(n);
23
- }
24
-
25
- var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
26
- var path__namespace = /*#__PURE__*/_interopNamespace(path);
27
- var os__namespace = /*#__PURE__*/_interopNamespace(os);
28
-
29
3
  var __defProp = Object.defineProperty;
30
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
31
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -47,16 +21,69 @@ var __copyProps = (to, from, except, desc) => {
47
21
  };
48
22
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
49
23
 
50
- // src/errors.ts
51
- var AgentSDKError, ReentrancyError, DisposedError, BackendNotFoundError, BackendAlreadyRegisteredError, SubprocessError, DependencyError, AbortError, ToolExecutionError, StructuredOutputError;
24
+ // src/types/errors.ts
25
+ function isRecoverableErrorCode(code) {
26
+ return RECOVERABLE_CODES.has(code);
27
+ }
28
+ function classifyAgentError(error) {
29
+ const msg = (error instanceof Error ? error.message : error).toLowerCase();
30
+ if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
31
+ return "TIMEOUT" /* TIMEOUT */;
32
+ }
33
+ if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
34
+ return "RATE_LIMIT" /* RATE_LIMIT */;
35
+ }
36
+ if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
37
+ return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
38
+ }
39
+ if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
40
+ return "NETWORK" /* NETWORK */;
41
+ }
42
+ if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
43
+ return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
44
+ }
45
+ if (msg.includes("abort") || msg.includes("cancel")) {
46
+ return "ABORTED" /* ABORTED */;
47
+ }
48
+ 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")) {
49
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
50
+ }
51
+ return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
52
+ }
53
+ var RECOVERABLE_CODES;
52
54
  var init_errors = __esm({
55
+ "src/types/errors.ts"() {
56
+ RECOVERABLE_CODES = /* @__PURE__ */ new Set([
57
+ "TIMEOUT" /* TIMEOUT */,
58
+ "RATE_LIMIT" /* RATE_LIMIT */,
59
+ "NETWORK" /* NETWORK */,
60
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
61
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
62
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
63
+ ]);
64
+ }
65
+ });
66
+
67
+ // src/errors.ts
68
+ var AgentSDKError, ReentrancyError, DisposedError, SubprocessError, DependencyError, AbortError, ToolExecutionError, ActivityTimeoutError;
69
+ var init_errors2 = __esm({
53
70
  "src/errors.ts"() {
71
+ init_errors();
54
72
  AgentSDKError = class extends Error {
55
73
  /** @internal Marker for cross-bundle identity checks */
56
74
  _agentSDKError = true;
75
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
76
+ code;
77
+ /** Whether this error is safe to retry */
78
+ retryable;
79
+ /** HTTP status code hint for error classification */
80
+ httpStatus;
57
81
  constructor(message, options) {
58
82
  super(message, options);
59
83
  this.name = "AgentSDKError";
84
+ this.code = options?.code;
85
+ this.retryable = options?.retryable ?? false;
86
+ this.httpStatus = options?.httpStatus;
60
87
  }
61
88
  /** Check if an error is an AgentSDKError (works across bundled copies) */
62
89
  static is(error) {
@@ -65,83 +92,84 @@ var init_errors = __esm({
65
92
  };
66
93
  ReentrancyError = class extends AgentSDKError {
67
94
  constructor() {
68
- super("Agent is already running. Await the current run before starting another.");
95
+ super("Agent is already running. Await the current run before starting another.", {
96
+ code: "REENTRANCY" /* REENTRANCY */
97
+ });
69
98
  this.name = "ReentrancyError";
70
99
  }
71
100
  };
72
101
  DisposedError = class extends AgentSDKError {
73
102
  constructor(entity) {
74
- super(`${entity} has been disposed and cannot be used.`);
103
+ super(`${entity} has been disposed and cannot be used.`, {
104
+ code: "DISPOSED" /* DISPOSED */
105
+ });
75
106
  this.name = "DisposedError";
76
107
  }
77
108
  };
78
- BackendNotFoundError = class extends AgentSDKError {
79
- constructor(backend) {
80
- super(
81
- `Unknown backend: "${backend}". Built-in: copilot, claude, vercel-ai. Custom: use registerBackend() first.`
82
- );
83
- this.name = "BackendNotFoundError";
84
- }
85
- };
86
- BackendAlreadyRegisteredError = class extends AgentSDKError {
87
- constructor(backend) {
88
- super(`Backend "${backend}" is already registered. Use a different name or unregister first.`);
89
- this.name = "BackendAlreadyRegisteredError";
90
- }
91
- };
92
109
  SubprocessError = class extends AgentSDKError {
93
110
  constructor(message, options) {
94
- super(message, options);
111
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
95
112
  this.name = "SubprocessError";
96
113
  }
97
114
  };
98
115
  DependencyError = class extends AgentSDKError {
99
116
  packageName;
100
117
  constructor(packageName) {
101
- super(`${packageName} is not installed. Install it: npm install ${packageName}`);
118
+ super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
119
+ code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
120
+ });
102
121
  this.name = "DependencyError";
103
122
  this.packageName = packageName;
104
123
  }
105
124
  };
106
125
  AbortError = class extends AgentSDKError {
107
126
  constructor() {
108
- super("Agent run was aborted.");
127
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
109
128
  this.name = "AbortError";
110
129
  }
111
130
  };
112
131
  ToolExecutionError = class extends AgentSDKError {
113
132
  toolName;
114
133
  constructor(toolName, message, options) {
115
- super(`Tool "${toolName}" failed: ${message}`, options);
134
+ super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
116
135
  this.name = "ToolExecutionError";
117
136
  this.toolName = toolName;
118
137
  }
119
138
  };
120
- StructuredOutputError = class extends AgentSDKError {
121
- constructor(message, options) {
122
- super(`Structured output error: ${message}`, options);
123
- this.name = "StructuredOutputError";
139
+ ActivityTimeoutError = class extends AgentSDKError {
140
+ constructor(timeoutMs) {
141
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
142
+ code: "TIMEOUT" /* TIMEOUT */,
143
+ retryable: true
144
+ });
145
+ this.name = "ActivityTimeoutError";
124
146
  }
125
147
  };
126
148
  }
127
149
  });
128
150
 
129
- // src/types.ts
130
- function isToolDefinition(tool) {
131
- return "execute" in tool && typeof tool.execute === "function";
132
- }
133
- function isTextContent(content) {
134
- return typeof content === "string";
135
- }
136
- function isMultiPartContent(content) {
137
- return Array.isArray(content);
138
- }
151
+ // src/types/guards.ts
139
152
  function getTextContent(content) {
140
153
  if (typeof content === "string") return content;
141
154
  return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
142
155
  }
156
+ var init_guards = __esm({
157
+ "src/types/guards.ts"() {
158
+ }
159
+ });
160
+
161
+ // src/types/index.ts
143
162
  var init_types = __esm({
163
+ "src/types/index.ts"() {
164
+ init_errors();
165
+ init_guards();
166
+ }
167
+ });
168
+
169
+ // src/types.ts
170
+ var init_types2 = __esm({
144
171
  "src/types.ts"() {
172
+ init_types();
145
173
  }
146
174
  });
147
175
 
@@ -149,12 +177,15 @@ var init_types = __esm({
149
177
  var BaseAgent;
150
178
  var init_base_agent = __esm({
151
179
  "src/base-agent.ts"() {
180
+ init_errors2();
181
+ init_errors2();
152
182
  init_errors();
153
183
  BaseAgent = class {
154
184
  state = "idle";
155
185
  abortController = null;
156
186
  config;
157
187
  _cleanupExternalSignal = null;
188
+ _streamMiddleware = [];
158
189
  /** CLI session ID for persistent mode. Override in backends that support it. */
159
190
  get sessionId() {
160
191
  return void 0;
@@ -170,8 +201,11 @@ var init_base_agent = __esm({
170
201
  this.state = "running";
171
202
  try {
172
203
  const messages = [{ role: "user", content: prompt }];
173
- const result = await this.executeRun(messages, options, ac.signal);
174
- this.enrichAndNotifyUsage(result);
204
+ const result = await this.withRetry(
205
+ () => this.executeRun(messages, options, ac.signal),
206
+ options
207
+ );
208
+ this.enrichAndNotifyUsage(result, options);
175
209
  return result;
176
210
  } finally {
177
211
  this.cleanupRun();
@@ -183,8 +217,11 @@ var init_base_agent = __esm({
183
217
  const ac = this.createAbortController(options?.signal);
184
218
  this.state = "running";
185
219
  try {
186
- const result = await this.executeRun(messages, options, ac.signal);
187
- this.enrichAndNotifyUsage(result);
220
+ const result = await this.withRetry(
221
+ () => this.executeRun(messages, options, ac.signal),
222
+ options
223
+ );
224
+ this.enrichAndNotifyUsage(result, options);
188
225
  return result;
189
226
  } finally {
190
227
  this.cleanupRun();
@@ -197,13 +234,11 @@ var init_base_agent = __esm({
197
234
  this.state = "running";
198
235
  try {
199
236
  const messages = [{ role: "user", content: prompt }];
200
- const result = await this.executeRunStructured(
201
- messages,
202
- schema,
203
- options,
204
- ac.signal
237
+ const result = await this.withRetry(
238
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
239
+ options
205
240
  );
206
- this.enrichAndNotifyUsage(result);
241
+ this.enrichAndNotifyUsage(result, options);
207
242
  return result;
208
243
  } finally {
209
244
  this.cleanupRun();
@@ -216,8 +251,10 @@ var init_base_agent = __esm({
216
251
  this.state = "streaming";
217
252
  try {
218
253
  const messages = [{ role: "user", content: prompt }];
219
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
220
- yield* this.heartbeatStream(enriched);
254
+ yield* this.streamWithRetry(
255
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
256
+ options
257
+ );
221
258
  } finally {
222
259
  this.cleanupRun();
223
260
  }
@@ -228,12 +265,37 @@ var init_base_agent = __esm({
228
265
  const ac = this.createAbortController(options?.signal);
229
266
  this.state = "streaming";
230
267
  try {
231
- const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
232
- yield* this.heartbeatStream(enriched);
268
+ yield* this.streamWithRetry(
269
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
270
+ options
271
+ );
233
272
  } finally {
234
273
  this.cleanupRun();
235
274
  }
236
275
  }
276
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
277
+ addStreamMiddleware(middleware) {
278
+ this.guardDisposed();
279
+ this._streamMiddleware.push(middleware);
280
+ }
281
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
282
+ async *applyStreamPipeline(source, options, ac) {
283
+ let stream = this.enrichStream(source, options);
284
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
285
+ stream = this.heartbeatStream(stream);
286
+ if (this._streamMiddleware.length > 0) {
287
+ const ctx = {
288
+ model: options.model,
289
+ backend: this.backendName,
290
+ abortController: ac,
291
+ config: Object.freeze({ ...this.config })
292
+ };
293
+ for (const mw of this._streamMiddleware) {
294
+ stream = mw(stream, ctx);
295
+ }
296
+ }
297
+ yield* stream;
298
+ }
237
299
  abort() {
238
300
  if (this.abortController) {
239
301
  this.abortController.abort();
@@ -256,26 +318,109 @@ var init_base_agent = __esm({
256
318
  this.abort();
257
319
  this.state = "disposed";
258
320
  }
321
+ // ─── Retry Logic ─────────────────────────────────────────────
322
+ /** Check if an error should be retried given the retry configuration. */
323
+ isRetryableError(error, retry) {
324
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
325
+ return false;
326
+ }
327
+ if (AgentSDKError.is(error)) {
328
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
329
+ return retry.retryableErrors.includes(error.code);
330
+ }
331
+ if (error.retryable) return true;
332
+ if (error.code) return isRecoverableErrorCode(error.code);
333
+ }
334
+ return false;
335
+ }
336
+ /** Execute a function with retry logic per RetryConfig. */
337
+ async withRetry(fn, options) {
338
+ const retry = options?.retry;
339
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
340
+ return fn();
341
+ }
342
+ const maxRetries = retry.maxRetries;
343
+ const initialDelay = retry.initialDelayMs ?? 1e3;
344
+ const multiplier = retry.backoffMultiplier ?? 2;
345
+ let lastError;
346
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
347
+ try {
348
+ return await fn();
349
+ } catch (err) {
350
+ lastError = err;
351
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
352
+ throw err;
353
+ }
354
+ const delay = initialDelay * Math.pow(multiplier, attempt);
355
+ await new Promise((resolve) => setTimeout(resolve, delay));
356
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
357
+ throw err;
358
+ }
359
+ }
360
+ }
361
+ throw lastError;
362
+ }
363
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
364
+ async *streamWithRetry(factory, options) {
365
+ const retry = options?.retry;
366
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
367
+ yield* factory();
368
+ return;
369
+ }
370
+ const maxRetries = retry.maxRetries;
371
+ const initialDelay = retry.initialDelayMs ?? 1e3;
372
+ const multiplier = retry.backoffMultiplier ?? 2;
373
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
374
+ try {
375
+ const stream = factory();
376
+ const iterator = stream[Symbol.asyncIterator]();
377
+ const first = await iterator.next();
378
+ if (first.done) return;
379
+ yield first.value;
380
+ while (true) {
381
+ const next = await iterator.next();
382
+ if (next.done) break;
383
+ yield next.value;
384
+ }
385
+ return;
386
+ } catch (err) {
387
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
388
+ throw err;
389
+ }
390
+ const delay = initialDelay * Math.pow(multiplier, attempt);
391
+ await new Promise((resolve) => setTimeout(resolve, delay));
392
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
393
+ throw err;
394
+ }
395
+ }
396
+ }
397
+ }
398
+ // ─── CallOptions Resolution ──────────────────────────────────
399
+ /** Resolve tools to use for this call (per-call override > config default) */
400
+ resolveTools(options) {
401
+ return options?.tools ?? this.config.tools ?? [];
402
+ }
259
403
  // ─── Usage Enrichment ───────────────────────────────────────────
260
404
  /** Enrich result usage with model/backend and fire onUsage callback */
261
- enrichAndNotifyUsage(result) {
405
+ enrichAndNotifyUsage(result, options) {
262
406
  if (result.usage) {
263
407
  result.usage = {
264
408
  ...result.usage,
265
- model: this.config.model,
409
+ model: options.model,
266
410
  backend: this.backendName
267
411
  };
268
412
  this.callOnUsage(result.usage);
269
413
  }
270
414
  }
271
415
  /** Wrap a stream to enrich usage_update events and fire onUsage callback */
272
- async *enrichStream(source) {
416
+ async *enrichStream(source, options) {
417
+ const model = options.model;
273
418
  for await (const event of source) {
274
419
  if (event.type === "usage_update") {
275
420
  const usage = {
276
421
  promptTokens: event.promptTokens,
277
422
  completionTokens: event.completionTokens,
278
- model: this.config.model,
423
+ model,
279
424
  backend: this.backendName
280
425
  };
281
426
  this.callOnUsage(usage);
@@ -311,9 +456,9 @@ var init_base_agent = __esm({
311
456
  let heartbeatResolve = null;
312
457
  const timer = setInterval(() => {
313
458
  if (heartbeatResolve) {
314
- const resolve2 = heartbeatResolve;
459
+ const resolve = heartbeatResolve;
315
460
  heartbeatResolve = null;
316
- resolve2();
461
+ resolve();
317
462
  }
318
463
  }, interval);
319
464
  try {
@@ -321,8 +466,8 @@ var init_base_agent = __esm({
321
466
  if (!pendingEvent) {
322
467
  pendingEvent = iterator.next();
323
468
  }
324
- const heartbeatPromise = new Promise((resolve2) => {
325
- heartbeatResolve = resolve2;
469
+ const heartbeatPromise = new Promise((resolve) => {
470
+ heartbeatResolve = resolve;
326
471
  });
327
472
  const eventDone = pendingEvent.then(
328
473
  (r) => ({ kind: "event", result: r })
@@ -345,6 +490,35 @@ var init_base_agent = __esm({
345
490
  heartbeatResolve = null;
346
491
  }
347
492
  }
493
+ // ─── Activity Timeout ────────────────────────────────────────
494
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
495
+ * When timeoutMs is not set, passes through directly. */
496
+ async *activityTimeoutStream(source, timeoutMs, ac) {
497
+ if (!timeoutMs || timeoutMs <= 0) {
498
+ yield* source;
499
+ return;
500
+ }
501
+ const iterator = source[Symbol.asyncIterator]();
502
+ let timerId;
503
+ try {
504
+ while (true) {
505
+ const timeoutPromise = new Promise((_, reject) => {
506
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
507
+ });
508
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
509
+ clearTimeout(timerId);
510
+ if (result.done) break;
511
+ yield result.value;
512
+ }
513
+ } catch (err) {
514
+ if (err instanceof ActivityTimeoutError) {
515
+ ac.abort(err);
516
+ }
517
+ throw err;
518
+ } finally {
519
+ clearTimeout(timerId);
520
+ }
521
+ }
348
522
  // ─── Guards ───────────────────────────────────────────────────
349
523
  guardReentrancy() {
350
524
  if (this.state === "running" || this.state === "streaming") {
@@ -449,6 +623,66 @@ var init_schema = __esm({
449
623
  }
450
624
  });
451
625
 
626
+ // src/backends/shared.ts
627
+ function extractLastUserPrompt(messages) {
628
+ for (let i = messages.length - 1; i >= 0; i--) {
629
+ const msg = messages[i];
630
+ if (msg.role === "user") {
631
+ return getTextContent(msg.content);
632
+ }
633
+ }
634
+ return "";
635
+ }
636
+ function serializeToolCall(tc) {
637
+ const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
638
+ return ` Tool call: ${tc.name}(${args})`;
639
+ }
640
+ function serializeToolResult(tr) {
641
+ const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
642
+ const prefix = tr.isError ? "[ERROR] " : "";
643
+ return ` ${tr.name} \u2192 ${prefix}${result}`;
644
+ }
645
+ function buildContextualPrompt(messages) {
646
+ if (messages.length <= 1) {
647
+ return extractLastUserPrompt(messages);
648
+ }
649
+ const history = messages.slice(0, -1).map((msg) => {
650
+ if (msg.role === "user") {
651
+ return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
652
+ }
653
+ if (msg.role === "tool" && msg.toolResults) {
654
+ const results = msg.toolResults.map(serializeToolResult).join("\n");
655
+ return `Tool results:
656
+ ${results}`;
657
+ }
658
+ if (msg.role === "assistant") {
659
+ const parts = [];
660
+ const thinking = msg.thinking;
661
+ if (thinking) {
662
+ parts.push(`[reasoning: ${thinking}]`);
663
+ }
664
+ const text2 = msg.content ? getTextContent(msg.content) : "";
665
+ if (text2) parts.push(text2);
666
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
667
+ parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
668
+ }
669
+ return `Assistant: ${parts.join("\n")}`;
670
+ }
671
+ const text = msg.content ? getTextContent(msg.content) : "";
672
+ return `${msg.role}: ${text}`;
673
+ }).join("\n");
674
+ const lastPrompt = extractLastUserPrompt(messages);
675
+ return `Conversation history:
676
+ ${history}
677
+
678
+ User: ${lastPrompt}`;
679
+ }
680
+ var init_shared = __esm({
681
+ "src/backends/shared.ts"() {
682
+ init_types2();
683
+ }
684
+ });
685
+
452
686
  // src/backends/copilot.ts
453
687
  var copilot_exports = {};
454
688
  __export(copilot_exports, {
@@ -457,10 +691,9 @@ __export(copilot_exports, {
457
691
  createCopilotService: () => createCopilotService
458
692
  });
459
693
  async function loadSDK() {
460
- if (sdkModule) return sdkModule;
694
+ if (_sdkMock) return _sdkMock;
461
695
  try {
462
- sdkModule = await import('@github/copilot-sdk');
463
- return sdkModule;
696
+ return await import('@github/copilot-sdk');
464
697
  } catch {
465
698
  throw new SubprocessError(
466
699
  "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
@@ -468,10 +701,10 @@ async function loadSDK() {
468
701
  }
469
702
  }
470
703
  function _injectSDK(mock) {
471
- sdkModule = mock;
704
+ _sdkMock = mock;
472
705
  }
473
706
  function _resetSDK() {
474
- sdkModule = null;
707
+ _sdkMock = null;
475
708
  }
476
709
  function mapToolsToSDK(tools) {
477
710
  return tools.map((tool) => ({
@@ -516,6 +749,7 @@ function buildPermissionHandler(config) {
516
749
  const unifiedRequest = {
517
750
  toolName,
518
751
  toolArgs: { ...request },
752
+ toolCallId: request.toolCallId,
519
753
  rawSDKRequest: request
520
754
  };
521
755
  const ac = new AbortController();
@@ -620,15 +854,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
620
854
  };
621
855
  case "session.error":
622
856
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
623
- return {
624
- type: "error",
625
- error: String(data.message ?? "Unknown error"),
626
- recoverable: false
627
- };
857
+ {
858
+ const errorMsg = String(data.message ?? "Unknown error");
859
+ const code = classifyAgentError(errorMsg);
860
+ return {
861
+ type: "error",
862
+ error: errorMsg,
863
+ recoverable: isRecoverableErrorCode(code),
864
+ code
865
+ };
866
+ }
628
867
  case "assistant.message": {
629
868
  const doneEvent = {
630
869
  type: "done",
631
- finalOutput: data.content ? String(data.content) : null
870
+ finalOutput: null,
871
+ streamed: true
632
872
  };
633
873
  if (thinkingTracker.endThinking()) {
634
874
  return [{ type: "thinking_end" }, doneEvent];
@@ -639,66 +879,13 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
639
879
  return null;
640
880
  }
641
881
  }
642
- function extractLastUserPrompt(messages) {
643
- for (let i = messages.length - 1; i >= 0; i--) {
644
- const msg = messages[i];
645
- if (msg.role === "user") {
646
- return getTextContent(msg.content);
647
- }
648
- }
649
- return "";
650
- }
651
- function serializeToolCall(tc) {
652
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
653
- return ` Tool call: ${tc.name}(${args})`;
654
- }
655
- function serializeToolResult(tr) {
656
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
657
- const prefix = tr.isError ? "[ERROR] " : "";
658
- return ` ${tr.name} \u2192 ${prefix}${result}`;
659
- }
660
- function buildContextualPrompt(messages) {
661
- if (messages.length <= 1) {
662
- return extractLastUserPrompt(messages);
663
- }
664
- const history = messages.slice(0, -1).map((msg) => {
665
- if (msg.role === "user") {
666
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
667
- }
668
- if (msg.role === "tool" && msg.toolResults) {
669
- const results = msg.toolResults.map(serializeToolResult).join("\n");
670
- return `Tool results:
671
- ${results}`;
672
- }
673
- if (msg.role === "assistant") {
674
- const parts = [];
675
- const thinking = msg.thinking;
676
- if (thinking) {
677
- parts.push(`[reasoning: ${thinking}]`);
678
- }
679
- const text2 = msg.content ? getTextContent(msg.content) : "";
680
- if (text2) parts.push(text2);
681
- if (msg.toolCalls && msg.toolCalls.length > 0) {
682
- parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
683
- }
684
- return `Assistant: ${parts.join("\n")}`;
685
- }
686
- const text = msg.content ? getTextContent(msg.content) : "";
687
- return `${msg.role}: ${text}`;
688
- }).join("\n");
689
- const lastPrompt = extractLastUserPrompt(messages);
690
- return `Conversation history:
691
- ${history}
692
-
693
- User: ${lastPrompt}`;
694
- }
695
882
  function withTimeout(promise, ms, message) {
696
- return new Promise((resolve2, reject) => {
883
+ return new Promise((resolve, reject) => {
697
884
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
698
885
  promise.then(
699
886
  (val) => {
700
887
  clearTimeout(timer);
701
- resolve2(val);
888
+ resolve(val);
702
889
  },
703
890
  (err) => {
704
891
  clearTimeout(timer);
@@ -710,14 +897,15 @@ function withTimeout(promise, ms, message) {
710
897
  function createCopilotService(options) {
711
898
  return new CopilotAgentService(options);
712
899
  }
713
- var sdkModule, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
900
+ var _sdkMock, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
714
901
  var init_copilot = __esm({
715
902
  "src/backends/copilot.ts"() {
716
- init_types();
903
+ init_types2();
717
904
  init_base_agent();
718
- init_errors();
905
+ init_errors2();
719
906
  init_schema();
720
- sdkModule = null;
907
+ init_shared();
908
+ _sdkMock = null;
721
909
  ToolCallTracker = class {
722
910
  map = /* @__PURE__ */ new Map();
723
911
  trackStart(toolCallId, toolName, args) {
@@ -765,6 +953,7 @@ var init_copilot = __esm({
765
953
  isPersistent;
766
954
  persistentSession = null;
767
955
  _sessionId;
956
+ _persistentModel;
768
957
  activeSession = null;
769
958
  _resumeSessionId;
770
959
  _toolsReady = null;
@@ -815,47 +1004,63 @@ var init_copilot = __esm({
815
1004
  });
816
1005
  this.persistentSession = null;
817
1006
  this._sessionId = void 0;
1007
+ this._persistentModel = void 0;
818
1008
  }
819
1009
  }
820
- async getOrCreateSession(streaming) {
1010
+ async getOrCreateSession(streaming, options) {
821
1011
  if (this.isPersistent && this.persistentSession) {
822
- return { session: this.persistentSession, isNew: false };
1012
+ if (options.model !== this._persistentModel) {
1013
+ this.persistentSession.destroy().catch(() => {
1014
+ });
1015
+ this.persistentSession = null;
1016
+ this._sessionId = void 0;
1017
+ } else {
1018
+ return { session: this.persistentSession, isNew: false };
1019
+ }
823
1020
  }
824
1021
  if (this._toolsReady) {
825
1022
  await this._toolsReady;
826
1023
  this._toolsReady = null;
827
1024
  }
1025
+ const sessionConfig = { ...this.sessionConfig };
1026
+ sessionConfig.model = options.model;
1027
+ const resolvedTools = this.resolveTools(options);
1028
+ if (options?.tools) {
1029
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
1030
+ }
828
1031
  const client = await this.getClient();
829
1032
  if (this._resumeSessionId) {
830
1033
  const storedId = this._resumeSessionId;
831
1034
  this._resumeSessionId = void 0;
832
1035
  try {
833
1036
  const session2 = await client.resumeSession(storedId, {
834
- ...this.sessionConfig,
1037
+ ...sessionConfig,
835
1038
  streaming: this.isPersistent ? true : streaming
836
1039
  });
837
1040
  if (this.isPersistent) {
838
1041
  this.persistentSession = session2;
839
1042
  this._sessionId = session2.sessionId;
1043
+ this._persistentModel = options.model;
840
1044
  }
841
1045
  return { session: session2, isNew: false };
842
1046
  } catch {
843
1047
  }
844
1048
  }
845
1049
  const session = await client.createSession({
846
- ...this.sessionConfig,
1050
+ ...sessionConfig,
847
1051
  streaming: this.isPersistent ? true : streaming
848
1052
  });
849
1053
  if (this.isPersistent) {
850
1054
  this.persistentSession = session;
851
1055
  this._sessionId = session.sessionId;
1056
+ this._persistentModel = options.model;
852
1057
  }
853
1058
  return { session, isNew: true };
854
1059
  }
855
1060
  // ─── executeRun ─────────────────────────────────────────────────
856
- async executeRun(messages, _options, signal) {
1061
+ async executeRun(messages, options, signal) {
857
1062
  this.checkAbort(signal);
858
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
1063
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
859
1064
  this.activeSession = session;
860
1065
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
861
1066
  const tracker = new ToolCallTracker();
@@ -952,9 +1157,9 @@ You MUST respond with ONLY valid JSON matching this schema:
952
1157
  };
953
1158
  }
954
1159
  // ─── executeStream ──────────────────────────────────────────────
955
- async *executeStream(messages, _options, signal) {
1160
+ async *executeStream(messages, options, signal) {
956
1161
  this.checkAbort(signal);
957
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1162
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
958
1163
  this.activeSession = session;
959
1164
  if (isNewSession) {
960
1165
  yield this.emitSessionInfo(session.sessionId);
@@ -971,8 +1176,8 @@ You MUST respond with ONLY valid JSON matching this schema:
971
1176
  notify = null;
972
1177
  }
973
1178
  };
974
- const waitForItem = () => new Promise((resolve2) => {
975
- notify = resolve2;
1179
+ const waitForItem = () => new Promise((resolve) => {
1180
+ notify = resolve;
976
1181
  });
977
1182
  const unsubscribe = session.on((event) => {
978
1183
  const mapped = mapSessionEvent(event, tracker, thinkingTracker);
@@ -1100,7 +1305,10 @@ You MUST respond with ONLY valid JSON matching this schema:
1100
1305
  return models.map((m) => ({
1101
1306
  id: m.id,
1102
1307
  name: m.name,
1103
- provider: "copilot"
1308
+ provider: "copilot",
1309
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1310
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1311
+ }
1104
1312
  }));
1105
1313
  }
1106
1314
  async validate() {
@@ -1153,10 +1361,9 @@ function stripMcpPrefix(name) {
1153
1361
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
1154
1362
  }
1155
1363
  async function loadSDK2() {
1156
- if (sdkModule2) return sdkModule2;
1364
+ if (_sdkMock2) return _sdkMock2;
1157
1365
  try {
1158
- sdkModule2 = await import('@anthropic-ai/claude-agent-sdk');
1159
- return sdkModule2;
1366
+ return await import('@anthropic-ai/claude-agent-sdk');
1160
1367
  } catch {
1161
1368
  throw new SubprocessError(
1162
1369
  "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
@@ -1164,13 +1371,32 @@ async function loadSDK2() {
1164
1371
  }
1165
1372
  }
1166
1373
  function _injectSDK2(mock) {
1167
- sdkModule2 = mock;
1374
+ _sdkMock2 = mock;
1168
1375
  }
1169
1376
  function _resetSDK2() {
1170
- sdkModule2 = null;
1377
+ _sdkMock2 = null;
1378
+ }
1379
+ function normalizeAskUserInput(args) {
1380
+ if (typeof args.question === "string") {
1381
+ return {
1382
+ question: args.question,
1383
+ choices: Array.isArray(args.choices) ? args.choices : void 0,
1384
+ allowFreeform: args.allowFreeform !== false
1385
+ };
1386
+ }
1387
+ const questions = args.questions;
1388
+ if (questions && questions.length > 0) {
1389
+ const first = questions[0];
1390
+ return {
1391
+ question: first.question,
1392
+ choices: first.options?.map((o) => o.label),
1393
+ allowFreeform: true
1394
+ };
1395
+ }
1396
+ return { question: JSON.stringify(args), allowFreeform: true };
1171
1397
  }
1172
- function buildMcpServer(sdk, tools, toolResultCapture) {
1173
- if (tools.length === 0) return void 0;
1398
+ function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
1399
+ if (tools.length === 0 && !onAskUser) return void 0;
1174
1400
  const mcpTools = tools.map((tool) => {
1175
1401
  const zodSchema = tool.parameters;
1176
1402
  const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
@@ -1194,6 +1420,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
1194
1420
  }
1195
1421
  );
1196
1422
  });
1423
+ if (onAskUser) {
1424
+ const askUserTool = sdk.tool(
1425
+ "ask_user",
1426
+ "Ask the user a question and wait for their response",
1427
+ {
1428
+ question: { type: "string", description: "The question to ask the user" },
1429
+ choices: {
1430
+ type: "array",
1431
+ items: { type: "string" },
1432
+ description: "Optional list of choices for multiple choice"
1433
+ },
1434
+ questions: {
1435
+ type: "array",
1436
+ items: {
1437
+ type: "object",
1438
+ properties: {
1439
+ question: { type: "string" },
1440
+ options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
1441
+ }
1442
+ },
1443
+ description: "Alternative nested question format"
1444
+ }
1445
+ },
1446
+ async (args) => {
1447
+ const normalized = normalizeAskUserInput(args);
1448
+ const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
1449
+ return {
1450
+ content: [{ type: "text", text: response.answer }]
1451
+ };
1452
+ }
1453
+ );
1454
+ mcpTools.push(askUserTool);
1455
+ }
1197
1456
  return sdk.createSdkMcpServer({
1198
1457
  name: MCP_SERVER_NAME,
1199
1458
  version: "1.0.0",
@@ -1243,6 +1502,7 @@ function buildCanUseTool(config) {
1243
1502
  const unifiedRequest = {
1244
1503
  toolName,
1245
1504
  toolArgs: input,
1505
+ toolCallId: options.toolUseID,
1246
1506
  suggestedScope: extractSuggestedScope(options.suggestions),
1247
1507
  rawSDKRequest: { toolName, input, ...options }
1248
1508
  };
@@ -1295,6 +1555,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1295
1555
  if (block.type === "tool_use") {
1296
1556
  const toolCallId = String(block.id ?? "");
1297
1557
  const toolName = stripMcpPrefix(block.name ?? "unknown");
1558
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1298
1559
  if (toolCallTracker) {
1299
1560
  toolCallTracker.trackStart(toolCallId, toolName);
1300
1561
  }
@@ -1318,6 +1579,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1318
1579
  case "tool_use_summary": {
1319
1580
  const summary = msg.summary;
1320
1581
  const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
1582
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
1321
1583
  const precedingIds = msg.preceding_tool_use_ids;
1322
1584
  let toolCallId = "";
1323
1585
  if (precedingIds && precedingIds.length > 0) {
@@ -1371,10 +1633,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1371
1633
  }
1372
1634
  if (msg.is_error) {
1373
1635
  const r = msg;
1636
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
1637
+ const code = classifyAgentError(errorMsg);
1374
1638
  return {
1375
1639
  type: "error",
1376
- error: r.errors?.join("; ") ?? "Unknown error",
1377
- recoverable: false
1640
+ error: errorMsg,
1641
+ recoverable: isRecoverableErrorCode(code),
1642
+ code
1378
1643
  };
1379
1644
  }
1380
1645
  return null;
@@ -1383,72 +1648,21 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1383
1648
  return null;
1384
1649
  }
1385
1650
  }
1386
- function extractLastUserPrompt2(messages) {
1387
- for (let i = messages.length - 1; i >= 0; i--) {
1388
- const msg = messages[i];
1389
- if (msg.role === "user") {
1390
- return getTextContent(msg.content);
1391
- }
1392
- }
1393
- return "";
1394
- }
1395
- function serializeToolCall2(tc) {
1396
- const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
1397
- return ` Tool call: ${tc.name}(${args})`;
1398
- }
1399
- function serializeToolResult2(tr) {
1400
- const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
1401
- const prefix = tr.isError ? "[ERROR] " : "";
1402
- return ` ${tr.name} \u2192 ${prefix}${result}`;
1403
- }
1404
- function buildContextualPrompt2(messages) {
1405
- if (messages.length <= 1) {
1406
- return extractLastUserPrompt2(messages);
1407
- }
1408
- const history = messages.slice(0, -1).map((msg) => {
1409
- if (msg.role === "user") {
1410
- return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
1411
- }
1412
- if (msg.role === "tool" && msg.toolResults) {
1413
- const results = msg.toolResults.map(serializeToolResult2).join("\n");
1414
- return `Tool results:
1415
- ${results}`;
1416
- }
1417
- if (msg.role === "assistant") {
1418
- const parts = [];
1419
- const thinking = msg.thinking;
1420
- if (thinking) {
1421
- parts.push(`[reasoning: ${thinking}]`);
1422
- }
1423
- const text2 = msg.content ? getTextContent(msg.content) : "";
1424
- if (text2) parts.push(text2);
1425
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1426
- parts.push(msg.toolCalls.map(serializeToolCall2).join("\n"));
1427
- }
1428
- return `Assistant: ${parts.join("\n")}`;
1429
- }
1430
- const text = msg.content ? getTextContent(msg.content) : "";
1431
- return `${msg.role}: ${text}`;
1432
- }).join("\n");
1433
- const lastPrompt = extractLastUserPrompt2(messages);
1434
- return `Conversation history:
1435
- ${history}
1436
-
1437
- User: ${lastPrompt}`;
1438
- }
1439
1651
  function createClaudeService(options) {
1440
1652
  return new ClaudeAgentService(options);
1441
1653
  }
1442
- var MCP_SERVER_NAME, MCP_TOOL_PREFIX, sdkModule2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1654
+ var MCP_SERVER_NAME, MCP_TOOL_PREFIX, CLAUDE_INTERNAL_TOOL_NAMES, _sdkMock2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1443
1655
  var init_claude = __esm({
1444
1656
  "src/backends/claude.ts"() {
1445
- init_types();
1657
+ init_types2();
1446
1658
  init_base_agent();
1447
- init_errors();
1659
+ init_errors2();
1448
1660
  init_schema();
1661
+ init_shared();
1449
1662
  MCP_SERVER_NAME = "agent-sdk-tools";
1450
1663
  MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
1451
- sdkModule2 = null;
1664
+ CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1665
+ _sdkMock2 = null;
1452
1666
  ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
1453
1667
  ANTHROPIC_API_VERSION = "2023-06-01";
1454
1668
  ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
@@ -1493,11 +1707,6 @@ var init_claude = __esm({
1493
1707
  if (options.resumeSessionId) {
1494
1708
  this._sessionId = options.resumeSessionId;
1495
1709
  }
1496
- if (config.supervisor?.onAskUser) {
1497
- console.warn(
1498
- "[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
1499
- );
1500
- }
1501
1710
  }
1502
1711
  get sessionId() {
1503
1712
  return this._sessionId;
@@ -1521,12 +1730,12 @@ var init_claude = __esm({
1521
1730
  const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
1522
1731
  return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
1523
1732
  }
1524
- buildQueryOptions(signal) {
1733
+ buildQueryOptions(signal, options) {
1525
1734
  const ac = new AbortController();
1526
1735
  signal.addEventListener("abort", () => ac.abort(), { once: true });
1527
1736
  const opts = {
1528
1737
  abortController: ac,
1529
- model: this.config.model,
1738
+ model: options.model,
1530
1739
  maxTurns: this.options.maxTurns,
1531
1740
  cwd: this.options.workingDirectory,
1532
1741
  pathToClaudeCodeExecutable: this.options.cliPath,
@@ -1556,25 +1765,85 @@ var init_claude = __esm({
1556
1765
  return opts;
1557
1766
  }
1558
1767
  async buildMcpConfig(opts, toolResultCapture) {
1559
- if (this.tools.length === 0) return opts;
1768
+ const onAskUser = this.config.supervisor?.onAskUser;
1769
+ if (this.tools.length === 0 && !onAskUser) return opts;
1560
1770
  const sdk = await loadSDK2();
1561
- const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
1771
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
1562
1772
  if (mcpServer) {
1563
1773
  opts.mcpServers = {
1564
1774
  [MCP_SERVER_NAME]: mcpServer
1565
1775
  };
1566
1776
  const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1777
+ if (onAskUser) {
1778
+ mcpToolNames.push(mcpToolName("ask_user"));
1779
+ }
1567
1780
  opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
1568
1781
  }
1782
+ if (onAskUser) {
1783
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1784
+ }
1569
1785
  return opts;
1570
1786
  }
1787
+ // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
1788
+ /** Setup a retry query: clear session, rebuild with full history */
1789
+ async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
1790
+ this.clearPersistentSession();
1791
+ const retryPrompt = buildContextualPrompt(messages);
1792
+ let retryOpts = this.buildQueryOptions(signal, options);
1793
+ toolResultCapture.clear();
1794
+ retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1795
+ modifyOpts?.(retryOpts);
1796
+ const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1797
+ this.activeQuery = retryQ;
1798
+ return retryQ;
1799
+ }
1800
+ /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
1801
+ collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
1802
+ if (msg.type !== "assistant") return;
1803
+ const betaMessage = msg.message;
1804
+ if (!betaMessage?.content) return;
1805
+ for (const block of betaMessage.content) {
1806
+ if (block.type === "tool_use") {
1807
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1808
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1809
+ toolCalls.push({
1810
+ toolName,
1811
+ args: block.input ?? {},
1812
+ result: toolResultCapture.get(toolName) ?? null,
1813
+ approved: true
1814
+ });
1815
+ }
1816
+ }
1817
+ }
1818
+ /** Back-fill tool results from capture map on summary/result messages */
1819
+ backfillToolResults(msg, toolCalls, toolResultCapture) {
1820
+ if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
1821
+ for (const tc of toolCalls) {
1822
+ if (tc.result === null) {
1823
+ const captured = toolResultCapture.get(tc.toolName);
1824
+ if (captured !== void 0) tc.result = captured;
1825
+ }
1826
+ }
1827
+ }
1828
+ /** Wrap retry inner loop with shared error handling */
1829
+ async withRetryErrorHandling(signal, fn) {
1830
+ try {
1831
+ return await fn();
1832
+ } catch (retryError) {
1833
+ if (this.isPersistent) this.clearPersistentSession();
1834
+ if (signal.aborted) throw new AbortError();
1835
+ throw retryError;
1836
+ } finally {
1837
+ this.activeQuery = null;
1838
+ }
1839
+ }
1571
1840
  // ─── executeRun ─────────────────────────────────────────────────
1572
- async executeRun(messages, _options, signal) {
1841
+ async executeRun(messages, options, signal) {
1573
1842
  this.checkAbort(signal);
1574
1843
  const sdk = await loadSDK2();
1575
1844
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1576
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1577
- let opts = this.buildQueryOptions(signal);
1845
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1846
+ let opts = this.buildQueryOptions(signal, options);
1578
1847
  const toolResultCapture = /* @__PURE__ */ new Map();
1579
1848
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1580
1849
  const q = sdk.query({ prompt, options: opts });
@@ -1584,30 +1853,8 @@ var init_claude = __esm({
1584
1853
  let usage;
1585
1854
  try {
1586
1855
  for await (const msg of q) {
1587
- if (msg.type === "assistant") {
1588
- const betaMessage = msg.message;
1589
- if (betaMessage?.content) {
1590
- for (const block of betaMessage.content) {
1591
- if (block.type === "tool_use") {
1592
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1593
- toolCalls.push({
1594
- toolName,
1595
- args: block.input ?? {},
1596
- result: toolResultCapture.get(toolName) ?? null,
1597
- approved: true
1598
- });
1599
- }
1600
- }
1601
- }
1602
- }
1603
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1604
- for (const tc of toolCalls) {
1605
- if (tc.result === null) {
1606
- const captured = toolResultCapture.get(tc.toolName);
1607
- if (captured !== void 0) tc.result = captured;
1608
- }
1609
- }
1610
- }
1856
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1857
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1611
1858
  if (msg.type === "result") {
1612
1859
  if (msg.subtype === "success") {
1613
1860
  const r = msg;
@@ -1627,41 +1874,13 @@ var init_claude = __esm({
1627
1874
  } catch (e) {
1628
1875
  if (signal.aborted) throw new AbortError();
1629
1876
  if (isResuming && this.isPersistent) {
1630
- this.clearPersistentSession();
1631
- const retryPrompt = buildContextualPrompt2(messages);
1632
- let retryOpts = this.buildQueryOptions(signal);
1633
- toolResultCapture.clear();
1634
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1635
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1636
- this.activeQuery = retryQ;
1877
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1637
1878
  toolCalls.length = 0;
1638
1879
  output = null;
1639
- try {
1880
+ return this.withRetryErrorHandling(signal, async () => {
1640
1881
  for await (const msg of retryQ) {
1641
- if (msg.type === "assistant") {
1642
- const betaMessage = msg.message;
1643
- if (betaMessage?.content) {
1644
- for (const block of betaMessage.content) {
1645
- if (block.type === "tool_use") {
1646
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1647
- toolCalls.push({
1648
- toolName,
1649
- args: block.input ?? {},
1650
- result: toolResultCapture.get(toolName) ?? null,
1651
- approved: true
1652
- });
1653
- }
1654
- }
1655
- }
1656
- }
1657
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1658
- for (const tc of toolCalls) {
1659
- if (tc.result === null) {
1660
- const captured = toolResultCapture.get(tc.toolName);
1661
- if (captured !== void 0) tc.result = captured;
1662
- }
1663
- }
1664
- }
1882
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1883
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1665
1884
  if (msg.type === "result") {
1666
1885
  if (msg.subtype === "success") {
1667
1886
  const r = msg;
@@ -1678,23 +1897,17 @@ var init_claude = __esm({
1678
1897
  }
1679
1898
  }
1680
1899
  }
1681
- } catch (retryError) {
1682
- if (this.isPersistent) this.clearPersistentSession();
1683
- if (signal.aborted) throw new AbortError();
1684
- throw retryError;
1685
- } finally {
1686
- this.activeQuery = null;
1687
- }
1688
- return {
1689
- output,
1690
- structuredOutput: void 0,
1691
- toolCalls,
1692
- messages: [
1693
- ...messages,
1694
- ...output !== null ? [{ role: "assistant", content: output }] : []
1695
- ],
1696
- usage
1697
- };
1900
+ return {
1901
+ output,
1902
+ structuredOutput: void 0,
1903
+ toolCalls,
1904
+ messages: [
1905
+ ...messages,
1906
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1907
+ ],
1908
+ usage
1909
+ };
1910
+ });
1698
1911
  }
1699
1912
  if (this.isPersistent) this.clearPersistentSession();
1700
1913
  throw e;
@@ -1713,12 +1926,12 @@ var init_claude = __esm({
1713
1926
  };
1714
1927
  }
1715
1928
  // ─── executeRunStructured ───────────────────────────────────────
1716
- async executeRunStructured(messages, schema, _options, signal) {
1929
+ async executeRunStructured(messages, schema, options, signal) {
1717
1930
  this.checkAbort(signal);
1718
1931
  const sdk = await loadSDK2();
1719
1932
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1720
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1721
- let opts = this.buildQueryOptions(signal);
1933
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1934
+ let opts = this.buildQueryOptions(signal, options);
1722
1935
  const toolResultCapture = /* @__PURE__ */ new Map();
1723
1936
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1724
1937
  const jsonSchema = zodToJsonSchema(schema.schema);
@@ -1734,30 +1947,8 @@ var init_claude = __esm({
1734
1947
  let usage;
1735
1948
  try {
1736
1949
  for await (const msg of q) {
1737
- if (msg.type === "assistant") {
1738
- const betaMessage = msg.message;
1739
- if (betaMessage?.content) {
1740
- for (const block of betaMessage.content) {
1741
- if (block.type === "tool_use") {
1742
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1743
- toolCalls.push({
1744
- toolName,
1745
- args: block.input ?? {},
1746
- result: toolResultCapture.get(toolName) ?? null,
1747
- approved: true
1748
- });
1749
- }
1750
- }
1751
- }
1752
- }
1753
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1754
- for (const tc of toolCalls) {
1755
- if (tc.result === null) {
1756
- const captured = toolResultCapture.get(tc.toolName);
1757
- if (captured !== void 0) tc.result = captured;
1758
- }
1759
- }
1760
- }
1950
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1951
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1761
1952
  if (msg.type === "result" && msg.subtype === "success") {
1762
1953
  const r = msg;
1763
1954
  output = r.result;
@@ -1792,46 +1983,23 @@ var init_claude = __esm({
1792
1983
  } catch (e) {
1793
1984
  if (signal.aborted) throw new AbortError();
1794
1985
  if (isResuming && this.isPersistent) {
1795
- this.clearPersistentSession();
1796
- const retryPrompt = buildContextualPrompt2(messages);
1797
- let retryOpts = this.buildQueryOptions(signal);
1798
- toolResultCapture.clear();
1799
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1800
- retryOpts.outputFormat = {
1801
- type: "json_schema",
1802
- schema: jsonSchema
1803
- };
1804
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1805
- this.activeQuery = retryQ;
1986
+ const retryQ = await this.prepareRetryQuery(
1987
+ sdk,
1988
+ messages,
1989
+ signal,
1990
+ options,
1991
+ toolResultCapture,
1992
+ (opts2) => {
1993
+ opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
1994
+ }
1995
+ );
1806
1996
  toolCalls.length = 0;
1807
1997
  output = null;
1808
1998
  structuredOutput = void 0;
1809
- try {
1999
+ return this.withRetryErrorHandling(signal, async () => {
1810
2000
  for await (const msg of retryQ) {
1811
- if (msg.type === "assistant") {
1812
- const betaMessage = msg.message;
1813
- if (betaMessage?.content) {
1814
- for (const block of betaMessage.content) {
1815
- if (block.type === "tool_use") {
1816
- const toolName = stripMcpPrefix(block.name ?? "unknown");
1817
- toolCalls.push({
1818
- toolName,
1819
- args: block.input ?? {},
1820
- result: toolResultCapture.get(toolName) ?? null,
1821
- approved: true
1822
- });
1823
- }
1824
- }
1825
- }
1826
- }
1827
- if (msg.type === "tool_use_summary" || msg.type === "result") {
1828
- for (const tc of toolCalls) {
1829
- if (tc.result === null) {
1830
- const captured = toolResultCapture.get(tc.toolName);
1831
- if (captured !== void 0) tc.result = captured;
1832
- }
1833
- }
1834
- }
2001
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
2002
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1835
2003
  if (msg.type === "result" && msg.subtype === "success") {
1836
2004
  const r = msg;
1837
2005
  output = r.result;
@@ -1863,23 +2031,17 @@ var init_claude = __esm({
1863
2031
  );
1864
2032
  }
1865
2033
  }
1866
- } catch (retryError) {
1867
- if (this.isPersistent) this.clearPersistentSession();
1868
- if (signal.aborted) throw new AbortError();
1869
- throw retryError;
1870
- } finally {
1871
- this.activeQuery = null;
1872
- }
1873
- return {
1874
- output,
1875
- structuredOutput,
1876
- toolCalls,
1877
- messages: [
1878
- ...messages,
1879
- ...output !== null ? [{ role: "assistant", content: output }] : []
1880
- ],
1881
- usage
1882
- };
2034
+ return {
2035
+ output,
2036
+ structuredOutput,
2037
+ toolCalls,
2038
+ messages: [
2039
+ ...messages,
2040
+ ...output !== null ? [{ role: "assistant", content: output }] : []
2041
+ ],
2042
+ usage
2043
+ };
2044
+ });
1883
2045
  }
1884
2046
  if (this.isPersistent) this.clearPersistentSession();
1885
2047
  throw e;
@@ -1898,12 +2060,12 @@ var init_claude = __esm({
1898
2060
  };
1899
2061
  }
1900
2062
  // ─── executeStream ──────────────────────────────────────────────
1901
- async *executeStream(messages, _options, signal) {
2063
+ async *executeStream(messages, options, signal) {
1902
2064
  this.checkAbort(signal);
1903
2065
  const sdk = await loadSDK2();
1904
2066
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1905
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1906
- let opts = this.buildQueryOptions(signal);
2067
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
2068
+ let opts = this.buildQueryOptions(signal, options);
1907
2069
  const toolResultCapture = /* @__PURE__ */ new Map();
1908
2070
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1909
2071
  const q = sdk.query({ prompt, options: opts });
@@ -1911,6 +2073,7 @@ var init_claude = __esm({
1911
2073
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
1912
2074
  const toolCallTracker = new ClaudeToolCallTracker();
1913
2075
  const pendingStreamToolCalls = /* @__PURE__ */ new Map();
2076
+ let hasStreamedText = false;
1914
2077
  try {
1915
2078
  for await (const msg of q) {
1916
2079
  if (signal.aborted) throw new AbortError();
@@ -1928,6 +2091,7 @@ var init_claude = __esm({
1928
2091
  } else if (e.type === "tool_call_end") {
1929
2092
  pendingStreamToolCalls.delete(e.toolCallId);
1930
2093
  }
2094
+ if (e.type === "text_delta") hasStreamedText = true;
1931
2095
  yield e;
1932
2096
  }
1933
2097
  }
@@ -1951,22 +2115,21 @@ var init_claude = __esm({
1951
2115
  }
1952
2116
  yield this.emitSessionInfo(r.session_id);
1953
2117
  }
1954
- yield { type: "done", finalOutput: r.result };
2118
+ yield {
2119
+ type: "done",
2120
+ finalOutput: hasStreamedText ? null : r.result,
2121
+ ...hasStreamedText ? { streamed: true } : {}
2122
+ };
1955
2123
  }
1956
2124
  }
1957
2125
  } catch (e) {
1958
2126
  if (signal.aborted) throw new AbortError();
1959
2127
  if (isResuming && this.isPersistent) {
1960
- this.clearPersistentSession();
1961
- const retryPrompt = buildContextualPrompt2(messages);
1962
- let retryOpts = this.buildQueryOptions(signal);
1963
- toolResultCapture.clear();
1964
- retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1965
- const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1966
- this.activeQuery = retryQ;
2128
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1967
2129
  const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
1968
2130
  const retryToolCallTracker = new ClaudeToolCallTracker();
1969
2131
  const retryPendingToolCalls = /* @__PURE__ */ new Map();
2132
+ let retryHasStreamedText = false;
1970
2133
  try {
1971
2134
  for await (const msg of retryQ) {
1972
2135
  if (signal.aborted) throw new AbortError();
@@ -1984,6 +2147,7 @@ var init_claude = __esm({
1984
2147
  } else if (ev.type === "tool_call_end") {
1985
2148
  retryPendingToolCalls.delete(ev.toolCallId);
1986
2149
  }
2150
+ if (ev.type === "text_delta") retryHasStreamedText = true;
1987
2151
  yield ev;
1988
2152
  }
1989
2153
  }
@@ -2007,7 +2171,11 @@ var init_claude = __esm({
2007
2171
  }
2008
2172
  yield this.emitSessionInfo(r.session_id);
2009
2173
  }
2010
- yield { type: "done", finalOutput: r.result };
2174
+ yield {
2175
+ type: "done",
2176
+ finalOutput: retryHasStreamedText ? null : r.result,
2177
+ ...retryHasStreamedText ? { streamed: true } : {}
2178
+ };
2011
2179
  }
2012
2180
  }
2013
2181
  } catch (retryError) {
@@ -2069,7 +2237,8 @@ var init_claude = __esm({
2069
2237
  this.cachedModels = body.data.map((m) => ({
2070
2238
  id: m.id,
2071
2239
  name: m.display_name,
2072
- provider: "claude"
2240
+ provider: "claude",
2241
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
2073
2242
  }));
2074
2243
  return this.cachedModels;
2075
2244
  }
@@ -2127,32 +2296,30 @@ __export(vercel_ai_exports, {
2127
2296
  createVercelAIService: () => createVercelAIService
2128
2297
  });
2129
2298
  async function loadSDK3() {
2130
- if (sdkModule3) return sdkModule3;
2299
+ if (_sdkMock3) return _sdkMock3;
2131
2300
  try {
2132
- sdkModule3 = await import('ai');
2133
- return sdkModule3;
2301
+ return await import('ai');
2134
2302
  } catch {
2135
2303
  throw new DependencyError("ai");
2136
2304
  }
2137
2305
  }
2138
2306
  async function loadCompat() {
2139
- if (compatModule) return compatModule;
2307
+ if (_compatMock) return _compatMock;
2140
2308
  try {
2141
- compatModule = await import('@ai-sdk/openai-compatible');
2142
- return compatModule;
2309
+ return await import('@ai-sdk/openai-compatible');
2143
2310
  } catch {
2144
2311
  throw new DependencyError("@ai-sdk/openai-compatible");
2145
2312
  }
2146
2313
  }
2147
2314
  function _injectSDK3(mock) {
2148
- sdkModule3 = mock;
2315
+ _sdkMock3 = mock;
2149
2316
  }
2150
2317
  function _injectCompat(mock) {
2151
- compatModule = mock;
2318
+ _compatMock = mock;
2152
2319
  }
2153
2320
  function _resetSDK3() {
2154
- sdkModule3 = null;
2155
- compatModule = null;
2321
+ _sdkMock3 = null;
2322
+ _compatMock = null;
2156
2323
  }
2157
2324
  function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, signal) {
2158
2325
  const toolMap = {};
@@ -2195,13 +2362,14 @@ function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, s
2195
2362
  return toolMap;
2196
2363
  }
2197
2364
  function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
2198
- return async (args) => {
2365
+ return async (args, options) => {
2199
2366
  if (ourTool.needsApproval && supervisor?.onPermission) {
2200
2367
  const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
2201
2368
  if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
2202
2369
  const request = {
2203
2370
  toolName: ourTool.name,
2204
- toolArgs: args ?? {}
2371
+ toolArgs: args ?? {},
2372
+ toolCallId: options?.toolCallId
2205
2373
  };
2206
2374
  const decision = await supervisor.onPermission(
2207
2375
  request,
@@ -2308,7 +2476,8 @@ function mapStreamPart(part) {
2308
2476
  return {
2309
2477
  type: "error",
2310
2478
  error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
2311
- recoverable: true
2479
+ recoverable: true,
2480
+ code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
2312
2481
  };
2313
2482
  }
2314
2483
  case "reasoning-start":
@@ -2329,10 +2498,13 @@ function mapStreamPart(part) {
2329
2498
  }
2330
2499
  case "error": {
2331
2500
  const p = part;
2501
+ const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
2502
+ const code = classifyAgentError(errorMsg);
2332
2503
  return {
2333
2504
  type: "error",
2334
- error: p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error"),
2335
- recoverable: false
2505
+ error: errorMsg,
2506
+ recoverable: isRecoverableErrorCode(code),
2507
+ code
2336
2508
  };
2337
2509
  }
2338
2510
  default:
@@ -2342,15 +2514,15 @@ function mapStreamPart(part) {
2342
2514
  function createVercelAIService(options) {
2343
2515
  return new VercelAIAgentService(options);
2344
2516
  }
2345
- var sdkModule3, compatModule, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2517
+ var _sdkMock3, _compatMock, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2346
2518
  var init_vercel_ai = __esm({
2347
2519
  "src/backends/vercel-ai.ts"() {
2348
- init_types();
2520
+ init_types2();
2349
2521
  init_base_agent();
2350
- init_errors();
2522
+ init_errors2();
2351
2523
  init_schema();
2352
- sdkModule3 = null;
2353
- compatModule = null;
2524
+ _sdkMock3 = null;
2525
+ _compatMock = null;
2354
2526
  DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2355
2527
  DEFAULT_PROVIDER = "openrouter";
2356
2528
  DEFAULT_MAX_TURNS = 10;
@@ -2363,28 +2535,33 @@ var init_vercel_ai = __esm({
2363
2535
  super(config);
2364
2536
  this.backendOptions = backendOptions;
2365
2537
  }
2366
- async getModel() {
2367
- if (this.model) return this.model;
2538
+ async getModel(options) {
2539
+ const requestedModel = options.model;
2540
+ const defaultModel = this.config.model;
2541
+ if (requestedModel === defaultModel && this.model) return this.model;
2368
2542
  const compat = await loadCompat();
2369
2543
  const provider = compat.createOpenAICompatible({
2370
2544
  name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
2371
2545
  baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
2372
2546
  apiKey: this.backendOptions.apiKey
2373
2547
  });
2374
- const modelId = this.config.model ?? "anthropic/claude-sonnet-4-5";
2375
- this.model = provider.chatModel(modelId);
2376
- return this.model;
2548
+ const model = provider.chatModel(requestedModel);
2549
+ if (requestedModel === defaultModel) {
2550
+ this.model = model;
2551
+ }
2552
+ return model;
2377
2553
  }
2378
- async getSDKTools(signal) {
2554
+ async getSDKTools(signal, options) {
2379
2555
  const sdk = await loadSDK3();
2380
- return mapToolsToSDK2(sdk, this.config.tools ?? [], this.config, this.sessionApprovals, this.config.permissionStore, signal);
2556
+ const tools = this.resolveTools(options);
2557
+ return mapToolsToSDK2(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
2381
2558
  }
2382
2559
  // ─── executeRun ─────────────────────────────────────────────────
2383
- async executeRun(messages, _options, signal) {
2560
+ async executeRun(messages, options, signal) {
2384
2561
  this.checkAbort(signal);
2385
2562
  const sdk = await loadSDK3();
2386
- const model = await this.getModel();
2387
- const tools = await this.getSDKTools(signal);
2563
+ const model = await this.getModel(options);
2564
+ const tools = await this.getSDKTools(signal, options);
2388
2565
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2389
2566
  const sdkMessages = messagesToSDK(messages);
2390
2567
  const hasTools = Object.keys(tools).length > 0;
@@ -2440,10 +2617,10 @@ var init_vercel_ai = __esm({
2440
2617
  };
2441
2618
  }
2442
2619
  // ─── executeRunStructured ───────────────────────────────────────
2443
- async executeRunStructured(messages, schema, _options, signal) {
2620
+ async executeRunStructured(messages, schema, options, signal) {
2444
2621
  this.checkAbort(signal);
2445
2622
  const sdk = await loadSDK3();
2446
- const model = await this.getModel();
2623
+ const model = await this.getModel(options);
2447
2624
  const sdkMessages = messagesToSDK(messages);
2448
2625
  const jsonSchema = zodToJsonSchema(schema.schema);
2449
2626
  const result = await sdk.generateObject({
@@ -2485,11 +2662,11 @@ var init_vercel_ai = __esm({
2485
2662
  };
2486
2663
  }
2487
2664
  // ─── executeStream ──────────────────────────────────────────────
2488
- async *executeStream(messages, _options, signal) {
2665
+ async *executeStream(messages, options, signal) {
2489
2666
  this.checkAbort(signal);
2490
2667
  const sdk = await loadSDK3();
2491
- const model = await this.getModel();
2492
- const tools = await this.getSDKTools(signal);
2668
+ const model = await this.getModel(options);
2669
+ const tools = await this.getSDKTools(signal, options);
2493
2670
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2494
2671
  const sdkMessages = messagesToSDK(messages);
2495
2672
  const hasTools = Object.keys(tools).length > 0;
@@ -2535,9 +2712,11 @@ var init_vercel_ai = __esm({
2535
2712
  promptTokens: Number(totalUsage?.inputTokens ?? 0),
2536
2713
  completionTokens: Number(totalUsage?.outputTokens ?? 0)
2537
2714
  };
2715
+ const hasStreamed = finalText.length > 0;
2538
2716
  yield {
2539
2717
  type: "done",
2540
- finalOutput: finalText || null
2718
+ finalOutput: hasStreamed ? null : finalText || null,
2719
+ ...hasStreamed ? { streamed: true } : {}
2541
2720
  };
2542
2721
  } catch (e) {
2543
2722
  if (signal.aborted) throw new AbortError();
@@ -2566,16 +2745,33 @@ var init_vercel_ai = __esm({
2566
2745
  const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
2567
2746
  try {
2568
2747
  const res = await globalThis.fetch(`${baseUrl}/models`, {
2569
- headers: { Authorization: `Bearer ${this.options.apiKey}` }
2748
+ headers: {
2749
+ Authorization: `Bearer ${this.options.apiKey}`,
2750
+ // OpenRouter requires HTTP-Referer for API access
2751
+ "HTTP-Referer": "https://github.com/nicepkg/agent-sdk"
2752
+ }
2570
2753
  });
2571
2754
  if (!res.ok) {
2572
2755
  return [];
2573
2756
  }
2574
2757
  const body = await res.json();
2575
- if (!body.data || body.data.length === 0) {
2576
- return [];
2758
+ if (body.data && Array.isArray(body.data)) {
2759
+ return body.data.filter((m) => typeof m.id === "string").map((m) => ({
2760
+ id: m.id,
2761
+ ...typeof m.name === "string" && { name: m.name },
2762
+ ...typeof m.description === "string" && { description: m.description },
2763
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
2764
+ }));
2765
+ }
2766
+ if (Array.isArray(body)) {
2767
+ return body.filter((m) => typeof m.id === "string").map((m) => ({
2768
+ id: m.id,
2769
+ ...typeof m.name === "string" && { name: m.name },
2770
+ ...typeof m.description === "string" && { description: m.description },
2771
+ ...typeof m.context_length === "number" && { contextWindow: m.context_length }
2772
+ }));
2577
2773
  }
2578
- return body.data.map((m) => ({ id: m.id }));
2774
+ return [];
2579
2775
  } catch {
2580
2776
  return [];
2581
2777
  }
@@ -2606,272 +2802,25 @@ var init_vercel_ai = __esm({
2606
2802
  }
2607
2803
  });
2608
2804
 
2609
- // src/registry.ts
2610
- function registerBackend(name, factory) {
2611
- if (registry.has(name)) {
2612
- throw new BackendAlreadyRegisteredError(name);
2613
- }
2614
- registry.set(name, { factory, builtin: false });
2615
- }
2616
- function unregisterBackend(name) {
2617
- return registry.delete(name);
2805
+ // src/chat/backends/types.ts
2806
+ function isResumableBackend(adapter) {
2807
+ return "canResume" in adapter && typeof adapter.canResume === "function";
2618
2808
  }
2619
- function hasBackend(name) {
2620
- return registry.has(name) || isBuiltinName(name);
2621
- }
2622
- function listBackends() {
2623
- const names = new Set(registry.keys());
2624
- for (const builtin of BUILTIN_BACKENDS) {
2625
- names.add(builtin);
2626
- }
2627
- return [...names];
2628
- }
2629
- function resetRegistry() {
2630
- registry.clear();
2631
- }
2632
- function isBuiltinName(name) {
2633
- return BUILTIN_BACKENDS.has(name);
2634
- }
2635
- async function loadBuiltinFactory(name) {
2636
- switch (name) {
2637
- case "copilot": {
2638
- const mod = await Promise.resolve().then(() => (init_copilot(), copilot_exports));
2639
- return (opts) => mod.createCopilotService(opts);
2640
- }
2641
- case "claude": {
2642
- const mod = await Promise.resolve().then(() => (init_claude(), claude_exports));
2643
- return (opts) => mod.createClaudeService(opts);
2644
- }
2645
- case "vercel-ai": {
2646
- const mod = await Promise.resolve().then(() => (init_vercel_ai(), vercel_ai_exports));
2647
- return (opts) => mod.createVercelAIService(opts);
2648
- }
2649
- }
2650
- }
2651
- async function createAgentService(name, options) {
2652
- const entry = registry.get(name);
2653
- if (entry) {
2654
- return entry.factory(options);
2655
- }
2656
- if (isBuiltinName(name)) {
2657
- const factory = await loadBuiltinFactory(name);
2658
- registry.set(name, { factory, builtin: true });
2659
- return factory(options);
2660
- }
2661
- throw new BackendNotFoundError(name);
2662
- }
2663
- var registry, BUILTIN_BACKENDS;
2664
- var init_registry = __esm({
2665
- "src/registry.ts"() {
2666
- init_errors();
2667
- registry = /* @__PURE__ */ new Map();
2668
- BUILTIN_BACKENDS = /* @__PURE__ */ new Set([
2669
- "copilot",
2670
- "claude",
2671
- "vercel-ai"
2672
- ]);
2673
- }
2674
- });
2675
-
2676
- // src/utils/messages.ts
2677
- function messagesToPrompt(messages) {
2678
- return messages.map((msg) => {
2679
- switch (msg.role) {
2680
- case "user":
2681
- return contentToText(msg.content);
2682
- case "assistant":
2683
- return contentToText(msg.content);
2684
- case "system":
2685
- return msg.content;
2686
- case "tool":
2687
- return msg.content ?? "";
2688
- }
2689
- }).filter(Boolean).join("\n\n");
2690
- }
2691
- function contentToText(content) {
2692
- return getTextContent(content);
2693
- }
2694
- function buildSystemPrompt(base, schemaInstruction) {
2695
- if (!schemaInstruction) return base;
2696
- return `${base}
2697
2809
 
2698
- ${schemaInstruction}`;
2699
- }
2700
- var init_messages = __esm({
2701
- "src/utils/messages.ts"() {
2702
- init_types();
2703
- }
2704
- });
2705
- function createDefaultPermissionStore(projectDir) {
2706
- const sessionStore = new InMemoryPermissionStore();
2707
- const projectPath = projectDir ? path__namespace.join(projectDir, ".agent-sdk", "permissions.json") : path__namespace.join(process.cwd(), ".agent-sdk", "permissions.json");
2708
- const userPath = path__namespace.join(os__namespace.homedir(), ".agent-sdk", "permissions.json");
2709
- const projectStore = new FilePermissionStore(projectPath);
2710
- const userStore = new FilePermissionStore(userPath);
2711
- return new CompositePermissionStore(sessionStore, projectStore, userStore);
2712
- }
2713
- var InMemoryPermissionStore, FilePermissionStore, CompositePermissionStore;
2714
- var init_permission_store = __esm({
2715
- "src/permission-store.ts"() {
2716
- InMemoryPermissionStore = class {
2717
- approvals = /* @__PURE__ */ new Map();
2718
- async isApproved(toolName) {
2719
- return this.approvals.has(toolName);
2720
- }
2721
- async approve(toolName, scope) {
2722
- if (scope === "once") return;
2723
- this.approvals.set(toolName, scope);
2724
- }
2725
- async revoke(toolName) {
2726
- this.approvals.delete(toolName);
2727
- }
2728
- async clear() {
2729
- this.approvals.clear();
2730
- }
2731
- async dispose() {
2732
- this.approvals.clear();
2733
- }
2734
- };
2735
- FilePermissionStore = class {
2736
- filePath;
2737
- constructor(filePath) {
2738
- this.filePath = path__namespace.resolve(filePath);
2739
- }
2740
- async isApproved(toolName) {
2741
- const data = this.readFile();
2742
- return toolName in data.approvals;
2743
- }
2744
- async approve(toolName, scope) {
2745
- if (scope === "once") return;
2746
- const data = this.readFile();
2747
- data.approvals[toolName] = { scope, timestamp: Date.now() };
2748
- this.writeFileAtomic(data);
2749
- }
2750
- async revoke(toolName) {
2751
- const data = this.readFile();
2752
- delete data.approvals[toolName];
2753
- this.writeFileAtomic(data);
2754
- }
2755
- async clear() {
2756
- this.writeFileAtomic({ approvals: {} });
2757
- }
2758
- async dispose() {
2759
- }
2760
- readFile() {
2761
- try {
2762
- const raw = fs__namespace.readFileSync(this.filePath, "utf-8");
2763
- const parsed = JSON.parse(raw);
2764
- if (parsed && typeof parsed.approvals === "object") return parsed;
2765
- } catch {
2766
- }
2767
- return { approvals: {} };
2768
- }
2769
- writeFileAtomic(data) {
2770
- const dir = path__namespace.dirname(this.filePath);
2771
- fs__namespace.mkdirSync(dir, { recursive: true });
2772
- const tmpPath = this.filePath + `.tmp.${process.pid}.${Date.now()}`;
2773
- fs__namespace.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
2774
- fs__namespace.renameSync(tmpPath, this.filePath);
2775
- }
2776
- };
2777
- CompositePermissionStore = class {
2778
- sessionStore;
2779
- projectStore;
2780
- userStore;
2781
- constructor(sessionStore, projectStore, userStore) {
2782
- this.sessionStore = sessionStore;
2783
- this.projectStore = projectStore;
2784
- this.userStore = userStore ?? projectStore;
2785
- }
2786
- async isApproved(toolName) {
2787
- return await this.sessionStore.isApproved(toolName) || await this.projectStore.isApproved(toolName) || await this.userStore.isApproved(toolName);
2788
- }
2789
- async approve(toolName, scope) {
2790
- if (scope === "once") return;
2791
- if (scope === "session") {
2792
- await this.sessionStore.approve(toolName, scope);
2793
- } else if (scope === "project") {
2794
- await this.projectStore.approve(toolName, scope);
2795
- } else {
2796
- await this.userStore.approve(toolName, scope);
2797
- }
2798
- }
2799
- async revoke(toolName) {
2800
- await this.sessionStore.revoke(toolName);
2801
- await this.projectStore.revoke(toolName);
2802
- await this.userStore.revoke(toolName);
2803
- }
2804
- async clear() {
2805
- await this.sessionStore.clear();
2806
- await this.projectStore.clear();
2807
- await this.userStore.clear();
2808
- }
2809
- async dispose() {
2810
- await this.sessionStore.dispose();
2811
- await this.projectStore.dispose();
2812
- if (this.userStore !== this.projectStore) {
2813
- await this.userStore.dispose();
2814
- }
2815
- }
2816
- };
2817
- }
2818
- });
2819
-
2820
- // src/index.ts
2821
- var src_exports = {};
2822
- __export(src_exports, {
2823
- AbortError: () => AbortError,
2824
- AgentSDKError: () => AgentSDKError,
2825
- BackendAlreadyRegisteredError: () => BackendAlreadyRegisteredError,
2826
- BackendNotFoundError: () => BackendNotFoundError,
2827
- BaseAgent: () => BaseAgent,
2828
- CompositePermissionStore: () => CompositePermissionStore,
2829
- DependencyError: () => DependencyError,
2830
- DisposedError: () => DisposedError,
2831
- FilePermissionStore: () => FilePermissionStore,
2832
- InMemoryPermissionStore: () => InMemoryPermissionStore,
2833
- ReentrancyError: () => ReentrancyError,
2834
- StructuredOutputError: () => StructuredOutputError,
2835
- SubprocessError: () => SubprocessError,
2836
- ToolExecutionError: () => ToolExecutionError,
2837
- buildSystemPrompt: () => buildSystemPrompt,
2838
- contentToText: () => contentToText,
2839
- createAgentService: () => createAgentService,
2840
- createDefaultPermissionStore: () => createDefaultPermissionStore,
2841
- getTextContent: () => getTextContent,
2842
- hasBackend: () => hasBackend,
2843
- isMultiPartContent: () => isMultiPartContent,
2844
- isTextContent: () => isTextContent,
2845
- isToolDefinition: () => isToolDefinition,
2846
- listBackends: () => listBackends,
2847
- messagesToPrompt: () => messagesToPrompt,
2848
- registerBackend: () => registerBackend,
2849
- resetRegistry: () => resetRegistry,
2850
- unregisterBackend: () => unregisterBackend,
2851
- zodToJsonSchema: () => zodToJsonSchema
2852
- });
2853
- var init_src = __esm({
2854
- "src/index.ts"() {
2855
- init_types();
2856
- init_errors();
2857
- init_registry();
2858
- init_base_agent();
2859
- init_schema();
2860
- init_messages();
2861
- init_permission_store();
2862
- }
2863
- });
2864
-
2865
- // src/chat/core.ts
2810
+ // src/chat/types.ts
2866
2811
  function createChatId() {
2867
2812
  return crypto.randomUUID();
2868
2813
  }
2814
+
2815
+ // src/chat/chat-utils.ts
2869
2816
  function getMessageText(message) {
2870
2817
  return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
2871
2818
  }
2872
2819
  function getMessageToolCalls(message) {
2873
2820
  return message.parts.filter((p) => p.type === "tool_call");
2874
2821
  }
2822
+
2823
+ // src/chat/bridge.ts
2875
2824
  function agentEventToChatEvent(event, messageId) {
2876
2825
  switch (event.type) {
2877
2826
  case "text_delta":
@@ -2924,6 +2873,7 @@ function agentEventToChatEvent(event, messageId) {
2924
2873
  type: "error",
2925
2874
  error: event.error,
2926
2875
  recoverable: event.recoverable,
2876
+ code: event.code,
2927
2877
  messageId
2928
2878
  };
2929
2879
  case "heartbeat":
@@ -2945,6 +2895,8 @@ async function* adaptAgentEvents(events, messageId) {
2945
2895
  }
2946
2896
  }
2947
2897
  }
2898
+
2899
+ // src/chat/conversion.ts
2948
2900
  function toAgentMessage(message) {
2949
2901
  const textContent = getMessageText(message);
2950
2902
  const toolCallParts = getMessageToolCalls(message);
@@ -2965,6 +2917,7 @@ function toAgentMessage(message) {
2965
2917
  }
2966
2918
 
2967
2919
  // src/chat/errors.ts
2920
+ init_errors2();
2968
2921
  init_errors();
2969
2922
  var ChatError = class extends AgentSDKError {
2970
2923
  code;
@@ -2972,7 +2925,11 @@ var ChatError = class extends AgentSDKError {
2972
2925
  retryAfter;
2973
2926
  timestamp;
2974
2927
  constructor(message, options) {
2975
- super(message, { cause: options.cause });
2928
+ super(message, {
2929
+ cause: options.cause,
2930
+ code: options.code,
2931
+ retryable: options.retryable
2932
+ });
2976
2933
  this.name = "ChatError";
2977
2934
  this.code = options.code;
2978
2935
  this.retryable = options.retryable ?? false;
@@ -2984,25 +2941,51 @@ var ChatError = class extends AgentSDKError {
2984
2941
  // src/chat/backends/base.ts
2985
2942
  var BaseBackendAdapter = class {
2986
2943
  name;
2987
- _agentService;
2988
- _agent = null;
2944
+ _agentService = null;
2945
+ _agentServiceFactory = null;
2989
2946
  _disposed = false;
2990
2947
  _agentConfig;
2991
2948
  _ownsService;
2949
+ // Agent lifecycle: tracks current agent and the model it was created with.
2950
+ // For persistent sessions, reused across calls when model matches.
2951
+ // For non-persistent, recreated every call.
2952
+ _currentAgent = null;
2992
2953
  constructor(name, options) {
2993
2954
  this.name = name;
2994
2955
  this._agentConfig = options.agentConfig;
2995
2956
  if (options.agentService) {
2996
2957
  this._agentService = options.agentService;
2997
2958
  this._ownsService = false;
2959
+ } else if (options.agentServiceFactory) {
2960
+ this._agentServiceFactory = options.agentServiceFactory;
2961
+ this._ownsService = true;
2998
2962
  } else {
2999
2963
  this._agentService = this.createService();
3000
2964
  this._ownsService = true;
3001
2965
  }
3002
2966
  }
3003
2967
  get agentService() {
2968
+ if (!this._agentService) {
2969
+ if (this._agentServiceFactory) {
2970
+ this._agentService = this._agentServiceFactory();
2971
+ this._agentServiceFactory = null;
2972
+ } else {
2973
+ throw new ChatError("Agent service not available", {
2974
+ code: "BACKEND_NOT_INSTALLED" /* BACKEND_NOT_INSTALLED */
2975
+ });
2976
+ }
2977
+ }
3004
2978
  return this._agentService;
3005
2979
  }
2980
+ get currentModel() {
2981
+ return this._agentConfig.model;
2982
+ }
2983
+ /**
2984
+ * @deprecated No-op. Tools are passed per-call via SendMessageOptions.tools.
2985
+ * Kept for backward compatibility with code that calls setTools() directly.
2986
+ */
2987
+ setTools() {
2988
+ }
3006
2989
  async sendMessage(session, message, options) {
3007
2990
  this.assertNotDisposed();
3008
2991
  const events = this.streamMessage(session, message, options);
@@ -3040,9 +3023,13 @@ var BaseBackendAdapter = class {
3040
3023
  */
3041
3024
  async *streamAgentEvents(agent, messages, options) {
3042
3025
  const messageId = createChatId();
3026
+ const model = options?.model ?? this._agentConfig.model ?? "";
3043
3027
  const agentEvents = agent.streamWithContext(messages, {
3028
+ model,
3044
3029
  signal: options?.signal,
3045
- context: options?.context
3030
+ context: options?.context,
3031
+ tools: options?.tools,
3032
+ ...options?.systemPrompt ? { systemMessage: options.systemPrompt } : {}
3046
3033
  });
3047
3034
  yield { type: "message:start", messageId, role: "assistant" };
3048
3035
  let text = "";
@@ -3068,31 +3055,45 @@ var BaseBackendAdapter = class {
3068
3055
  }
3069
3056
  async listModels() {
3070
3057
  this.assertNotDisposed();
3071
- return this._agentService.listModels();
3058
+ return this.agentService.listModels();
3072
3059
  }
3073
3060
  async validate() {
3074
3061
  this.assertNotDisposed();
3075
- return this._agentService.validate();
3062
+ return this.agentService.validate();
3076
3063
  }
3077
3064
  async dispose() {
3078
3065
  if (this._disposed) return;
3079
3066
  this._disposed = true;
3080
- this._agent?.dispose();
3081
- this._agent = null;
3082
- if (this._ownsService) {
3067
+ if (this._currentAgent) {
3068
+ this._currentAgent.instance.dispose();
3069
+ this._currentAgent = null;
3070
+ }
3071
+ if (this._ownsService && this._agentService && typeof this._agentService.dispose === "function") {
3083
3072
  await this._agentService.dispose();
3084
3073
  }
3085
3074
  }
3086
- /** Get or create an agent, applying model override from options */
3075
+ /** Get or create an agent. Model is passed per-call via RunOptions.
3076
+ * Tools are passed per-call via SendMessageOptions — not baked into config.
3077
+ * For persistent sessions, reuses agent when model matches. */
3087
3078
  getOrCreateAgent(options) {
3088
- const config = options?.model ? { ...this._agentConfig, model: options.model } : this._agentConfig;
3089
- if (this._agentConfig.sessionMode === "persistent" && this._agent) {
3090
- return this._agent;
3079
+ const model = options?.model ?? this._agentConfig.model;
3080
+ if (this._agentConfig.sessionMode === "persistent" && this._currentAgent) {
3081
+ if (this._currentAgent.model === model) {
3082
+ return this._currentAgent.instance;
3083
+ }
3084
+ this._currentAgent.instance.dispose();
3085
+ this._currentAgent = null;
3091
3086
  }
3092
- const agent = this._agentService.createAgent(config);
3093
- if (this._agentConfig.sessionMode === "persistent") {
3094
- this._agent = agent;
3087
+ if (this._currentAgent) {
3088
+ this._currentAgent.instance.dispose();
3089
+ this._currentAgent = null;
3095
3090
  }
3091
+ const config = {
3092
+ ...this._agentConfig,
3093
+ ...model !== void 0 && { model }
3094
+ };
3095
+ const agent = this.agentService.createAgent(config);
3096
+ this._currentAgent = { instance: agent, model };
3096
3097
  return agent;
3097
3098
  }
3098
3099
  assertNotDisposed() {
@@ -3117,8 +3118,8 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
3117
3118
  this._copilotOptions = options.copilotOptions;
3118
3119
  }
3119
3120
  createService() {
3120
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3121
- return createAgentService2("copilot", this._copilotOptions);
3121
+ const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
3122
+ return createCopilotService2(this._copilotOptions || {});
3122
3123
  }
3123
3124
  get backendSessionId() {
3124
3125
  return this._backendSessionId;
@@ -3170,8 +3171,8 @@ var ClaudeChatAdapter = class extends BaseBackendAdapter {
3170
3171
  this._claudeOptions = options.claudeOptions;
3171
3172
  }
3172
3173
  createService() {
3173
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3174
- return createAgentService2("claude", this._claudeOptions);
3174
+ const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
3175
+ return createClaudeService2(this._claudeOptions || {});
3175
3176
  }
3176
3177
  get backendSessionId() {
3177
3178
  return this._backendSessionId;
@@ -3218,20 +3219,8 @@ var VercelAIChatAdapter = class extends BaseBackendAdapter {
3218
3219
  this._vercelOptions = options.vercelOptions;
3219
3220
  }
3220
3221
  createService() {
3221
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3222
- return createAgentService2("vercel-ai", this._vercelOptions);
3223
- }
3224
- get backendSessionId() {
3225
- return null;
3226
- }
3227
- canResume() {
3228
- return false;
3229
- }
3230
- async *resume(_session, _backendSessionId, _options) {
3231
- throw new ChatError(
3232
- "Vercel AI adapter does not support session resume (stateless)",
3233
- { code: "PROVIDER_ERROR" /* PROVIDER_ERROR */ }
3234
- );
3222
+ const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
3223
+ return createVercelAIService2(this._vercelOptions || {});
3235
3224
  }
3236
3225
  captureSessionId(_agent) {
3237
3226
  }
@@ -3412,9 +3401,9 @@ var InProcessChatTransport = class {
3412
3401
  send(event) {
3413
3402
  if (!this._open) return;
3414
3403
  if (this._resolve) {
3415
- const resolve2 = this._resolve;
3404
+ const resolve = this._resolve;
3416
3405
  this._resolve = null;
3417
- resolve2({ value: event, done: false });
3406
+ resolve({ value: event, done: false });
3418
3407
  } else {
3419
3408
  this._buffer.push(event);
3420
3409
  }
@@ -3423,9 +3412,9 @@ var InProcessChatTransport = class {
3423
3412
  if (!this._open) return;
3424
3413
  this._open = false;
3425
3414
  if (this._resolve) {
3426
- const resolve2 = this._resolve;
3415
+ const resolve = this._resolve;
3427
3416
  this._resolve = null;
3428
- resolve2({ value: void 0, done: true });
3417
+ resolve({ value: void 0, done: true });
3429
3418
  }
3430
3419
  }
3431
3420
  error(err) {
@@ -3437,9 +3426,9 @@ var InProcessChatTransport = class {
3437
3426
  recoverable: false
3438
3427
  };
3439
3428
  if (this._resolve) {
3440
- const resolve2 = this._resolve;
3429
+ const resolve = this._resolve;
3441
3430
  this._resolve = null;
3442
- resolve2({ value: errorEvent, done: false });
3431
+ resolve({ value: errorEvent, done: false });
3443
3432
  } else {
3444
3433
  this._error = err;
3445
3434
  }
@@ -3464,8 +3453,8 @@ var InProcessChatTransport = class {
3464
3453
  if (!this._open) {
3465
3454
  return Promise.resolve({ value: void 0, done: true });
3466
3455
  }
3467
- return new Promise((resolve2) => {
3468
- this._resolve = resolve2;
3456
+ return new Promise((resolve) => {
3457
+ this._resolve = resolve;
3469
3458
  });
3470
3459
  }
3471
3460
  };
@@ -3528,6 +3517,7 @@ exports.SSEChatTransport = SSEChatTransport;
3528
3517
  exports.VercelAIChatAdapter = VercelAIChatAdapter;
3529
3518
  exports.WS_READY_STATE = WS_READY_STATE;
3530
3519
  exports.WsChatTransport = WsChatTransport;
3520
+ exports.isResumableBackend = isResumableBackend;
3531
3521
  exports.streamToTransport = streamToTransport;
3532
3522
  exports.withInterceptors = withInterceptors;
3533
3523
  //# sourceMappingURL=backends.cjs.map