@witqq/agent-sdk 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
  2. package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -102
  3. package/dist/auth/index.cjs +72 -1
  4. package/dist/auth/index.cjs.map +1 -1
  5. package/dist/auth/index.d.cts +21 -154
  6. package/dist/auth/index.d.ts +21 -154
  7. package/dist/auth/index.js +72 -1
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/backends/claude.cjs +480 -261
  10. package/dist/backends/claude.cjs.map +1 -1
  11. package/dist/backends/claude.d.cts +3 -1
  12. package/dist/backends/claude.d.ts +3 -1
  13. package/dist/backends/claude.js +480 -261
  14. package/dist/backends/claude.js.map +1 -1
  15. package/dist/backends/copilot.cjs +337 -112
  16. package/dist/backends/copilot.cjs.map +1 -1
  17. package/dist/backends/copilot.d.cts +12 -4
  18. package/dist/backends/copilot.d.ts +12 -4
  19. package/dist/backends/copilot.js +337 -112
  20. package/dist/backends/copilot.js.map +1 -1
  21. package/dist/backends/mock-llm.cjs +719 -0
  22. package/dist/backends/mock-llm.cjs.map +1 -0
  23. package/dist/backends/mock-llm.d.cts +37 -0
  24. package/dist/backends/mock-llm.d.ts +37 -0
  25. package/dist/backends/mock-llm.js +717 -0
  26. package/dist/backends/mock-llm.js.map +1 -0
  27. package/dist/backends/vercel-ai.cjs +301 -61
  28. package/dist/backends/vercel-ai.cjs.map +1 -1
  29. package/dist/backends/vercel-ai.d.cts +3 -1
  30. package/dist/backends/vercel-ai.d.ts +3 -1
  31. package/dist/backends/vercel-ai.js +301 -61
  32. package/dist/backends/vercel-ai.js.map +1 -1
  33. package/dist/backends-Cno0gZjy.d.cts +114 -0
  34. package/dist/backends-Cno0gZjy.d.ts +114 -0
  35. package/dist/chat/accumulator.cjs +1 -1
  36. package/dist/chat/accumulator.cjs.map +1 -1
  37. package/dist/chat/accumulator.d.cts +5 -2
  38. package/dist/chat/accumulator.d.ts +5 -2
  39. package/dist/chat/accumulator.js +1 -1
  40. package/dist/chat/accumulator.js.map +1 -1
  41. package/dist/chat/backends.cjs +1084 -821
  42. package/dist/chat/backends.cjs.map +1 -1
  43. package/dist/chat/backends.d.cts +10 -6
  44. package/dist/chat/backends.d.ts +10 -6
  45. package/dist/chat/backends.js +1082 -800
  46. package/dist/chat/backends.js.map +1 -1
  47. package/dist/chat/context.cjs +50 -0
  48. package/dist/chat/context.cjs.map +1 -1
  49. package/dist/chat/context.d.cts +27 -3
  50. package/dist/chat/context.d.ts +27 -3
  51. package/dist/chat/context.js +50 -0
  52. package/dist/chat/context.js.map +1 -1
  53. package/dist/chat/core.cjs +60 -27
  54. package/dist/chat/core.cjs.map +1 -1
  55. package/dist/chat/core.d.cts +41 -382
  56. package/dist/chat/core.d.ts +41 -382
  57. package/dist/chat/core.js +58 -28
  58. package/dist/chat/core.js.map +1 -1
  59. package/dist/chat/errors.cjs +48 -26
  60. package/dist/chat/errors.cjs.map +1 -1
  61. package/dist/chat/errors.d.cts +6 -31
  62. package/dist/chat/errors.d.ts +6 -31
  63. package/dist/chat/errors.js +48 -25
  64. package/dist/chat/errors.js.map +1 -1
  65. package/dist/chat/events.cjs.map +1 -1
  66. package/dist/chat/events.d.cts +6 -2
  67. package/dist/chat/events.d.ts +6 -2
  68. package/dist/chat/events.js.map +1 -1
  69. package/dist/chat/index.cjs +1612 -1125
  70. package/dist/chat/index.cjs.map +1 -1
  71. package/dist/chat/index.d.cts +35 -10
  72. package/dist/chat/index.d.ts +35 -10
  73. package/dist/chat/index.js +1600 -1097
  74. package/dist/chat/index.js.map +1 -1
  75. package/dist/chat/react/theme.css +2517 -0
  76. package/dist/chat/react.cjs +2212 -1158
  77. package/dist/chat/react.cjs.map +1 -1
  78. package/dist/chat/react.d.cts +665 -122
  79. package/dist/chat/react.d.ts +665 -122
  80. package/dist/chat/react.js +2191 -1156
  81. package/dist/chat/react.js.map +1 -1
  82. package/dist/chat/runtime.cjs +405 -186
  83. package/dist/chat/runtime.cjs.map +1 -1
  84. package/dist/chat/runtime.d.cts +92 -28
  85. package/dist/chat/runtime.d.ts +92 -28
  86. package/dist/chat/runtime.js +405 -186
  87. package/dist/chat/runtime.js.map +1 -1
  88. package/dist/chat/server.cjs +2247 -212
  89. package/dist/chat/server.cjs.map +1 -1
  90. package/dist/chat/server.d.cts +451 -90
  91. package/dist/chat/server.d.ts +451 -90
  92. package/dist/chat/server.js +2234 -213
  93. package/dist/chat/server.js.map +1 -1
  94. package/dist/chat/sessions.cjs +64 -66
  95. package/dist/chat/sessions.cjs.map +1 -1
  96. package/dist/chat/sessions.d.cts +37 -118
  97. package/dist/chat/sessions.d.ts +37 -118
  98. package/dist/chat/sessions.js +65 -67
  99. package/dist/chat/sessions.js.map +1 -1
  100. package/dist/chat/sqlite.cjs +536 -0
  101. package/dist/chat/sqlite.cjs.map +1 -0
  102. package/dist/chat/sqlite.d.cts +164 -0
  103. package/dist/chat/sqlite.d.ts +164 -0
  104. package/dist/chat/sqlite.js +527 -0
  105. package/dist/chat/sqlite.js.map +1 -0
  106. package/dist/chat/state.cjs +14 -1
  107. package/dist/chat/state.cjs.map +1 -1
  108. package/dist/chat/state.d.cts +5 -2
  109. package/dist/chat/state.d.ts +5 -2
  110. package/dist/chat/state.js +14 -1
  111. package/dist/chat/state.js.map +1 -1
  112. package/dist/chat/storage.cjs +58 -33
  113. package/dist/chat/storage.cjs.map +1 -1
  114. package/dist/chat/storage.d.cts +18 -8
  115. package/dist/chat/storage.d.ts +18 -8
  116. package/dist/chat/storage.js +59 -34
  117. package/dist/chat/storage.js.map +1 -1
  118. package/dist/errors-C-so0M4t.d.cts +33 -0
  119. package/dist/errors-C-so0M4t.d.ts +33 -0
  120. package/dist/errors-CmVvczxZ.d.cts +28 -0
  121. package/dist/errors-CmVvczxZ.d.ts +28 -0
  122. package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-7EIit9Xk.d.ts} +72 -33
  123. package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
  124. package/dist/index.cjs +354 -60
  125. package/dist/index.cjs.map +1 -1
  126. package/dist/index.d.cts +294 -123
  127. package/dist/index.d.ts +294 -123
  128. package/dist/index.js +347 -60
  129. package/dist/index.js.map +1 -1
  130. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  131. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  132. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  133. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  134. package/dist/testing.cjs +1107 -0
  135. package/dist/testing.cjs.map +1 -0
  136. package/dist/testing.d.cts +144 -0
  137. package/dist/testing.d.ts +144 -0
  138. package/dist/testing.js +1101 -0
  139. package/dist/testing.js.map +1 -0
  140. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  141. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  142. package/dist/{transport-DX1Nhm4N.d.cts → transport-DLWCN18G.d.cts} +5 -4
  143. package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.d.ts} +5 -4
  144. package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
  145. package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
  146. package/dist/types-DgtI1hzh.d.ts +364 -0
  147. package/dist/types-DkSXALKg.d.cts +364 -0
  148. package/package.json +41 -5
  149. package/LICENSE +0 -21
  150. package/README.md +0 -948
  151. package/dist/errors-BDLbNu9w.d.cts +0 -13
  152. package/dist/errors-BDLbNu9w.d.ts +0 -13
  153. package/dist/types-DLZzlJxt.d.ts +0 -39
  154. package/dist/types-tE0CXwBl.d.cts +0 -39
@@ -1,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) => ({
@@ -491,17 +724,6 @@ function convertParameters(params) {
491
724
  }
492
725
  return params;
493
726
  }
494
- async function mapToolsToSDKAsync(tools) {
495
- return tools.map((tool) => ({
496
- name: tool.name,
497
- description: tool.description,
498
- parameters: convertParameters(tool.parameters),
499
- handler: async (args) => {
500
- const result = await tool.execute(args);
501
- return typeof result === "string" ? result : JSON.stringify(result);
502
- }
503
- }));
504
- }
505
727
  function buildPermissionHandler(config) {
506
728
  const onPermission = config.supervisor?.onPermission;
507
729
  if (!onPermission) {
@@ -516,6 +738,7 @@ function buildPermissionHandler(config) {
516
738
  const unifiedRequest = {
517
739
  toolName,
518
740
  toolArgs: { ...request },
741
+ toolCallId: request.toolCallId,
519
742
  rawSDKRequest: request
520
743
  };
521
744
  const ac = new AbortController();
@@ -620,15 +843,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
620
843
  };
621
844
  case "session.error":
622
845
  console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
623
- return {
624
- type: "error",
625
- error: String(data.message ?? "Unknown error"),
626
- recoverable: false
627
- };
846
+ {
847
+ const errorMsg = String(data.message ?? "Unknown error");
848
+ const code = classifyAgentError(errorMsg);
849
+ return {
850
+ type: "error",
851
+ error: errorMsg,
852
+ recoverable: isRecoverableErrorCode(code),
853
+ code
854
+ };
855
+ }
628
856
  case "assistant.message": {
629
857
  const doneEvent = {
630
858
  type: "done",
631
- finalOutput: data.content ? String(data.content) : null
859
+ finalOutput: null,
860
+ streamed: true
632
861
  };
633
862
  if (thinkingTracker.endThinking()) {
634
863
  return [{ type: "thinking_end" }, doneEvent];
@@ -639,66 +868,13 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
639
868
  return null;
640
869
  }
641
870
  }
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
871
  function withTimeout(promise, ms, message) {
696
- return new Promise((resolve2, reject) => {
872
+ return new Promise((resolve, reject) => {
697
873
  const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
698
874
  promise.then(
699
875
  (val) => {
700
876
  clearTimeout(timer);
701
- resolve2(val);
877
+ resolve(val);
702
878
  },
703
879
  (err) => {
704
880
  clearTimeout(timer);
@@ -710,14 +886,15 @@ function withTimeout(promise, ms, message) {
710
886
  function createCopilotService(options) {
711
887
  return new CopilotAgentService(options);
712
888
  }
713
- var sdkModule, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
889
+ var _sdkMock, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
714
890
  var init_copilot = __esm({
715
891
  "src/backends/copilot.ts"() {
716
- init_types();
892
+ init_types2();
717
893
  init_base_agent();
718
- init_errors();
894
+ init_errors2();
719
895
  init_schema();
720
- sdkModule = null;
896
+ init_shared();
897
+ _sdkMock = null;
721
898
  ToolCallTracker = class {
722
899
  map = /* @__PURE__ */ new Map();
723
900
  trackStart(toolCallId, toolName, args) {
@@ -765,6 +942,7 @@ var init_copilot = __esm({
765
942
  isPersistent;
766
943
  persistentSession = null;
767
944
  _sessionId;
945
+ _persistentModel;
768
946
  activeSession = null;
769
947
  _resumeSessionId;
770
948
  _toolsReady = null;
@@ -783,15 +961,15 @@ var init_copilot = __esm({
783
961
  },
784
962
  onPermissionRequest: buildPermissionHandler(config),
785
963
  onUserInputRequest: buildUserInputHandler(config),
786
- ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
964
+ ...config.availableTools ? { availableTools: config.availableTools } : {}
787
965
  };
788
966
  this._toolsReady = this._initToolsAsync(config);
789
967
  this._resumeSessionId = resumeSessionId;
790
968
  }
791
- /** Pre-convert Zod schemas to JSON Schema asynchronously.
969
+ /** Pre-convert Zod schemas to JSON Schema.
792
970
  * Updates sdkTools and sessionConfig.tools before first session creation. */
793
971
  async _initToolsAsync(config) {
794
- this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
972
+ this.sdkTools = mapToolsToSDK(config.tools ?? []);
795
973
  this.sessionConfig.tools = this.sdkTools;
796
974
  }
797
975
  get sessionId() {
@@ -815,47 +993,63 @@ var init_copilot = __esm({
815
993
  });
816
994
  this.persistentSession = null;
817
995
  this._sessionId = void 0;
996
+ this._persistentModel = void 0;
818
997
  }
819
998
  }
820
- async getOrCreateSession(streaming) {
999
+ async getOrCreateSession(streaming, options) {
821
1000
  if (this.isPersistent && this.persistentSession) {
822
- return { session: this.persistentSession, isNew: false };
1001
+ if (options.model !== this._persistentModel) {
1002
+ this.persistentSession.destroy().catch(() => {
1003
+ });
1004
+ this.persistentSession = null;
1005
+ this._sessionId = void 0;
1006
+ } else {
1007
+ return { session: this.persistentSession, isNew: false };
1008
+ }
823
1009
  }
824
1010
  if (this._toolsReady) {
825
1011
  await this._toolsReady;
826
1012
  this._toolsReady = null;
827
1013
  }
1014
+ const sessionConfig = { ...this.sessionConfig };
1015
+ sessionConfig.model = options.model;
1016
+ const resolvedTools = this.resolveTools(options);
1017
+ if (options?.tools) {
1018
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
1019
+ }
828
1020
  const client = await this.getClient();
829
1021
  if (this._resumeSessionId) {
830
1022
  const storedId = this._resumeSessionId;
831
1023
  this._resumeSessionId = void 0;
832
1024
  try {
833
1025
  const session2 = await client.resumeSession(storedId, {
834
- ...this.sessionConfig,
1026
+ ...sessionConfig,
835
1027
  streaming: this.isPersistent ? true : streaming
836
1028
  });
837
1029
  if (this.isPersistent) {
838
1030
  this.persistentSession = session2;
839
1031
  this._sessionId = session2.sessionId;
1032
+ this._persistentModel = options.model;
840
1033
  }
841
1034
  return { session: session2, isNew: false };
842
1035
  } catch {
843
1036
  }
844
1037
  }
845
1038
  const session = await client.createSession({
846
- ...this.sessionConfig,
1039
+ ...sessionConfig,
847
1040
  streaming: this.isPersistent ? true : streaming
848
1041
  });
849
1042
  if (this.isPersistent) {
850
1043
  this.persistentSession = session;
851
1044
  this._sessionId = session.sessionId;
1045
+ this._persistentModel = options.model;
852
1046
  }
853
1047
  return { session, isNew: true };
854
1048
  }
855
1049
  // ─── executeRun ─────────────────────────────────────────────────
856
- async executeRun(messages, _options, signal) {
1050
+ async executeRun(messages, options, signal) {
857
1051
  this.checkAbort(signal);
858
- const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
1052
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
859
1053
  this.activeSession = session;
860
1054
  const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
861
1055
  const tracker = new ToolCallTracker();
@@ -952,9 +1146,9 @@ You MUST respond with ONLY valid JSON matching this schema:
952
1146
  };
953
1147
  }
954
1148
  // ─── executeStream ──────────────────────────────────────────────
955
- async *executeStream(messages, _options, signal) {
1149
+ async *executeStream(messages, options, signal) {
956
1150
  this.checkAbort(signal);
957
- const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
1151
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
958
1152
  this.activeSession = session;
959
1153
  if (isNewSession) {
960
1154
  yield this.emitSessionInfo(session.sessionId);
@@ -971,8 +1165,8 @@ You MUST respond with ONLY valid JSON matching this schema:
971
1165
  notify = null;
972
1166
  }
973
1167
  };
974
- const waitForItem = () => new Promise((resolve2) => {
975
- notify = resolve2;
1168
+ const waitForItem = () => new Promise((resolve) => {
1169
+ notify = resolve;
976
1170
  });
977
1171
  const unsubscribe = session.on((event) => {
978
1172
  const mapped = mapSessionEvent(event, tracker, thinkingTracker);
@@ -1066,7 +1260,11 @@ You MUST respond with ONLY valid JSON matching this schema:
1066
1260
  githubToken: this.options.githubToken,
1067
1261
  useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
1068
1262
  ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
1069
- ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
1263
+ env: {
1264
+ ...process.env,
1265
+ ...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
1266
+ ...this.options.env
1267
+ }
1070
1268
  });
1071
1269
  const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
1072
1270
  await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
@@ -1100,7 +1298,10 @@ You MUST respond with ONLY valid JSON matching this schema:
1100
1298
  return models.map((m) => ({
1101
1299
  id: m.id,
1102
1300
  name: m.name,
1103
- provider: "copilot"
1301
+ provider: "copilot",
1302
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1303
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1304
+ }
1104
1305
  }));
1105
1306
  }
1106
1307
  async validate() {
@@ -1153,10 +1354,9 @@ function stripMcpPrefix(name) {
1153
1354
  return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
1154
1355
  }
1155
1356
  async function loadSDK2() {
1156
- if (sdkModule2) return sdkModule2;
1357
+ if (_sdkMock2) return _sdkMock2;
1157
1358
  try {
1158
- sdkModule2 = await import('@anthropic-ai/claude-agent-sdk');
1159
- return sdkModule2;
1359
+ return await import('@anthropic-ai/claude-agent-sdk');
1160
1360
  } catch {
1161
1361
  throw new SubprocessError(
1162
1362
  "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
@@ -1164,13 +1364,32 @@ async function loadSDK2() {
1164
1364
  }
1165
1365
  }
1166
1366
  function _injectSDK2(mock) {
1167
- sdkModule2 = mock;
1367
+ _sdkMock2 = mock;
1168
1368
  }
1169
1369
  function _resetSDK2() {
1170
- sdkModule2 = null;
1370
+ _sdkMock2 = null;
1171
1371
  }
1172
- function buildMcpServer(sdk, tools, toolResultCapture) {
1173
- if (tools.length === 0) return void 0;
1372
+ function normalizeAskUserInput(args) {
1373
+ if (typeof args.question === "string") {
1374
+ return {
1375
+ question: args.question,
1376
+ choices: Array.isArray(args.choices) ? args.choices : void 0,
1377
+ allowFreeform: args.allowFreeform !== false
1378
+ };
1379
+ }
1380
+ const questions = args.questions;
1381
+ if (questions && questions.length > 0) {
1382
+ const first = questions[0];
1383
+ return {
1384
+ question: first.question,
1385
+ choices: first.options?.map((o) => o.label),
1386
+ allowFreeform: true
1387
+ };
1388
+ }
1389
+ return { question: JSON.stringify(args), allowFreeform: true };
1390
+ }
1391
+ function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
1392
+ if (tools.length === 0 && !onAskUser) return void 0;
1174
1393
  const mcpTools = tools.map((tool) => {
1175
1394
  const zodSchema = tool.parameters;
1176
1395
  const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
@@ -1194,6 +1413,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
1194
1413
  }
1195
1414
  );
1196
1415
  });
1416
+ if (onAskUser) {
1417
+ const askUserTool = sdk.tool(
1418
+ "ask_user",
1419
+ "Ask the user a question and wait for their response",
1420
+ {
1421
+ question: { type: "string", description: "The question to ask the user" },
1422
+ choices: {
1423
+ type: "array",
1424
+ items: { type: "string" },
1425
+ description: "Optional list of choices for multiple choice"
1426
+ },
1427
+ questions: {
1428
+ type: "array",
1429
+ items: {
1430
+ type: "object",
1431
+ properties: {
1432
+ question: { type: "string" },
1433
+ options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
1434
+ }
1435
+ },
1436
+ description: "Alternative nested question format"
1437
+ }
1438
+ },
1439
+ async (args) => {
1440
+ const normalized = normalizeAskUserInput(args);
1441
+ const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
1442
+ return {
1443
+ content: [{ type: "text", text: response.answer }]
1444
+ };
1445
+ }
1446
+ );
1447
+ mcpTools.push(askUserTool);
1448
+ }
1197
1449
  return sdk.createSdkMcpServer({
1198
1450
  name: MCP_SERVER_NAME,
1199
1451
  version: "1.0.0",
@@ -1243,6 +1495,7 @@ function buildCanUseTool(config) {
1243
1495
  const unifiedRequest = {
1244
1496
  toolName,
1245
1497
  toolArgs: input,
1498
+ toolCallId: options.toolUseID,
1246
1499
  suggestedScope: extractSuggestedScope(options.suggestions),
1247
1500
  rawSDKRequest: { toolName, input, ...options }
1248
1501
  };
@@ -1295,6 +1548,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1295
1548
  if (block.type === "tool_use") {
1296
1549
  const toolCallId = String(block.id ?? "");
1297
1550
  const toolName = stripMcpPrefix(block.name ?? "unknown");
1551
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1298
1552
  if (toolCallTracker) {
1299
1553
  toolCallTracker.trackStart(toolCallId, toolName);
1300
1554
  }
@@ -1318,6 +1572,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1318
1572
  case "tool_use_summary": {
1319
1573
  const summary = msg.summary;
1320
1574
  const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
1575
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
1321
1576
  const precedingIds = msg.preceding_tool_use_ids;
1322
1577
  let toolCallId = "";
1323
1578
  if (precedingIds && precedingIds.length > 0) {
@@ -1371,10 +1626,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1371
1626
  }
1372
1627
  if (msg.is_error) {
1373
1628
  const r = msg;
1629
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
1630
+ const code = classifyAgentError(errorMsg);
1374
1631
  return {
1375
1632
  type: "error",
1376
- error: r.errors?.join("; ") ?? "Unknown error",
1377
- recoverable: false
1633
+ error: errorMsg,
1634
+ recoverable: isRecoverableErrorCode(code),
1635
+ code
1378
1636
  };
1379
1637
  }
1380
1638
  return null;
@@ -1383,72 +1641,21 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1383
1641
  return null;
1384
1642
  }
1385
1643
  }
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})`;
1644
+ function createClaudeService(options) {
1645
+ return new ClaudeAgentService(options);
1398
1646
  }
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
- function createClaudeService(options) {
1440
- return new ClaudeAgentService(options);
1441
- }
1442
- var MCP_SERVER_NAME, MCP_TOOL_PREFIX, sdkModule2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1647
+ 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
1648
  var init_claude = __esm({
1444
1649
  "src/backends/claude.ts"() {
1445
- init_types();
1650
+ init_types2();
1446
1651
  init_base_agent();
1447
- init_errors();
1652
+ init_errors2();
1448
1653
  init_schema();
1654
+ init_shared();
1449
1655
  MCP_SERVER_NAME = "agent-sdk-tools";
1450
1656
  MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
1451
- sdkModule2 = null;
1657
+ CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1658
+ _sdkMock2 = null;
1452
1659
  ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
1453
1660
  ANTHROPIC_API_VERSION = "2023-06-01";
1454
1661
  ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
@@ -1493,11 +1700,6 @@ var init_claude = __esm({
1493
1700
  if (options.resumeSessionId) {
1494
1701
  this._sessionId = options.resumeSessionId;
1495
1702
  }
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
1703
  }
1502
1704
  get sessionId() {
1503
1705
  return this._sessionId;
@@ -1521,12 +1723,12 @@ var init_claude = __esm({
1521
1723
  const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
1522
1724
  return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
1523
1725
  }
1524
- buildQueryOptions(signal) {
1726
+ buildQueryOptions(signal, options) {
1525
1727
  const ac = new AbortController();
1526
1728
  signal.addEventListener("abort", () => ac.abort(), { once: true });
1527
1729
  const opts = {
1528
1730
  abortController: ac,
1529
- model: this.config.model,
1731
+ model: options.model,
1530
1732
  maxTurns: this.options.maxTurns,
1531
1733
  cwd: this.options.workingDirectory,
1532
1734
  pathToClaudeCodeExecutable: this.options.cliPath,
@@ -1556,25 +1758,85 @@ var init_claude = __esm({
1556
1758
  return opts;
1557
1759
  }
1558
1760
  async buildMcpConfig(opts, toolResultCapture) {
1559
- if (this.tools.length === 0) return opts;
1761
+ const onAskUser = this.config.supervisor?.onAskUser;
1762
+ if (this.tools.length === 0 && !onAskUser) return opts;
1560
1763
  const sdk = await loadSDK2();
1561
- const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
1764
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
1562
1765
  if (mcpServer) {
1563
1766
  opts.mcpServers = {
1564
1767
  [MCP_SERVER_NAME]: mcpServer
1565
1768
  };
1566
1769
  const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1770
+ if (onAskUser) {
1771
+ mcpToolNames.push(mcpToolName("ask_user"));
1772
+ }
1567
1773
  opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
1568
1774
  }
1775
+ if (onAskUser) {
1776
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1777
+ }
1569
1778
  return opts;
1570
1779
  }
1780
+ // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
1781
+ /** Setup a retry query: clear session, rebuild with full history */
1782
+ async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
1783
+ this.clearPersistentSession();
1784
+ const retryPrompt = buildContextualPrompt(messages);
1785
+ let retryOpts = this.buildQueryOptions(signal, options);
1786
+ toolResultCapture.clear();
1787
+ retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1788
+ modifyOpts?.(retryOpts);
1789
+ const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1790
+ this.activeQuery = retryQ;
1791
+ return retryQ;
1792
+ }
1793
+ /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
1794
+ collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
1795
+ if (msg.type !== "assistant") return;
1796
+ const betaMessage = msg.message;
1797
+ if (!betaMessage?.content) return;
1798
+ for (const block of betaMessage.content) {
1799
+ if (block.type === "tool_use") {
1800
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1801
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1802
+ toolCalls.push({
1803
+ toolName,
1804
+ args: block.input ?? {},
1805
+ result: toolResultCapture.get(toolName) ?? null,
1806
+ approved: true
1807
+ });
1808
+ }
1809
+ }
1810
+ }
1811
+ /** Back-fill tool results from capture map on summary/result messages */
1812
+ backfillToolResults(msg, toolCalls, toolResultCapture) {
1813
+ if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
1814
+ for (const tc of toolCalls) {
1815
+ if (tc.result === null) {
1816
+ const captured = toolResultCapture.get(tc.toolName);
1817
+ if (captured !== void 0) tc.result = captured;
1818
+ }
1819
+ }
1820
+ }
1821
+ /** Wrap retry inner loop with shared error handling */
1822
+ async withRetryErrorHandling(signal, fn) {
1823
+ try {
1824
+ return await fn();
1825
+ } catch (retryError) {
1826
+ if (this.isPersistent) this.clearPersistentSession();
1827
+ if (signal.aborted) throw new AbortError();
1828
+ throw retryError;
1829
+ } finally {
1830
+ this.activeQuery = null;
1831
+ }
1832
+ }
1571
1833
  // ─── executeRun ─────────────────────────────────────────────────
1572
- async executeRun(messages, _options, signal) {
1834
+ async executeRun(messages, options, signal) {
1573
1835
  this.checkAbort(signal);
1574
1836
  const sdk = await loadSDK2();
1575
1837
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1576
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1577
- let opts = this.buildQueryOptions(signal);
1838
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1839
+ let opts = this.buildQueryOptions(signal, options);
1578
1840
  const toolResultCapture = /* @__PURE__ */ new Map();
1579
1841
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1580
1842
  const q = sdk.query({ prompt, options: opts });
@@ -1584,30 +1846,8 @@ var init_claude = __esm({
1584
1846
  let usage;
1585
1847
  try {
1586
1848
  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
- }
1849
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1850
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1611
1851
  if (msg.type === "result") {
1612
1852
  if (msg.subtype === "success") {
1613
1853
  const r = msg;
@@ -1627,41 +1867,13 @@ var init_claude = __esm({
1627
1867
  } catch (e) {
1628
1868
  if (signal.aborted) throw new AbortError();
1629
1869
  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;
1870
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1637
1871
  toolCalls.length = 0;
1638
1872
  output = null;
1639
- try {
1873
+ return this.withRetryErrorHandling(signal, async () => {
1640
1874
  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
- }
1875
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1876
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1665
1877
  if (msg.type === "result") {
1666
1878
  if (msg.subtype === "success") {
1667
1879
  const r = msg;
@@ -1678,23 +1890,17 @@ var init_claude = __esm({
1678
1890
  }
1679
1891
  }
1680
1892
  }
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
- };
1893
+ return {
1894
+ output,
1895
+ structuredOutput: void 0,
1896
+ toolCalls,
1897
+ messages: [
1898
+ ...messages,
1899
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1900
+ ],
1901
+ usage
1902
+ };
1903
+ });
1698
1904
  }
1699
1905
  if (this.isPersistent) this.clearPersistentSession();
1700
1906
  throw e;
@@ -1713,12 +1919,12 @@ var init_claude = __esm({
1713
1919
  };
1714
1920
  }
1715
1921
  // ─── executeRunStructured ───────────────────────────────────────
1716
- async executeRunStructured(messages, schema, _options, signal) {
1922
+ async executeRunStructured(messages, schema, options, signal) {
1717
1923
  this.checkAbort(signal);
1718
1924
  const sdk = await loadSDK2();
1719
1925
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1720
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1721
- let opts = this.buildQueryOptions(signal);
1926
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1927
+ let opts = this.buildQueryOptions(signal, options);
1722
1928
  const toolResultCapture = /* @__PURE__ */ new Map();
1723
1929
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1724
1930
  const jsonSchema = zodToJsonSchema(schema.schema);
@@ -1734,30 +1940,8 @@ var init_claude = __esm({
1734
1940
  let usage;
1735
1941
  try {
1736
1942
  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
- }
1943
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1944
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1761
1945
  if (msg.type === "result" && msg.subtype === "success") {
1762
1946
  const r = msg;
1763
1947
  output = r.result;
@@ -1792,46 +1976,23 @@ var init_claude = __esm({
1792
1976
  } catch (e) {
1793
1977
  if (signal.aborted) throw new AbortError();
1794
1978
  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;
1979
+ const retryQ = await this.prepareRetryQuery(
1980
+ sdk,
1981
+ messages,
1982
+ signal,
1983
+ options,
1984
+ toolResultCapture,
1985
+ (opts2) => {
1986
+ opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
1987
+ }
1988
+ );
1806
1989
  toolCalls.length = 0;
1807
1990
  output = null;
1808
1991
  structuredOutput = void 0;
1809
- try {
1992
+ return this.withRetryErrorHandling(signal, async () => {
1810
1993
  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
- }
1994
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1995
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1835
1996
  if (msg.type === "result" && msg.subtype === "success") {
1836
1997
  const r = msg;
1837
1998
  output = r.result;
@@ -1863,23 +2024,17 @@ var init_claude = __esm({
1863
2024
  );
1864
2025
  }
1865
2026
  }
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
- };
2027
+ return {
2028
+ output,
2029
+ structuredOutput,
2030
+ toolCalls,
2031
+ messages: [
2032
+ ...messages,
2033
+ ...output !== null ? [{ role: "assistant", content: output }] : []
2034
+ ],
2035
+ usage
2036
+ };
2037
+ });
1883
2038
  }
1884
2039
  if (this.isPersistent) this.clearPersistentSession();
1885
2040
  throw e;
@@ -1898,12 +2053,12 @@ var init_claude = __esm({
1898
2053
  };
1899
2054
  }
1900
2055
  // ─── executeStream ──────────────────────────────────────────────
1901
- async *executeStream(messages, _options, signal) {
2056
+ async *executeStream(messages, options, signal) {
1902
2057
  this.checkAbort(signal);
1903
2058
  const sdk = await loadSDK2();
1904
2059
  const isResuming = this.isPersistent && this._sessionId !== void 0;
1905
- const prompt = isResuming ? extractLastUserPrompt2(messages) : buildContextualPrompt2(messages);
1906
- let opts = this.buildQueryOptions(signal);
2060
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
2061
+ let opts = this.buildQueryOptions(signal, options);
1907
2062
  const toolResultCapture = /* @__PURE__ */ new Map();
1908
2063
  opts = await this.buildMcpConfig(opts, toolResultCapture);
1909
2064
  const q = sdk.query({ prompt, options: opts });
@@ -1911,6 +2066,7 @@ var init_claude = __esm({
1911
2066
  const thinkingBlockIndices = /* @__PURE__ */ new Set();
1912
2067
  const toolCallTracker = new ClaudeToolCallTracker();
1913
2068
  const pendingStreamToolCalls = /* @__PURE__ */ new Map();
2069
+ let hasStreamedText = false;
1914
2070
  try {
1915
2071
  for await (const msg of q) {
1916
2072
  if (signal.aborted) throw new AbortError();
@@ -1928,6 +2084,7 @@ var init_claude = __esm({
1928
2084
  } else if (e.type === "tool_call_end") {
1929
2085
  pendingStreamToolCalls.delete(e.toolCallId);
1930
2086
  }
2087
+ if (e.type === "text_delta") hasStreamedText = true;
1931
2088
  yield e;
1932
2089
  }
1933
2090
  }
@@ -1951,22 +2108,21 @@ var init_claude = __esm({
1951
2108
  }
1952
2109
  yield this.emitSessionInfo(r.session_id);
1953
2110
  }
1954
- yield { type: "done", finalOutput: r.result };
2111
+ yield {
2112
+ type: "done",
2113
+ finalOutput: hasStreamedText ? null : r.result,
2114
+ ...hasStreamedText ? { streamed: true } : {}
2115
+ };
1955
2116
  }
1956
2117
  }
1957
2118
  } catch (e) {
1958
2119
  if (signal.aborted) throw new AbortError();
1959
2120
  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;
2121
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1967
2122
  const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
1968
2123
  const retryToolCallTracker = new ClaudeToolCallTracker();
1969
2124
  const retryPendingToolCalls = /* @__PURE__ */ new Map();
2125
+ let retryHasStreamedText = false;
1970
2126
  try {
1971
2127
  for await (const msg of retryQ) {
1972
2128
  if (signal.aborted) throw new AbortError();
@@ -1984,6 +2140,7 @@ var init_claude = __esm({
1984
2140
  } else if (ev.type === "tool_call_end") {
1985
2141
  retryPendingToolCalls.delete(ev.toolCallId);
1986
2142
  }
2143
+ if (ev.type === "text_delta") retryHasStreamedText = true;
1987
2144
  yield ev;
1988
2145
  }
1989
2146
  }
@@ -2007,7 +2164,11 @@ var init_claude = __esm({
2007
2164
  }
2008
2165
  yield this.emitSessionInfo(r.session_id);
2009
2166
  }
2010
- yield { type: "done", finalOutput: r.result };
2167
+ yield {
2168
+ type: "done",
2169
+ finalOutput: retryHasStreamedText ? null : r.result,
2170
+ ...retryHasStreamedText ? { streamed: true } : {}
2171
+ };
2011
2172
  }
2012
2173
  }
2013
2174
  } catch (retryError) {
@@ -2069,7 +2230,8 @@ var init_claude = __esm({
2069
2230
  this.cachedModels = body.data.map((m) => ({
2070
2231
  id: m.id,
2071
2232
  name: m.display_name,
2072
- provider: "claude"
2233
+ provider: "claude",
2234
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
2073
2235
  }));
2074
2236
  return this.cachedModels;
2075
2237
  }
@@ -2127,32 +2289,30 @@ __export(vercel_ai_exports, {
2127
2289
  createVercelAIService: () => createVercelAIService
2128
2290
  });
2129
2291
  async function loadSDK3() {
2130
- if (sdkModule3) return sdkModule3;
2292
+ if (_sdkMock3) return _sdkMock3;
2131
2293
  try {
2132
- sdkModule3 = await import('ai');
2133
- return sdkModule3;
2294
+ return await import('ai');
2134
2295
  } catch {
2135
2296
  throw new DependencyError("ai");
2136
2297
  }
2137
2298
  }
2138
2299
  async function loadCompat() {
2139
- if (compatModule) return compatModule;
2300
+ if (_compatMock) return _compatMock;
2140
2301
  try {
2141
- compatModule = await import('@ai-sdk/openai-compatible');
2142
- return compatModule;
2302
+ return await import('@ai-sdk/openai-compatible');
2143
2303
  } catch {
2144
2304
  throw new DependencyError("@ai-sdk/openai-compatible");
2145
2305
  }
2146
2306
  }
2147
2307
  function _injectSDK3(mock) {
2148
- sdkModule3 = mock;
2308
+ _sdkMock3 = mock;
2149
2309
  }
2150
2310
  function _injectCompat(mock) {
2151
- compatModule = mock;
2311
+ _compatMock = mock;
2152
2312
  }
2153
2313
  function _resetSDK3() {
2154
- sdkModule3 = null;
2155
- compatModule = null;
2314
+ _sdkMock3 = null;
2315
+ _compatMock = null;
2156
2316
  }
2157
2317
  function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, signal) {
2158
2318
  const toolMap = {};
@@ -2195,13 +2355,14 @@ function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, s
2195
2355
  return toolMap;
2196
2356
  }
2197
2357
  function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
2198
- return async (args) => {
2358
+ return async (args, options) => {
2199
2359
  if (ourTool.needsApproval && supervisor?.onPermission) {
2200
2360
  const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
2201
2361
  if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
2202
2362
  const request = {
2203
2363
  toolName: ourTool.name,
2204
- toolArgs: args ?? {}
2364
+ toolArgs: args ?? {},
2365
+ toolCallId: options?.toolCallId
2205
2366
  };
2206
2367
  const decision = await supervisor.onPermission(
2207
2368
  request,
@@ -2308,7 +2469,8 @@ function mapStreamPart(part) {
2308
2469
  return {
2309
2470
  type: "error",
2310
2471
  error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
2311
- recoverable: true
2472
+ recoverable: true,
2473
+ code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
2312
2474
  };
2313
2475
  }
2314
2476
  case "reasoning-start":
@@ -2329,10 +2491,13 @@ function mapStreamPart(part) {
2329
2491
  }
2330
2492
  case "error": {
2331
2493
  const p = part;
2494
+ const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
2495
+ const code = classifyAgentError(errorMsg);
2332
2496
  return {
2333
2497
  type: "error",
2334
- error: p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error"),
2335
- recoverable: false
2498
+ error: errorMsg,
2499
+ recoverable: isRecoverableErrorCode(code),
2500
+ code
2336
2501
  };
2337
2502
  }
2338
2503
  default:
@@ -2342,15 +2507,15 @@ function mapStreamPart(part) {
2342
2507
  function createVercelAIService(options) {
2343
2508
  return new VercelAIAgentService(options);
2344
2509
  }
2345
- var sdkModule3, compatModule, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2510
+ var _sdkMock3, _compatMock, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2346
2511
  var init_vercel_ai = __esm({
2347
2512
  "src/backends/vercel-ai.ts"() {
2348
- init_types();
2513
+ init_types2();
2349
2514
  init_base_agent();
2350
- init_errors();
2515
+ init_errors2();
2351
2516
  init_schema();
2352
- sdkModule3 = null;
2353
- compatModule = null;
2517
+ _sdkMock3 = null;
2518
+ _compatMock = null;
2354
2519
  DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2355
2520
  DEFAULT_PROVIDER = "openrouter";
2356
2521
  DEFAULT_MAX_TURNS = 10;
@@ -2363,28 +2528,33 @@ var init_vercel_ai = __esm({
2363
2528
  super(config);
2364
2529
  this.backendOptions = backendOptions;
2365
2530
  }
2366
- async getModel() {
2367
- if (this.model) return this.model;
2531
+ async getModel(options) {
2532
+ const requestedModel = options.model;
2533
+ const defaultModel = this.config.model;
2534
+ if (requestedModel === defaultModel && this.model) return this.model;
2368
2535
  const compat = await loadCompat();
2369
2536
  const provider = compat.createOpenAICompatible({
2370
2537
  name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
2371
2538
  baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
2372
2539
  apiKey: this.backendOptions.apiKey
2373
2540
  });
2374
- const modelId = this.config.model ?? "anthropic/claude-sonnet-4-5";
2375
- this.model = provider.chatModel(modelId);
2376
- return this.model;
2541
+ const model = provider.chatModel(requestedModel);
2542
+ if (requestedModel === defaultModel) {
2543
+ this.model = model;
2544
+ }
2545
+ return model;
2377
2546
  }
2378
- async getSDKTools(signal) {
2547
+ async getSDKTools(signal, options) {
2379
2548
  const sdk = await loadSDK3();
2380
- return mapToolsToSDK2(sdk, this.config.tools ?? [], this.config, this.sessionApprovals, this.config.permissionStore, signal);
2549
+ const tools = this.resolveTools(options);
2550
+ return mapToolsToSDK2(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
2381
2551
  }
2382
2552
  // ─── executeRun ─────────────────────────────────────────────────
2383
- async executeRun(messages, _options, signal) {
2553
+ async executeRun(messages, options, signal) {
2384
2554
  this.checkAbort(signal);
2385
2555
  const sdk = await loadSDK3();
2386
- const model = await this.getModel();
2387
- const tools = await this.getSDKTools(signal);
2556
+ const model = await this.getModel(options);
2557
+ const tools = await this.getSDKTools(signal, options);
2388
2558
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2389
2559
  const sdkMessages = messagesToSDK(messages);
2390
2560
  const hasTools = Object.keys(tools).length > 0;
@@ -2440,10 +2610,10 @@ var init_vercel_ai = __esm({
2440
2610
  };
2441
2611
  }
2442
2612
  // ─── executeRunStructured ───────────────────────────────────────
2443
- async executeRunStructured(messages, schema, _options, signal) {
2613
+ async executeRunStructured(messages, schema, options, signal) {
2444
2614
  this.checkAbort(signal);
2445
2615
  const sdk = await loadSDK3();
2446
- const model = await this.getModel();
2616
+ const model = await this.getModel(options);
2447
2617
  const sdkMessages = messagesToSDK(messages);
2448
2618
  const jsonSchema = zodToJsonSchema(schema.schema);
2449
2619
  const result = await sdk.generateObject({
@@ -2485,11 +2655,11 @@ var init_vercel_ai = __esm({
2485
2655
  };
2486
2656
  }
2487
2657
  // ─── executeStream ──────────────────────────────────────────────
2488
- async *executeStream(messages, _options, signal) {
2658
+ async *executeStream(messages, options, signal) {
2489
2659
  this.checkAbort(signal);
2490
2660
  const sdk = await loadSDK3();
2491
- const model = await this.getModel();
2492
- const tools = await this.getSDKTools(signal);
2661
+ const model = await this.getModel(options);
2662
+ const tools = await this.getSDKTools(signal, options);
2493
2663
  const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2494
2664
  const sdkMessages = messagesToSDK(messages);
2495
2665
  const hasTools = Object.keys(tools).length > 0;
@@ -2514,6 +2684,7 @@ var init_vercel_ai = __esm({
2514
2684
  }
2515
2685
  });
2516
2686
  let finalText = "";
2687
+ let lastFinishReason;
2517
2688
  try {
2518
2689
  for await (const part of result.fullStream) {
2519
2690
  if (signal.aborted) throw new AbortError();
@@ -2524,10 +2695,15 @@ var init_vercel_ai = __esm({
2524
2695
  }
2525
2696
  if (part.type === "finish-step") {
2526
2697
  const p = part;
2698
+ lastFinishReason = p.finishReason;
2527
2699
  if (p.finishReason === "tool-calls") {
2528
2700
  finalText = "";
2529
2701
  }
2530
2702
  }
2703
+ if (part.type === "finish") {
2704
+ const p = part;
2705
+ lastFinishReason = p.finishReason;
2706
+ }
2531
2707
  }
2532
2708
  const totalUsage = await result.totalUsage;
2533
2709
  yield {
@@ -2535,9 +2711,12 @@ var init_vercel_ai = __esm({
2535
2711
  promptTokens: Number(totalUsage?.inputTokens ?? 0),
2536
2712
  completionTokens: Number(totalUsage?.outputTokens ?? 0)
2537
2713
  };
2714
+ const hasStreamed = finalText.length > 0;
2538
2715
  yield {
2539
2716
  type: "done",
2540
- finalOutput: finalText || null
2717
+ finalOutput: hasStreamed ? null : finalText || null,
2718
+ ...hasStreamed ? { streamed: true } : {},
2719
+ ...lastFinishReason ? { finishReason: lastFinishReason } : {}
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);
2618
- }
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];
2805
+ // src/chat/backends/types.ts
2806
+ function isResumableBackend(adapter) {
2807
+ return "canResume" in adapter && typeof adapter.canResume === "function";
2628
2808
  }
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":
@@ -2931,8 +2881,9 @@ function agentEventToChatEvent(event, messageId) {
2931
2881
  case "ask_user":
2932
2882
  case "ask_user_response":
2933
2883
  case "session_info":
2934
- case "done":
2935
2884
  return null;
2885
+ case "done":
2886
+ return { type: "done", finalOutput: event.finalOutput ?? void 0, finishReason: event.finishReason };
2936
2887
  default:
2937
2888
  return null;
2938
2889
  }
@@ -2945,26 +2896,42 @@ async function* adaptAgentEvents(events, messageId) {
2945
2896
  }
2946
2897
  }
2947
2898
  }
2948
- function toAgentMessage(message) {
2899
+
2900
+ // src/chat/conversion.ts
2901
+ function toAgentMessages(message) {
2949
2902
  const textContent = getMessageText(message);
2950
2903
  const toolCallParts = getMessageToolCalls(message);
2951
2904
  switch (message.role) {
2952
2905
  case "user":
2953
- return { role: "user", content: textContent };
2906
+ return [{ role: "user", content: textContent }];
2954
2907
  case "assistant": {
2955
2908
  const toolCalls = toolCallParts.length > 0 ? toolCallParts.map((p) => ({ id: p.toolCallId, name: p.name, args: p.args })) : void 0;
2956
- return {
2909
+ const assistantMsg = {
2957
2910
  role: "assistant",
2958
2911
  content: textContent,
2959
2912
  toolCalls
2960
2913
  };
2914
+ const toolResults = extractToolResults(message);
2915
+ if (toolResults.length > 0) {
2916
+ return [assistantMsg, { role: "tool", toolResults }];
2917
+ }
2918
+ return [assistantMsg];
2961
2919
  }
2962
2920
  case "system":
2963
- return { role: "system", content: textContent };
2921
+ return [{ role: "system", content: textContent }];
2964
2922
  }
2965
2923
  }
2924
+ function extractToolResults(message) {
2925
+ return getMessageToolCalls(message).filter((p) => p.result !== void 0).map((p) => ({
2926
+ toolCallId: p.toolCallId,
2927
+ name: p.name,
2928
+ result: p.result,
2929
+ isError: p.status === "error" ? true : void 0
2930
+ }));
2931
+ }
2966
2932
 
2967
2933
  // src/chat/errors.ts
2934
+ init_errors2();
2968
2935
  init_errors();
2969
2936
  var ChatError = class extends AgentSDKError {
2970
2937
  code;
@@ -2972,7 +2939,11 @@ var ChatError = class extends AgentSDKError {
2972
2939
  retryAfter;
2973
2940
  timestamp;
2974
2941
  constructor(message, options) {
2975
- super(message, { cause: options.cause });
2942
+ super(message, {
2943
+ cause: options.cause,
2944
+ code: options.code,
2945
+ retryable: options.retryable
2946
+ });
2976
2947
  this.name = "ChatError";
2977
2948
  this.code = options.code;
2978
2949
  this.retryable = options.retryable ?? false;
@@ -2984,25 +2955,51 @@ var ChatError = class extends AgentSDKError {
2984
2955
  // src/chat/backends/base.ts
2985
2956
  var BaseBackendAdapter = class {
2986
2957
  name;
2987
- _agentService;
2988
- _agent = null;
2958
+ _agentService = null;
2959
+ _agentServiceFactory = null;
2989
2960
  _disposed = false;
2990
2961
  _agentConfig;
2991
2962
  _ownsService;
2963
+ // Agent lifecycle: tracks current agent and the model it was created with.
2964
+ // For persistent sessions, reused across calls when model matches.
2965
+ // For non-persistent, recreated every call.
2966
+ _currentAgent = null;
2992
2967
  constructor(name, options) {
2993
2968
  this.name = name;
2994
2969
  this._agentConfig = options.agentConfig;
2995
2970
  if (options.agentService) {
2996
2971
  this._agentService = options.agentService;
2997
2972
  this._ownsService = false;
2973
+ } else if (options.agentServiceFactory) {
2974
+ this._agentServiceFactory = options.agentServiceFactory;
2975
+ this._ownsService = true;
2998
2976
  } else {
2999
2977
  this._agentService = this.createService();
3000
2978
  this._ownsService = true;
3001
2979
  }
3002
2980
  }
3003
2981
  get agentService() {
2982
+ if (!this._agentService) {
2983
+ if (this._agentServiceFactory) {
2984
+ this._agentService = this._agentServiceFactory();
2985
+ this._agentServiceFactory = null;
2986
+ } else {
2987
+ throw new ChatError("Agent service not available", {
2988
+ code: "BACKEND_NOT_INSTALLED" /* BACKEND_NOT_INSTALLED */
2989
+ });
2990
+ }
2991
+ }
3004
2992
  return this._agentService;
3005
2993
  }
2994
+ get currentModel() {
2995
+ return this._agentConfig.model;
2996
+ }
2997
+ /**
2998
+ * @deprecated No-op. Tools are passed per-call via SendMessageOptions.tools.
2999
+ * Kept for backward compatibility with code that calls setTools() directly.
3000
+ */
3001
+ setTools() {
3002
+ }
3006
3003
  async sendMessage(session, message, options) {
3007
3004
  this.assertNotDisposed();
3008
3005
  const events = this.streamMessage(session, message, options);
@@ -3030,7 +3027,7 @@ var BaseBackendAdapter = class {
3030
3027
  async *streamMessage(session, message, options) {
3031
3028
  this.assertNotDisposed();
3032
3029
  const agent = this.getOrCreateAgent(options);
3033
- const messages = session.messages.map(toAgentMessage);
3030
+ const messages = session.messages.flatMap(toAgentMessages);
3034
3031
  messages.push({ role: "user", content: message });
3035
3032
  yield* this.streamAgentEvents(agent, messages, options);
3036
3033
  }
@@ -3040,9 +3037,13 @@ var BaseBackendAdapter = class {
3040
3037
  */
3041
3038
  async *streamAgentEvents(agent, messages, options) {
3042
3039
  const messageId = createChatId();
3040
+ const model = options?.model ?? this._agentConfig.model ?? "";
3043
3041
  const agentEvents = agent.streamWithContext(messages, {
3042
+ model,
3044
3043
  signal: options?.signal,
3045
- context: options?.context
3044
+ context: options?.context,
3045
+ tools: options?.tools,
3046
+ ...options?.systemPrompt ? { systemMessage: options.systemPrompt } : {}
3046
3047
  });
3047
3048
  yield { type: "message:start", messageId, role: "assistant" };
3048
3049
  let text = "";
@@ -3068,31 +3069,46 @@ var BaseBackendAdapter = class {
3068
3069
  }
3069
3070
  async listModels() {
3070
3071
  this.assertNotDisposed();
3071
- return this._agentService.listModels();
3072
+ return this.agentService.listModels();
3072
3073
  }
3073
3074
  async validate() {
3074
3075
  this.assertNotDisposed();
3075
- return this._agentService.validate();
3076
+ return this.agentService.validate();
3076
3077
  }
3077
3078
  async dispose() {
3078
3079
  if (this._disposed) return;
3079
3080
  this._disposed = true;
3080
- this._agent?.dispose();
3081
- this._agent = null;
3082
- if (this._ownsService) {
3081
+ if (this._currentAgent) {
3082
+ this._currentAgent.instance.dispose();
3083
+ this._currentAgent = null;
3084
+ }
3085
+ if (this._ownsService && this._agentService && typeof this._agentService.dispose === "function") {
3083
3086
  await this._agentService.dispose();
3084
3087
  }
3085
3088
  }
3086
- /** Get or create an agent, applying model override from options */
3089
+ /** Get or create an agent. Model is passed per-call via RunOptions.
3090
+ * Tools are passed per-call via SendMessageOptions — not baked into config.
3091
+ * For persistent sessions, reuses agent when model matches. */
3087
3092
  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;
3091
- }
3092
- const agent = this._agentService.createAgent(config);
3093
- if (this._agentConfig.sessionMode === "persistent") {
3094
- this._agent = agent;
3095
- }
3093
+ const model = options?.model ?? this._agentConfig.model;
3094
+ if (this._agentConfig.sessionMode === "persistent" && this._currentAgent) {
3095
+ if (this._currentAgent.model === model) {
3096
+ return this._currentAgent.instance;
3097
+ }
3098
+ this._currentAgent.instance.dispose();
3099
+ this._currentAgent = null;
3100
+ }
3101
+ if (this._currentAgent) {
3102
+ this._currentAgent.instance.dispose();
3103
+ this._currentAgent = null;
3104
+ }
3105
+ const config = {
3106
+ ...this._agentConfig,
3107
+ ...model !== void 0 && { model },
3108
+ ...options?.tools?.length ? { tools: options.tools } : {}
3109
+ };
3110
+ const agent = this.agentService.createAgent(config);
3111
+ this._currentAgent = { instance: agent, model };
3096
3112
  return agent;
3097
3113
  }
3098
3114
  assertNotDisposed() {
@@ -3104,21 +3120,15 @@ var BaseBackendAdapter = class {
3104
3120
  }
3105
3121
  };
3106
3122
 
3107
- // src/chat/backends/copilot.ts
3108
- var CopilotChatAdapter = class extends BaseBackendAdapter {
3123
+ // src/chat/backends/resumable.ts
3124
+ var ResumableChatAdapter = class extends BaseBackendAdapter {
3109
3125
  _backendSessionId = null;
3110
- _copilotOptions;
3111
- constructor(options) {
3126
+ constructor(name, options) {
3112
3127
  const agentConfig = {
3113
3128
  ...options.agentConfig,
3114
3129
  sessionMode: "persistent"
3115
3130
  };
3116
- super("copilot", { ...options, agentConfig });
3117
- this._copilotOptions = options.copilotOptions;
3118
- }
3119
- createService() {
3120
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3121
- return createAgentService2("copilot", this._copilotOptions);
3131
+ super(name, { ...options, agentConfig });
3122
3132
  }
3123
3133
  get backendSessionId() {
3124
3134
  return this._backendSessionId;
@@ -3147,7 +3157,7 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
3147
3157
  { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
3148
3158
  );
3149
3159
  }
3150
- const messages = session.messages.map(toAgentMessage);
3160
+ const messages = session.messages.flatMap(toAgentMessages);
3151
3161
  yield* this.streamAgentEvents(agent, messages, options);
3152
3162
  }
3153
3163
  captureSessionId(agent) {
@@ -3157,81 +3167,325 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
3157
3167
  }
3158
3168
  };
3159
3169
 
3170
+ // src/chat/backends/copilot.ts
3171
+ var CopilotChatAdapter = class extends ResumableChatAdapter {
3172
+ _copilotOptions;
3173
+ constructor(options) {
3174
+ super("copilot", options);
3175
+ this._copilotOptions = options.copilotOptions;
3176
+ }
3177
+ createService() {
3178
+ const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
3179
+ return createCopilotService2(this._copilotOptions || {});
3180
+ }
3181
+ };
3182
+
3160
3183
  // src/chat/backends/claude.ts
3161
- var ClaudeChatAdapter = class extends BaseBackendAdapter {
3162
- _backendSessionId = null;
3184
+ var ClaudeChatAdapter = class extends ResumableChatAdapter {
3163
3185
  _claudeOptions;
3164
3186
  constructor(options) {
3165
- const agentConfig = {
3166
- ...options.agentConfig,
3167
- sessionMode: "persistent"
3168
- };
3169
- super("claude", { ...options, agentConfig });
3187
+ super("claude", options);
3170
3188
  this._claudeOptions = options.claudeOptions;
3171
3189
  }
3172
3190
  createService() {
3173
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3174
- return createAgentService2("claude", this._claudeOptions);
3191
+ const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
3192
+ return createClaudeService2(this._claudeOptions || {});
3175
3193
  }
3176
- get backendSessionId() {
3177
- return this._backendSessionId;
3194
+ };
3195
+
3196
+ // src/chat/backends/vercel-ai.ts
3197
+ var VercelAIChatAdapter = class extends BaseBackendAdapter {
3198
+ _vercelOptions;
3199
+ constructor(options) {
3200
+ super("vercel-ai", options);
3201
+ this._vercelOptions = options.vercelOptions;
3178
3202
  }
3179
- canResume() {
3180
- return this._backendSessionId !== null;
3203
+ createService() {
3204
+ const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
3205
+ return createVercelAIService2(this._vercelOptions || {});
3181
3206
  }
3182
- async *resume(session, backendSessionId, options) {
3183
- this.assertNotDisposed();
3184
- if (!backendSessionId) {
3185
- throw new ChatError("Backend session ID is required for resume", {
3186
- code: "INVALID_INPUT" /* INVALID_INPUT */
3207
+ captureSessionId(_agent) {
3208
+ }
3209
+ };
3210
+
3211
+ // src/backends/mock-llm.ts
3212
+ init_base_agent();
3213
+ init_errors2();
3214
+ function extractPrompt(messages) {
3215
+ for (let i = messages.length - 1; i >= 0; i--) {
3216
+ const msg = messages[i];
3217
+ if (msg.role === "user") {
3218
+ return typeof msg.content === "string" ? msg.content : msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
3219
+ }
3220
+ }
3221
+ return "";
3222
+ }
3223
+ function resolveResponse(mode, messages, callIndex) {
3224
+ switch (mode.type) {
3225
+ case "echo":
3226
+ return extractPrompt(messages);
3227
+ case "static":
3228
+ return mode.response;
3229
+ case "scripted": {
3230
+ if (mode.loop) {
3231
+ return mode.responses[callIndex % mode.responses.length];
3232
+ }
3233
+ if (callIndex < mode.responses.length) {
3234
+ return mode.responses[callIndex];
3235
+ }
3236
+ return mode.responses[mode.responses.length - 1];
3237
+ }
3238
+ case "error":
3239
+ throw new AgentSDKError(mode.error, {
3240
+ code: mode.code ?? "backend_error",
3241
+ retryable: mode.recoverable ?? false
3187
3242
  });
3243
+ }
3244
+ }
3245
+ async function applyLatency(latency, signal) {
3246
+ if (!latency) return;
3247
+ const ms = latency.type === "fixed" ? latency.ms : latency.minMs + Math.random() * (latency.maxMs - latency.minMs);
3248
+ if (ms <= 0) return;
3249
+ await new Promise((resolve, reject) => {
3250
+ const timer = setTimeout(resolve, ms);
3251
+ const onAbort = () => {
3252
+ clearTimeout(timer);
3253
+ reject(new Error("aborted"));
3254
+ };
3255
+ if (signal.aborted) {
3256
+ clearTimeout(timer);
3257
+ reject(new Error("aborted"));
3258
+ return;
3188
3259
  }
3189
- const agent = this.getOrCreateAgent(options);
3190
- const currentSessionId = agent.sessionId;
3191
- if (!currentSessionId) {
3192
- throw new ChatError(
3193
- `No active session to resume (requested: ${backendSessionId})`,
3194
- { code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
3195
- );
3260
+ signal.addEventListener("abort", onAbort, { once: true });
3261
+ });
3262
+ }
3263
+ function chunkText(text, streaming) {
3264
+ if (streaming?.chunkSize && streaming.chunkSize > 0) {
3265
+ const chunks = [];
3266
+ for (let i = 0; i < text.length; i += streaming.chunkSize) {
3267
+ chunks.push(text.slice(i, i + streaming.chunkSize));
3196
3268
  }
3197
- if (currentSessionId !== backendSessionId) {
3198
- throw new ChatError(
3199
- `Session expired: expected ${backendSessionId}, got ${currentSessionId}`,
3200
- { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
3201
- );
3269
+ return chunks;
3270
+ }
3271
+ return text.split(/(\s+)/).filter(Boolean);
3272
+ }
3273
+ async function chunkDelay(streaming, signal) {
3274
+ const ms = streaming?.chunkDelayMs;
3275
+ if (!ms || ms <= 0) return;
3276
+ await new Promise((resolve, reject) => {
3277
+ const timer = setTimeout(resolve, ms);
3278
+ const onAbort = () => {
3279
+ clearTimeout(timer);
3280
+ reject(new Error("aborted"));
3281
+ };
3282
+ if (signal.aborted) {
3283
+ clearTimeout(timer);
3284
+ reject(new Error("aborted"));
3285
+ return;
3202
3286
  }
3203
- const messages = session.messages.map(toAgentMessage);
3204
- yield* this.streamAgentEvents(agent, messages, options);
3287
+ signal.addEventListener("abort", onAbort, { once: true });
3288
+ });
3289
+ }
3290
+ var MockLLMAgent = class extends BaseAgent {
3291
+ backendName = "mock-llm";
3292
+ mode;
3293
+ latency;
3294
+ streaming;
3295
+ finishReason;
3296
+ permissions;
3297
+ toolCallConfigs;
3298
+ configuredStructuredOutput;
3299
+ callIndex = 0;
3300
+ constructor(config, options) {
3301
+ super(config);
3302
+ this.mode = options.mode ?? { type: "echo" };
3303
+ this.latency = options.latency;
3304
+ this.streaming = options.streaming;
3305
+ this.finishReason = options.finishReason ?? "stop";
3306
+ this.permissions = options.permissions;
3307
+ this.toolCallConfigs = options.toolCalls ?? [];
3308
+ this.configuredStructuredOutput = options.structuredOutput;
3309
+ }
3310
+ async executeRun(messages, _options, signal) {
3311
+ this.checkAbort(signal);
3312
+ await applyLatency(this.latency, signal);
3313
+ this.checkAbort(signal);
3314
+ const idx = this.callIndex++;
3315
+ const output = resolveResponse(this.mode, messages, idx);
3316
+ const toolCalls = this.toolCallConfigs.map((tc) => ({
3317
+ toolName: tc.toolName,
3318
+ args: tc.args ?? {},
3319
+ result: tc.result ?? null,
3320
+ approved: true
3321
+ }));
3322
+ return {
3323
+ output,
3324
+ structuredOutput: void 0,
3325
+ toolCalls,
3326
+ messages: [
3327
+ ...messages,
3328
+ { role: "assistant", content: output }
3329
+ ],
3330
+ usage: { promptTokens: 10, completionTokens: output.length }
3331
+ };
3205
3332
  }
3206
- captureSessionId(agent) {
3207
- if (agent.sessionId) {
3208
- this._backendSessionId = agent.sessionId;
3333
+ async executeRunStructured(messages, _schema, _options, signal) {
3334
+ this.checkAbort(signal);
3335
+ await applyLatency(this.latency, signal);
3336
+ this.checkAbort(signal);
3337
+ const idx = this.callIndex++;
3338
+ const output = resolveResponse(this.mode, messages, idx);
3339
+ let parsed;
3340
+ if (this.configuredStructuredOutput !== void 0) {
3341
+ parsed = this.configuredStructuredOutput;
3342
+ } else {
3343
+ try {
3344
+ parsed = JSON.parse(output);
3345
+ } catch {
3346
+ parsed = output;
3347
+ }
3348
+ }
3349
+ return {
3350
+ output,
3351
+ structuredOutput: parsed,
3352
+ toolCalls: [],
3353
+ messages: [
3354
+ ...messages,
3355
+ { role: "assistant", content: output }
3356
+ ],
3357
+ usage: { promptTokens: 10, completionTokens: output.length }
3358
+ };
3359
+ }
3360
+ async *executeStream(messages, _options, signal) {
3361
+ this.checkAbort(signal);
3362
+ await applyLatency(this.latency, signal);
3363
+ this.checkAbort(signal);
3364
+ if (this.permissions) {
3365
+ yield* this.simulatePermissions(signal);
3366
+ }
3367
+ if (this.toolCallConfigs.length > 0) {
3368
+ yield* this.simulateToolCalls(signal);
3369
+ }
3370
+ const idx = this.callIndex++;
3371
+ const output = resolveResponse(this.mode, messages, idx);
3372
+ const chunks = chunkText(output, this.streaming);
3373
+ for (let i = 0; i < chunks.length; i++) {
3374
+ this.checkAbort(signal);
3375
+ if (i > 0) {
3376
+ await chunkDelay(this.streaming, signal);
3377
+ }
3378
+ yield { type: "text_delta", text: chunks[i] };
3379
+ }
3380
+ yield {
3381
+ type: "usage_update",
3382
+ promptTokens: 10,
3383
+ completionTokens: output.length
3384
+ };
3385
+ yield {
3386
+ type: "done",
3387
+ finalOutput: output,
3388
+ finishReason: this.finishReason
3389
+ };
3390
+ }
3391
+ async *simulateToolCalls(signal) {
3392
+ for (let i = 0; i < this.toolCallConfigs.length; i++) {
3393
+ this.checkAbort(signal);
3394
+ const tc = this.toolCallConfigs[i];
3395
+ const toolCallId = tc.toolCallId ?? `mock-tc-${i}`;
3396
+ yield {
3397
+ type: "tool_call_start",
3398
+ toolCallId,
3399
+ toolName: tc.toolName,
3400
+ args: tc.args ?? {}
3401
+ };
3402
+ yield {
3403
+ type: "tool_call_end",
3404
+ toolCallId,
3405
+ toolName: tc.toolName,
3406
+ result: tc.result ?? null
3407
+ };
3408
+ }
3409
+ }
3410
+ async *simulatePermissions(signal) {
3411
+ const perms = this.permissions;
3412
+ for (const toolName of perms.toolNames) {
3413
+ this.checkAbort(signal);
3414
+ const request = {
3415
+ toolName,
3416
+ toolArgs: {}
3417
+ };
3418
+ yield { type: "permission_request", request };
3419
+ if (perms.denyTools?.includes(toolName)) {
3420
+ yield {
3421
+ type: "permission_response",
3422
+ toolName,
3423
+ decision: { allowed: false, reason: "Denied by mock configuration" }
3424
+ };
3425
+ } else if (perms.autoApprove) {
3426
+ yield {
3427
+ type: "permission_response",
3428
+ toolName,
3429
+ decision: { allowed: true, scope: "once" }
3430
+ };
3431
+ } else {
3432
+ const supervisor = this.getConfig().supervisor;
3433
+ if (supervisor?.onPermission) {
3434
+ const decision = await supervisor.onPermission(request, signal);
3435
+ yield { type: "permission_response", toolName, decision };
3436
+ } else {
3437
+ yield {
3438
+ type: "permission_response",
3439
+ toolName,
3440
+ decision: { allowed: true, scope: "once" }
3441
+ };
3442
+ }
3443
+ }
3209
3444
  }
3210
3445
  }
3211
3446
  };
3212
-
3213
- // src/chat/backends/vercel-ai.ts
3214
- var VercelAIChatAdapter = class extends BaseBackendAdapter {
3215
- _vercelOptions;
3216
- constructor(options) {
3217
- super("vercel-ai", options);
3218
- this._vercelOptions = options.vercelOptions;
3447
+ var MockLLMService = class {
3448
+ name = "mock-llm";
3449
+ options;
3450
+ models;
3451
+ constructor(options = {}) {
3452
+ this.options = options;
3453
+ this.models = (options.models ?? [
3454
+ { id: "mock-fast", name: "Mock Fast" },
3455
+ { id: "mock-quality", name: "Mock Quality" }
3456
+ ]).map((m) => ({
3457
+ id: m.id,
3458
+ name: m.name,
3459
+ description: m.description
3460
+ }));
3461
+ }
3462
+ createAgent(config) {
3463
+ return new MockLLMAgent(config, this.options);
3219
3464
  }
3220
- createService() {
3221
- const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
3222
- return createAgentService2("vercel-ai", this._vercelOptions);
3465
+ async listModels() {
3466
+ return this.models;
3223
3467
  }
3224
- get backendSessionId() {
3225
- return null;
3468
+ async validate() {
3469
+ return { valid: true, errors: [] };
3226
3470
  }
3227
- canResume() {
3228
- return false;
3471
+ async dispose() {
3229
3472
  }
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
- );
3473
+ };
3474
+ function createMockLLMService(options = {}) {
3475
+ return new MockLLMService(options);
3476
+ }
3477
+
3478
+ // src/chat/backends/mock-llm.ts
3479
+ var MockLLMChatAdapter = class extends BaseBackendAdapter {
3480
+ constructor(options) {
3481
+ const mockOpts = options.mockOptions;
3482
+ super("mock-llm", {
3483
+ ...options,
3484
+ agentServiceFactory: () => createMockLLMService(mockOpts || {})
3485
+ });
3486
+ }
3487
+ createService() {
3488
+ return createMockLLMService({});
3235
3489
  }
3236
3490
  captureSessionId(_agent) {
3237
3491
  }
@@ -3312,16 +3566,22 @@ var SSEChatTransport = class {
3312
3566
  };
3313
3567
  async function streamToTransport(events, transport) {
3314
3568
  try {
3315
- let accumulatedText = "";
3569
+ const textChunks = [];
3570
+ let finishReason;
3316
3571
  for await (const event of events) {
3317
3572
  if (!transport.isOpen) break;
3573
+ if (event.type === "done") {
3574
+ finishReason = event.finishReason;
3575
+ continue;
3576
+ }
3318
3577
  transport.send(event);
3319
3578
  if (event.type === "message:delta") {
3320
- accumulatedText += event.text;
3579
+ textChunks.push(event.text);
3321
3580
  }
3322
3581
  }
3323
3582
  if (transport.isOpen) {
3324
- transport.send({ type: "done", finalOutput: accumulatedText || void 0 });
3583
+ const finalOutput = textChunks.length > 0 ? textChunks.join("") : void 0;
3584
+ transport.send({ type: "done", finalOutput, finishReason });
3325
3585
  }
3326
3586
  transport.close();
3327
3587
  } catch (err) {
@@ -3412,9 +3672,9 @@ var InProcessChatTransport = class {
3412
3672
  send(event) {
3413
3673
  if (!this._open) return;
3414
3674
  if (this._resolve) {
3415
- const resolve2 = this._resolve;
3675
+ const resolve = this._resolve;
3416
3676
  this._resolve = null;
3417
- resolve2({ value: event, done: false });
3677
+ resolve({ value: event, done: false });
3418
3678
  } else {
3419
3679
  this._buffer.push(event);
3420
3680
  }
@@ -3423,9 +3683,9 @@ var InProcessChatTransport = class {
3423
3683
  if (!this._open) return;
3424
3684
  this._open = false;
3425
3685
  if (this._resolve) {
3426
- const resolve2 = this._resolve;
3686
+ const resolve = this._resolve;
3427
3687
  this._resolve = null;
3428
- resolve2({ value: void 0, done: true });
3688
+ resolve({ value: void 0, done: true });
3429
3689
  }
3430
3690
  }
3431
3691
  error(err) {
@@ -3437,9 +3697,9 @@ var InProcessChatTransport = class {
3437
3697
  recoverable: false
3438
3698
  };
3439
3699
  if (this._resolve) {
3440
- const resolve2 = this._resolve;
3700
+ const resolve = this._resolve;
3441
3701
  this._resolve = null;
3442
- resolve2({ value: errorEvent, done: false });
3702
+ resolve({ value: errorEvent, done: false });
3443
3703
  } else {
3444
3704
  this._error = err;
3445
3705
  }
@@ -3464,8 +3724,8 @@ var InProcessChatTransport = class {
3464
3724
  if (!this._open) {
3465
3725
  return Promise.resolve({ value: void 0, done: true });
3466
3726
  }
3467
- return new Promise((resolve2) => {
3468
- this._resolve = resolve2;
3727
+ return new Promise((resolve) => {
3728
+ this._resolve = resolve;
3469
3729
  });
3470
3730
  }
3471
3731
  };
@@ -3524,10 +3784,13 @@ exports.BaseBackendAdapter = BaseBackendAdapter;
3524
3784
  exports.ClaudeChatAdapter = ClaudeChatAdapter;
3525
3785
  exports.CopilotChatAdapter = CopilotChatAdapter;
3526
3786
  exports.InProcessChatTransport = InProcessChatTransport;
3787
+ exports.MockLLMChatAdapter = MockLLMChatAdapter;
3788
+ exports.ResumableChatAdapter = ResumableChatAdapter;
3527
3789
  exports.SSEChatTransport = SSEChatTransport;
3528
3790
  exports.VercelAIChatAdapter = VercelAIChatAdapter;
3529
3791
  exports.WS_READY_STATE = WS_READY_STATE;
3530
3792
  exports.WsChatTransport = WsChatTransport;
3793
+ exports.isResumableBackend = isResumableBackend;
3531
3794
  exports.streamToTransport = streamToTransport;
3532
3795
  exports.withInterceptors = withInterceptors;
3533
3796
  //# sourceMappingURL=backends.cjs.map