@witqq/agent-sdk 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +539 -6
  2. package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
  3. package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
  4. package/dist/auth/index.cjs +260 -2
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +21 -138
  7. package/dist/auth/index.d.ts +21 -138
  8. package/dist/auth/index.js +260 -3
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/backends/claude.cjs +653 -140
  11. package/dist/backends/claude.cjs.map +1 -1
  12. package/dist/backends/claude.d.cts +4 -1
  13. package/dist/backends/claude.d.ts +4 -1
  14. package/dist/backends/claude.js +653 -140
  15. package/dist/backends/claude.js.map +1 -1
  16. package/dist/backends/copilot.cjs +428 -88
  17. package/dist/backends/copilot.cjs.map +1 -1
  18. package/dist/backends/copilot.d.cts +13 -4
  19. package/dist/backends/copilot.d.ts +13 -4
  20. package/dist/backends/copilot.js +428 -88
  21. package/dist/backends/copilot.js.map +1 -1
  22. package/dist/backends/vercel-ai.cjs +349 -77
  23. package/dist/backends/vercel-ai.cjs.map +1 -1
  24. package/dist/backends/vercel-ai.d.cts +3 -1
  25. package/dist/backends/vercel-ai.d.ts +3 -1
  26. package/dist/backends/vercel-ai.js +349 -77
  27. package/dist/backends/vercel-ai.js.map +1 -1
  28. package/dist/backends-BSrsBYFn.d.cts +39 -0
  29. package/dist/backends-BSrsBYFn.d.ts +39 -0
  30. package/dist/chat/accumulator.cjs +147 -0
  31. package/dist/chat/accumulator.cjs.map +1 -0
  32. package/dist/chat/accumulator.d.cts +64 -0
  33. package/dist/chat/accumulator.d.ts +64 -0
  34. package/dist/chat/accumulator.js +145 -0
  35. package/dist/chat/accumulator.js.map +1 -0
  36. package/dist/chat/backends.cjs +3524 -0
  37. package/dist/chat/backends.cjs.map +1 -0
  38. package/dist/chat/backends.d.cts +66 -0
  39. package/dist/chat/backends.d.ts +66 -0
  40. package/dist/chat/backends.js +3512 -0
  41. package/dist/chat/backends.js.map +1 -0
  42. package/dist/chat/context.cjs +280 -0
  43. package/dist/chat/context.cjs.map +1 -0
  44. package/dist/chat/context.d.cts +191 -0
  45. package/dist/chat/context.d.ts +191 -0
  46. package/dist/chat/context.js +277 -0
  47. package/dist/chat/context.js.map +1 -0
  48. package/dist/chat/core.cjs +305 -0
  49. package/dist/chat/core.cjs.map +1 -0
  50. package/dist/chat/core.d.cts +84 -0
  51. package/dist/chat/core.d.ts +84 -0
  52. package/dist/chat/core.js +282 -0
  53. package/dist/chat/core.js.map +1 -0
  54. package/dist/chat/errors.cjs +273 -0
  55. package/dist/chat/errors.cjs.map +1 -0
  56. package/dist/chat/errors.d.cts +97 -0
  57. package/dist/chat/errors.d.ts +97 -0
  58. package/dist/chat/errors.js +266 -0
  59. package/dist/chat/errors.js.map +1 -0
  60. package/dist/chat/events.cjs +203 -0
  61. package/dist/chat/events.cjs.map +1 -0
  62. package/dist/chat/events.d.cts +245 -0
  63. package/dist/chat/events.d.ts +245 -0
  64. package/dist/chat/events.js +196 -0
  65. package/dist/chat/events.js.map +1 -0
  66. package/dist/chat/index.cjs +5550 -0
  67. package/dist/chat/index.cjs.map +1 -0
  68. package/dist/chat/index.d.cts +77 -0
  69. package/dist/chat/index.d.ts +77 -0
  70. package/dist/chat/index.js +5505 -0
  71. package/dist/chat/index.js.map +1 -0
  72. package/dist/chat/react/theme.css +2517 -0
  73. package/dist/chat/react.cjs +3589 -0
  74. package/dist/chat/react.cjs.map +1 -0
  75. package/dist/chat/react.d.cts +1088 -0
  76. package/dist/chat/react.d.ts +1088 -0
  77. package/dist/chat/react.js +3547 -0
  78. package/dist/chat/react.js.map +1 -0
  79. package/dist/chat/runtime.cjs +1245 -0
  80. package/dist/chat/runtime.cjs.map +1 -0
  81. package/dist/chat/runtime.d.cts +182 -0
  82. package/dist/chat/runtime.d.ts +182 -0
  83. package/dist/chat/runtime.js +1243 -0
  84. package/dist/chat/runtime.js.map +1 -0
  85. package/dist/chat/server.cjs +2668 -0
  86. package/dist/chat/server.cjs.map +1 -0
  87. package/dist/chat/server.d.cts +648 -0
  88. package/dist/chat/server.d.ts +648 -0
  89. package/dist/chat/server.js +2628 -0
  90. package/dist/chat/server.js.map +1 -0
  91. package/dist/chat/sessions.cjs +380 -0
  92. package/dist/chat/sessions.cjs.map +1 -0
  93. package/dist/chat/sessions.d.cts +158 -0
  94. package/dist/chat/sessions.d.ts +158 -0
  95. package/dist/chat/sessions.js +376 -0
  96. package/dist/chat/sessions.js.map +1 -0
  97. package/dist/chat/sqlite.cjs +441 -0
  98. package/dist/chat/sqlite.cjs.map +1 -0
  99. package/dist/chat/sqlite.d.cts +128 -0
  100. package/dist/chat/sqlite.d.ts +128 -0
  101. package/dist/chat/sqlite.js +435 -0
  102. package/dist/chat/sqlite.js.map +1 -0
  103. package/dist/chat/state.cjs +190 -0
  104. package/dist/chat/state.cjs.map +1 -0
  105. package/dist/chat/state.d.cts +95 -0
  106. package/dist/chat/state.d.ts +95 -0
  107. package/dist/chat/state.js +180 -0
  108. package/dist/chat/state.js.map +1 -0
  109. package/dist/chat/storage.cjs +249 -0
  110. package/dist/chat/storage.cjs.map +1 -0
  111. package/dist/chat/storage.d.cts +197 -0
  112. package/dist/chat/storage.d.ts +197 -0
  113. package/dist/chat/storage.js +245 -0
  114. package/dist/chat/storage.js.map +1 -0
  115. package/dist/errors-C-so0M4t.d.cts +33 -0
  116. package/dist/errors-C-so0M4t.d.ts +33 -0
  117. package/dist/errors-CmVvczxZ.d.cts +28 -0
  118. package/dist/errors-CmVvczxZ.d.ts +28 -0
  119. package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
  120. package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
  121. package/dist/index.cjs +365 -59
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.d.cts +322 -125
  124. package/dist/index.d.ts +322 -125
  125. package/dist/index.js +359 -60
  126. package/dist/index.js.map +1 -1
  127. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  128. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  129. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  130. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  131. package/dist/testing.cjs +383 -0
  132. package/dist/testing.cjs.map +1 -0
  133. package/dist/testing.d.cts +132 -0
  134. package/dist/testing.d.ts +132 -0
  135. package/dist/testing.js +377 -0
  136. package/dist/testing.js.map +1 -0
  137. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  138. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  139. package/dist/transport-Cdh3M0tS.d.cts +68 -0
  140. package/dist/transport-Ciap4PWK.d.ts +68 -0
  141. package/dist/types-4vbcmPTp.d.cts +143 -0
  142. package/dist/types-BxggH0Yh.d.ts +143 -0
  143. package/dist/types-DRgd_9R7.d.cts +363 -0
  144. package/dist/types-ajANVzf7.d.ts +363 -0
  145. package/package.json +178 -6
@@ -0,0 +1,3524 @@
1
+ 'use strict';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __esm = (fn, res) => function __init() {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
+
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;
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({
70
+ "src/errors.ts"() {
71
+ init_errors();
72
+ AgentSDKError = class extends Error {
73
+ /** @internal Marker for cross-bundle identity checks */
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;
81
+ constructor(message, options) {
82
+ super(message, options);
83
+ this.name = "AgentSDKError";
84
+ this.code = options?.code;
85
+ this.retryable = options?.retryable ?? false;
86
+ this.httpStatus = options?.httpStatus;
87
+ }
88
+ /** Check if an error is an AgentSDKError (works across bundled copies) */
89
+ static is(error) {
90
+ return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
91
+ }
92
+ };
93
+ ReentrancyError = class extends AgentSDKError {
94
+ constructor() {
95
+ super("Agent is already running. Await the current run before starting another.", {
96
+ code: "REENTRANCY" /* REENTRANCY */
97
+ });
98
+ this.name = "ReentrancyError";
99
+ }
100
+ };
101
+ DisposedError = class extends AgentSDKError {
102
+ constructor(entity) {
103
+ super(`${entity} has been disposed and cannot be used.`, {
104
+ code: "DISPOSED" /* DISPOSED */
105
+ });
106
+ this.name = "DisposedError";
107
+ }
108
+ };
109
+ SubprocessError = class extends AgentSDKError {
110
+ constructor(message, options) {
111
+ super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
112
+ this.name = "SubprocessError";
113
+ }
114
+ };
115
+ DependencyError = class extends AgentSDKError {
116
+ packageName;
117
+ constructor(packageName) {
118
+ super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
119
+ code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
120
+ });
121
+ this.name = "DependencyError";
122
+ this.packageName = packageName;
123
+ }
124
+ };
125
+ AbortError = class extends AgentSDKError {
126
+ constructor() {
127
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
128
+ this.name = "AbortError";
129
+ }
130
+ };
131
+ ToolExecutionError = class extends AgentSDKError {
132
+ toolName;
133
+ constructor(toolName, message, options) {
134
+ super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
135
+ this.name = "ToolExecutionError";
136
+ this.toolName = toolName;
137
+ }
138
+ };
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";
146
+ }
147
+ };
148
+ }
149
+ });
150
+
151
+ // src/types/guards.ts
152
+ function getTextContent(content) {
153
+ if (typeof content === "string") return content;
154
+ return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
155
+ }
156
+ var init_guards = __esm({
157
+ "src/types/guards.ts"() {
158
+ }
159
+ });
160
+
161
+ // src/types/index.ts
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({
171
+ "src/types.ts"() {
172
+ init_types();
173
+ }
174
+ });
175
+
176
+ // src/base-agent.ts
177
+ var BaseAgent;
178
+ var init_base_agent = __esm({
179
+ "src/base-agent.ts"() {
180
+ init_errors2();
181
+ init_errors2();
182
+ init_errors();
183
+ BaseAgent = class {
184
+ state = "idle";
185
+ abortController = null;
186
+ config;
187
+ _cleanupExternalSignal = null;
188
+ _streamMiddleware = [];
189
+ /** CLI session ID for persistent mode. Override in backends that support it. */
190
+ get sessionId() {
191
+ return void 0;
192
+ }
193
+ constructor(config) {
194
+ this.config = Object.freeze({ ...config });
195
+ }
196
+ // ─── Public Interface ─────────────────────────────────────────
197
+ async run(prompt, options) {
198
+ this.guardReentrancy();
199
+ this.guardDisposed();
200
+ const ac = this.createAbortController(options?.signal);
201
+ this.state = "running";
202
+ try {
203
+ const messages = [{ role: "user", content: prompt }];
204
+ const result = await this.withRetry(
205
+ () => this.executeRun(messages, options, ac.signal),
206
+ options
207
+ );
208
+ this.enrichAndNotifyUsage(result, options);
209
+ return result;
210
+ } finally {
211
+ this.cleanupRun();
212
+ }
213
+ }
214
+ async runWithContext(messages, options) {
215
+ this.guardReentrancy();
216
+ this.guardDisposed();
217
+ const ac = this.createAbortController(options?.signal);
218
+ this.state = "running";
219
+ try {
220
+ const result = await this.withRetry(
221
+ () => this.executeRun(messages, options, ac.signal),
222
+ options
223
+ );
224
+ this.enrichAndNotifyUsage(result, options);
225
+ return result;
226
+ } finally {
227
+ this.cleanupRun();
228
+ }
229
+ }
230
+ async runStructured(prompt, schema, options) {
231
+ this.guardReentrancy();
232
+ this.guardDisposed();
233
+ const ac = this.createAbortController(options?.signal);
234
+ this.state = "running";
235
+ try {
236
+ const messages = [{ role: "user", content: prompt }];
237
+ const result = await this.withRetry(
238
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
239
+ options
240
+ );
241
+ this.enrichAndNotifyUsage(result, options);
242
+ return result;
243
+ } finally {
244
+ this.cleanupRun();
245
+ }
246
+ }
247
+ async *stream(prompt, options) {
248
+ this.guardReentrancy();
249
+ this.guardDisposed();
250
+ const ac = this.createAbortController(options?.signal);
251
+ this.state = "streaming";
252
+ try {
253
+ const messages = [{ role: "user", content: prompt }];
254
+ yield* this.streamWithRetry(
255
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
256
+ options
257
+ );
258
+ } finally {
259
+ this.cleanupRun();
260
+ }
261
+ }
262
+ async *streamWithContext(messages, options) {
263
+ this.guardReentrancy();
264
+ this.guardDisposed();
265
+ const ac = this.createAbortController(options?.signal);
266
+ this.state = "streaming";
267
+ try {
268
+ yield* this.streamWithRetry(
269
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
270
+ options
271
+ );
272
+ } finally {
273
+ this.cleanupRun();
274
+ }
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
+ }
299
+ abort() {
300
+ if (this.abortController) {
301
+ this.abortController.abort();
302
+ }
303
+ }
304
+ /** Default interrupt — falls back to abort(). Backends may override with graceful shutdown. */
305
+ async interrupt() {
306
+ this.abort();
307
+ }
308
+ getState() {
309
+ return this.state;
310
+ }
311
+ getConfig() {
312
+ return this.config;
313
+ }
314
+ /** Mark agent as disposed. Override to add cleanup. */
315
+ dispose() {
316
+ this._cleanupExternalSignal?.();
317
+ this._cleanupExternalSignal = null;
318
+ this.abort();
319
+ this.state = "disposed";
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
+ }
403
+ // ─── Usage Enrichment ───────────────────────────────────────────
404
+ /** Enrich result usage with model/backend and fire onUsage callback */
405
+ enrichAndNotifyUsage(result, options) {
406
+ if (result.usage) {
407
+ result.usage = {
408
+ ...result.usage,
409
+ model: options.model,
410
+ backend: this.backendName
411
+ };
412
+ this.callOnUsage(result.usage);
413
+ }
414
+ }
415
+ /** Wrap a stream to enrich usage_update events and fire onUsage callback */
416
+ async *enrichStream(source, options) {
417
+ const model = options.model;
418
+ for await (const event of source) {
419
+ if (event.type === "usage_update") {
420
+ const usage = {
421
+ promptTokens: event.promptTokens,
422
+ completionTokens: event.completionTokens,
423
+ model,
424
+ backend: this.backendName
425
+ };
426
+ this.callOnUsage(usage);
427
+ yield { type: "usage_update", ...usage };
428
+ } else {
429
+ yield event;
430
+ }
431
+ }
432
+ }
433
+ /** Fire onUsage callback (fire-and-forget: errors logged, not propagated) */
434
+ callOnUsage(usage) {
435
+ if (!this.config.onUsage) return;
436
+ try {
437
+ this.config.onUsage(usage);
438
+ } catch (e) {
439
+ console.warn(
440
+ "[agent-sdk] onUsage callback error:",
441
+ e instanceof Error ? e.message : String(e)
442
+ );
443
+ }
444
+ }
445
+ // ─── Heartbeat ───────────────────────────────────────────────
446
+ /** Wrap a stream to emit heartbeat events at configured intervals.
447
+ * When heartbeatInterval is not set, passes through directly. */
448
+ async *heartbeatStream(source) {
449
+ const interval = this.config.heartbeatInterval;
450
+ if (!interval || interval <= 0) {
451
+ yield* source;
452
+ return;
453
+ }
454
+ const iterator = source[Symbol.asyncIterator]();
455
+ let pendingEvent = null;
456
+ let heartbeatResolve = null;
457
+ const timer = setInterval(() => {
458
+ if (heartbeatResolve) {
459
+ const resolve = heartbeatResolve;
460
+ heartbeatResolve = null;
461
+ resolve();
462
+ }
463
+ }, interval);
464
+ try {
465
+ while (true) {
466
+ if (!pendingEvent) {
467
+ pendingEvent = iterator.next();
468
+ }
469
+ const heartbeatPromise = new Promise((resolve) => {
470
+ heartbeatResolve = resolve;
471
+ });
472
+ const eventDone = pendingEvent.then(
473
+ (r) => ({ kind: "event", result: r })
474
+ );
475
+ const heartbeatDone = heartbeatPromise.then(
476
+ () => ({ kind: "heartbeat" })
477
+ );
478
+ const winner = await Promise.race([eventDone, heartbeatDone]);
479
+ if (winner.kind === "heartbeat") {
480
+ yield { type: "heartbeat" };
481
+ } else {
482
+ pendingEvent = null;
483
+ heartbeatResolve = null;
484
+ if (winner.result.done) break;
485
+ yield winner.result.value;
486
+ }
487
+ }
488
+ } finally {
489
+ clearInterval(timer);
490
+ heartbeatResolve = null;
491
+ }
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
+ }
522
+ // ─── Guards ───────────────────────────────────────────────────
523
+ guardReentrancy() {
524
+ if (this.state === "running" || this.state === "streaming") {
525
+ throw new ReentrancyError();
526
+ }
527
+ }
528
+ guardDisposed() {
529
+ if (this.state === "disposed") {
530
+ throw new DisposedError("Agent");
531
+ }
532
+ }
533
+ /** Throw AbortError if signal is already aborted */
534
+ checkAbort(signal) {
535
+ if (signal.aborted) {
536
+ throw new AbortError();
537
+ }
538
+ }
539
+ // ─── Internal Helpers ─────────────────────────────────────────
540
+ /** Clean up after a run completes (success, error, or abort). */
541
+ cleanupRun() {
542
+ this._cleanupExternalSignal?.();
543
+ this._cleanupExternalSignal = null;
544
+ this.state = "idle";
545
+ this.abortController = null;
546
+ }
547
+ createAbortController(externalSignal) {
548
+ const ac = new AbortController();
549
+ this.abortController = ac;
550
+ this._cleanupExternalSignal = null;
551
+ if (externalSignal) {
552
+ if (externalSignal.aborted) {
553
+ ac.abort();
554
+ } else {
555
+ const listener = () => ac.abort();
556
+ externalSignal.addEventListener("abort", listener, { once: true });
557
+ this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
558
+ }
559
+ }
560
+ return ac;
561
+ }
562
+ };
563
+ }
564
+ });
565
+
566
+ // src/utils/schema.ts
567
+ function zodToJsonSchema(schema) {
568
+ const schemaAny = schema;
569
+ if ("toJSONSchema" in schema && typeof schemaAny.toJSONSchema === "function") {
570
+ return schemaAny.toJSONSchema();
571
+ }
572
+ if ("jsonSchema" in schema && typeof schemaAny.jsonSchema === "function") {
573
+ return schemaAny.jsonSchema();
574
+ }
575
+ return extractSchemaFromDef(schema);
576
+ }
577
+ function extractSchemaFromDef(schema) {
578
+ const def = schema._def;
579
+ const typeName = def.typeName;
580
+ switch (typeName) {
581
+ case "ZodString":
582
+ return { type: "string" };
583
+ case "ZodNumber":
584
+ return { type: "number" };
585
+ case "ZodBoolean":
586
+ return { type: "boolean" };
587
+ case "ZodNull":
588
+ return { type: "null" };
589
+ case "ZodArray":
590
+ return {
591
+ type: "array",
592
+ items: extractSchemaFromDef(def.type)
593
+ };
594
+ case "ZodObject": {
595
+ const shape = schema.shape;
596
+ const properties = {};
597
+ const required = [];
598
+ for (const [key, value] of Object.entries(shape)) {
599
+ const valueDef = value._def;
600
+ if (valueDef.typeName === "ZodOptional") {
601
+ properties[key] = extractSchemaFromDef(valueDef.innerType);
602
+ } else {
603
+ properties[key] = extractSchemaFromDef(value);
604
+ required.push(key);
605
+ }
606
+ }
607
+ return {
608
+ type: "object",
609
+ properties,
610
+ ...required.length > 0 ? { required } : {}
611
+ };
612
+ }
613
+ case "ZodOptional":
614
+ return extractSchemaFromDef(def.innerType);
615
+ case "ZodEnum":
616
+ return { type: "string", enum: def.values };
617
+ default:
618
+ return {};
619
+ }
620
+ }
621
+ var init_schema = __esm({
622
+ "src/utils/schema.ts"() {
623
+ }
624
+ });
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
+
686
+ // src/backends/copilot.ts
687
+ var copilot_exports = {};
688
+ __export(copilot_exports, {
689
+ _injectSDK: () => _injectSDK,
690
+ _resetSDK: () => _resetSDK,
691
+ createCopilotService: () => createCopilotService
692
+ });
693
+ async function loadSDK() {
694
+ if (_sdkMock) return _sdkMock;
695
+ try {
696
+ return await import('@github/copilot-sdk');
697
+ } catch {
698
+ throw new SubprocessError(
699
+ "@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
700
+ );
701
+ }
702
+ }
703
+ function _injectSDK(mock) {
704
+ _sdkMock = mock;
705
+ }
706
+ function _resetSDK() {
707
+ _sdkMock = null;
708
+ }
709
+ function mapToolsToSDK(tools) {
710
+ return tools.map((tool) => ({
711
+ name: tool.name,
712
+ description: tool.description,
713
+ parameters: convertParameters(tool.parameters),
714
+ handler: async (args) => {
715
+ const result = await tool.execute(args);
716
+ return typeof result === "string" ? result : JSON.stringify(result);
717
+ }
718
+ }));
719
+ }
720
+ function convertParameters(params) {
721
+ if (!params) return void 0;
722
+ if (params && typeof params === "object" && "_def" in params) {
723
+ return zodToJsonSchema(params);
724
+ }
725
+ return params;
726
+ }
727
+ async function mapToolsToSDKAsync(tools) {
728
+ return tools.map((tool) => ({
729
+ name: tool.name,
730
+ description: tool.description,
731
+ parameters: convertParameters(tool.parameters),
732
+ handler: async (args) => {
733
+ const result = await tool.execute(args);
734
+ return typeof result === "string" ? result : JSON.stringify(result);
735
+ }
736
+ }));
737
+ }
738
+ function buildPermissionHandler(config) {
739
+ const onPermission = config.supervisor?.onPermission;
740
+ if (!onPermission) {
741
+ return async () => ({ kind: "approved" });
742
+ }
743
+ const permissionStore = config.permissionStore;
744
+ return async (request) => {
745
+ const toolName = String(request.kind);
746
+ if (permissionStore && await permissionStore.isApproved(toolName)) {
747
+ return { kind: "approved" };
748
+ }
749
+ const unifiedRequest = {
750
+ toolName,
751
+ toolArgs: { ...request },
752
+ toolCallId: request.toolCallId,
753
+ rawSDKRequest: request
754
+ };
755
+ const ac = new AbortController();
756
+ const decision = await onPermission(unifiedRequest, ac.signal);
757
+ if (decision.allowed) {
758
+ if (permissionStore && decision.scope) {
759
+ await permissionStore.approve(toolName, decision.scope);
760
+ }
761
+ return { kind: "approved" };
762
+ }
763
+ return { kind: "denied-interactively-by-user" };
764
+ };
765
+ }
766
+ function buildUserInputHandler(config) {
767
+ const onAskUser = config.supervisor?.onAskUser;
768
+ if (!onAskUser) {
769
+ return async () => ({
770
+ answer: "Complete the task autonomously without asking questions.",
771
+ wasFreeform: true
772
+ });
773
+ }
774
+ return async (request) => {
775
+ const ac = new AbortController();
776
+ const response = await onAskUser(
777
+ {
778
+ question: request.question,
779
+ choices: request.choices,
780
+ allowFreeform: request.allowFreeform
781
+ },
782
+ ac.signal
783
+ );
784
+ return { answer: response.answer, wasFreeform: response.wasFreeform };
785
+ };
786
+ }
787
+ function mapSessionEvent(event, tracker, thinkingTracker) {
788
+ const data = event.data;
789
+ switch (event.type) {
790
+ case "assistant.message_delta": {
791
+ const textEvent = {
792
+ type: "text_delta",
793
+ text: String(data.deltaContent ?? "")
794
+ };
795
+ if (thinkingTracker.endThinking()) {
796
+ return [{ type: "thinking_end" }, textEvent];
797
+ }
798
+ return textEvent;
799
+ }
800
+ case "assistant.reasoning":
801
+ case "assistant.reasoning_delta": {
802
+ if (thinkingTracker.isCompleted()) return null;
803
+ const events = [];
804
+ if (!thinkingTracker.isActive()) {
805
+ thinkingTracker.startThinking();
806
+ events.push({ type: "thinking_start" });
807
+ }
808
+ const reasoningText = String(data.deltaContent ?? data.content ?? "");
809
+ if (reasoningText) {
810
+ events.push({ type: "thinking_delta", text: reasoningText });
811
+ }
812
+ return events.length === 1 ? events[0] : events.length > 1 ? events : null;
813
+ }
814
+ case "tool.execution_start": {
815
+ const toolCallId = String(data.toolCallId ?? "");
816
+ const toolName = String(data.toolName ?? "unknown");
817
+ let args = {};
818
+ if (typeof data.arguments === "string") {
819
+ try {
820
+ args = JSON.parse(data.arguments);
821
+ } catch {
822
+ args = data.arguments;
823
+ }
824
+ } else if (data.arguments != null) {
825
+ args = data.arguments;
826
+ }
827
+ tracker.trackStart(toolCallId, toolName, args);
828
+ const toolStartEvent = { type: "tool_call_start", toolCallId, toolName, args };
829
+ const events = [];
830
+ if (thinkingTracker.endThinking()) {
831
+ events.push({ type: "thinking_end" });
832
+ }
833
+ thinkingTracker.reset();
834
+ events.push(toolStartEvent);
835
+ return events.length === 1 ? events[0] : events;
836
+ }
837
+ case "tool.execution_complete": {
838
+ const toolCallId = String(data.toolCallId ?? "");
839
+ const info = tracker.getInfo(toolCallId);
840
+ const rawResult = data.result;
841
+ const result = (rawResult && typeof rawResult === "object" && "content" in rawResult ? rawResult.content : rawResult) ?? null;
842
+ return {
843
+ type: "tool_call_end",
844
+ toolCallId,
845
+ toolName: info?.toolName ?? "unknown",
846
+ result
847
+ };
848
+ }
849
+ case "assistant.usage":
850
+ return {
851
+ type: "usage_update",
852
+ promptTokens: Number(data.inputTokens ?? 0),
853
+ completionTokens: Number(data.outputTokens ?? 0)
854
+ };
855
+ case "session.error":
856
+ console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
857
+ {
858
+ const errorMsg = String(data.message ?? "Unknown error");
859
+ const code = classifyAgentError(errorMsg);
860
+ return {
861
+ type: "error",
862
+ error: errorMsg,
863
+ recoverable: isRecoverableErrorCode(code),
864
+ code
865
+ };
866
+ }
867
+ case "assistant.message": {
868
+ const doneEvent = {
869
+ type: "done",
870
+ finalOutput: null,
871
+ streamed: true
872
+ };
873
+ if (thinkingTracker.endThinking()) {
874
+ return [{ type: "thinking_end" }, doneEvent];
875
+ }
876
+ return doneEvent;
877
+ }
878
+ default:
879
+ return null;
880
+ }
881
+ }
882
+ function withTimeout(promise, ms, message) {
883
+ return new Promise((resolve, reject) => {
884
+ const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
885
+ promise.then(
886
+ (val) => {
887
+ clearTimeout(timer);
888
+ resolve(val);
889
+ },
890
+ (err) => {
891
+ clearTimeout(timer);
892
+ reject(err);
893
+ }
894
+ );
895
+ });
896
+ }
897
+ function createCopilotService(options) {
898
+ return new CopilotAgentService(options);
899
+ }
900
+ var _sdkMock, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
901
+ var init_copilot = __esm({
902
+ "src/backends/copilot.ts"() {
903
+ init_types2();
904
+ init_base_agent();
905
+ init_errors2();
906
+ init_schema();
907
+ init_shared();
908
+ _sdkMock = null;
909
+ ToolCallTracker = class {
910
+ map = /* @__PURE__ */ new Map();
911
+ trackStart(toolCallId, toolName, args) {
912
+ this.map.set(toolCallId, { toolName, args });
913
+ }
914
+ getInfo(toolCallId) {
915
+ return this.map.get(toolCallId);
916
+ }
917
+ clear() {
918
+ this.map.clear();
919
+ }
920
+ };
921
+ ThinkingTracker = class {
922
+ active = false;
923
+ completed = false;
924
+ isActive() {
925
+ return this.active;
926
+ }
927
+ /** Returns true if thinking already completed (should ignore further reasoning events). */
928
+ isCompleted() {
929
+ return this.completed;
930
+ }
931
+ startThinking() {
932
+ this.active = true;
933
+ }
934
+ /** Returns true if thinking was active and is now ended. */
935
+ endThinking() {
936
+ if (!this.active) return false;
937
+ this.active = false;
938
+ this.completed = true;
939
+ return true;
940
+ }
941
+ /** Reset for next turn (e.g. after done event). */
942
+ reset() {
943
+ this.active = false;
944
+ this.completed = false;
945
+ }
946
+ };
947
+ CopilotAgent = class extends BaseAgent {
948
+ backendName = "copilot";
949
+ getClient;
950
+ sdkTools;
951
+ sessionConfig;
952
+ sendAndWaitTimeout;
953
+ isPersistent;
954
+ persistentSession = null;
955
+ _sessionId;
956
+ _persistentModel;
957
+ activeSession = null;
958
+ _resumeSessionId;
959
+ _toolsReady = null;
960
+ constructor(config, getClient, sendAndWaitTimeout, resumeSessionId) {
961
+ super(config);
962
+ this.getClient = getClient;
963
+ this.sendAndWaitTimeout = sendAndWaitTimeout;
964
+ this.isPersistent = config.sessionMode === "persistent";
965
+ this.sdkTools = mapToolsToSDK(config.tools ?? []);
966
+ this.sessionConfig = {
967
+ model: config.model,
968
+ tools: this.sdkTools,
969
+ systemMessage: {
970
+ mode: config.systemMessageMode ?? "append",
971
+ content: config.systemPrompt
972
+ },
973
+ onPermissionRequest: buildPermissionHandler(config),
974
+ onUserInputRequest: buildUserInputHandler(config),
975
+ ...config.availableTools?.length ? { availableTools: config.availableTools } : {}
976
+ };
977
+ this._toolsReady = this._initToolsAsync(config);
978
+ this._resumeSessionId = resumeSessionId;
979
+ }
980
+ /** Pre-convert Zod schemas to JSON Schema asynchronously.
981
+ * Updates sdkTools and sessionConfig.tools before first session creation. */
982
+ async _initToolsAsync(config) {
983
+ this.sdkTools = await mapToolsToSDKAsync(config.tools ?? []);
984
+ this.sessionConfig.tools = this.sdkTools;
985
+ }
986
+ get sessionId() {
987
+ return this._sessionId;
988
+ }
989
+ async interrupt() {
990
+ if (this.activeSession) {
991
+ this.activeSession.abort().catch(() => {
992
+ });
993
+ }
994
+ this.abort();
995
+ }
996
+ emitSessionInfo(sessionId) {
997
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
998
+ const transcriptPath = home ? `${home}/.copilot/session-state/${sessionId}/events.jsonl` : void 0;
999
+ return { type: "session_info", sessionId, transcriptPath, backend: "copilot" };
1000
+ }
1001
+ clearPersistentSession() {
1002
+ if (this.isPersistent) {
1003
+ this.persistentSession?.destroy().catch(() => {
1004
+ });
1005
+ this.persistentSession = null;
1006
+ this._sessionId = void 0;
1007
+ this._persistentModel = void 0;
1008
+ }
1009
+ }
1010
+ async getOrCreateSession(streaming, options) {
1011
+ if (this.isPersistent && this.persistentSession) {
1012
+ if (options.model !== this._persistentModel) {
1013
+ this.persistentSession.destroy().catch(() => {
1014
+ });
1015
+ this.persistentSession = null;
1016
+ this._sessionId = void 0;
1017
+ } else {
1018
+ return { session: this.persistentSession, isNew: false };
1019
+ }
1020
+ }
1021
+ if (this._toolsReady) {
1022
+ await this._toolsReady;
1023
+ this._toolsReady = null;
1024
+ }
1025
+ const sessionConfig = { ...this.sessionConfig };
1026
+ sessionConfig.model = options.model;
1027
+ const resolvedTools = this.resolveTools(options);
1028
+ if (options?.tools) {
1029
+ sessionConfig.tools = mapToolsToSDK(resolvedTools);
1030
+ }
1031
+ const client = await this.getClient();
1032
+ if (this._resumeSessionId) {
1033
+ const storedId = this._resumeSessionId;
1034
+ this._resumeSessionId = void 0;
1035
+ try {
1036
+ const session2 = await client.resumeSession(storedId, {
1037
+ ...sessionConfig,
1038
+ streaming: this.isPersistent ? true : streaming
1039
+ });
1040
+ if (this.isPersistent) {
1041
+ this.persistentSession = session2;
1042
+ this._sessionId = session2.sessionId;
1043
+ this._persistentModel = options.model;
1044
+ }
1045
+ return { session: session2, isNew: false };
1046
+ } catch {
1047
+ }
1048
+ }
1049
+ const session = await client.createSession({
1050
+ ...sessionConfig,
1051
+ streaming: this.isPersistent ? true : streaming
1052
+ });
1053
+ if (this.isPersistent) {
1054
+ this.persistentSession = session;
1055
+ this._sessionId = session.sessionId;
1056
+ this._persistentModel = options.model;
1057
+ }
1058
+ return { session, isNew: true };
1059
+ }
1060
+ // ─── executeRun ─────────────────────────────────────────────────
1061
+ async executeRun(messages, options, signal) {
1062
+ this.checkAbort(signal);
1063
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
1064
+ this.activeSession = session;
1065
+ const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1066
+ const tracker = new ToolCallTracker();
1067
+ const toolCalls = [];
1068
+ let usage;
1069
+ const unsubscribe = session.on((event) => {
1070
+ if (event.type === "tool.execution_start") {
1071
+ tracker.trackStart(
1072
+ String(event.data.toolCallId ?? ""),
1073
+ String(event.data.toolName ?? "unknown"),
1074
+ event.data.arguments ?? {}
1075
+ );
1076
+ }
1077
+ if (event.type === "tool.execution_complete") {
1078
+ const info = tracker.getInfo(String(event.data.toolCallId ?? ""));
1079
+ const resultContent = event.data.result?.content;
1080
+ toolCalls.push({
1081
+ toolName: info?.toolName ?? "unknown",
1082
+ args: info?.args ?? {},
1083
+ result: resultContent ?? null,
1084
+ approved: Boolean(event.data.success ?? true)
1085
+ });
1086
+ }
1087
+ if (event.type === "assistant.usage") {
1088
+ usage = {
1089
+ promptTokens: Number(event.data.inputTokens ?? 0),
1090
+ completionTokens: Number(event.data.outputTokens ?? 0)
1091
+ };
1092
+ }
1093
+ });
1094
+ const onAbort = () => {
1095
+ session.abort().catch(() => {
1096
+ });
1097
+ };
1098
+ signal.addEventListener("abort", onAbort, { once: true });
1099
+ try {
1100
+ const response = this.sendAndWaitTimeout !== void 0 ? await session.sendAndWait({ prompt }, this.sendAndWaitTimeout) : await session.sendAndWait({ prompt });
1101
+ const output = response?.data?.content ?? null;
1102
+ return {
1103
+ output,
1104
+ structuredOutput: void 0,
1105
+ toolCalls,
1106
+ messages: [
1107
+ ...messages,
1108
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1109
+ ],
1110
+ usage
1111
+ };
1112
+ } catch (error) {
1113
+ this.clearPersistentSession();
1114
+ throw error;
1115
+ } finally {
1116
+ this.activeSession = null;
1117
+ signal.removeEventListener("abort", onAbort);
1118
+ unsubscribe();
1119
+ tracker.clear();
1120
+ if (!this.isPersistent) {
1121
+ session.destroy().catch(() => {
1122
+ });
1123
+ }
1124
+ }
1125
+ }
1126
+ // ─── executeRunStructured ───────────────────────────────────────
1127
+ async executeRunStructured(messages, schema, options, signal) {
1128
+ const jsonSchema = zodToJsonSchema(schema.schema);
1129
+ const instruction = `
1130
+
1131
+ You MUST respond with ONLY valid JSON matching this schema:
1132
+ ` + JSON.stringify(jsonSchema, null, 2);
1133
+ const augmented = [...messages];
1134
+ const lastIdx = augmented.length - 1;
1135
+ if (lastIdx >= 0 && augmented[lastIdx].role === "user") {
1136
+ const orig = augmented[lastIdx];
1137
+ augmented[lastIdx] = {
1138
+ role: "user",
1139
+ content: getTextContent(orig.content) + instruction
1140
+ };
1141
+ }
1142
+ const result = await this.executeRun(augmented, options, signal);
1143
+ let structuredOutput;
1144
+ if (result.output) {
1145
+ try {
1146
+ const jsonMatch = result.output.match(
1147
+ /```(?:json)?\s*([\s\S]*?)```/
1148
+ );
1149
+ const raw = jsonMatch ? jsonMatch[1].trim() : result.output.trim();
1150
+ structuredOutput = schema.schema.parse(JSON.parse(raw));
1151
+ } catch {
1152
+ }
1153
+ }
1154
+ return {
1155
+ ...result,
1156
+ structuredOutput
1157
+ };
1158
+ }
1159
+ // ─── executeStream ──────────────────────────────────────────────
1160
+ async *executeStream(messages, options, signal) {
1161
+ this.checkAbort(signal);
1162
+ const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
1163
+ this.activeSession = session;
1164
+ if (isNewSession) {
1165
+ yield this.emitSessionInfo(session.sessionId);
1166
+ }
1167
+ const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1168
+ const tracker = new ToolCallTracker();
1169
+ const thinkingTracker = new ThinkingTracker();
1170
+ const queue = [];
1171
+ let notify = null;
1172
+ const push = (item) => {
1173
+ queue.push(item);
1174
+ if (notify) {
1175
+ notify();
1176
+ notify = null;
1177
+ }
1178
+ };
1179
+ const waitForItem = () => new Promise((resolve) => {
1180
+ notify = resolve;
1181
+ });
1182
+ const unsubscribe = session.on((event) => {
1183
+ const mapped = mapSessionEvent(event, tracker, thinkingTracker);
1184
+ if (mapped) {
1185
+ if (Array.isArray(mapped)) {
1186
+ for (const e of mapped) push({ event: e });
1187
+ } else {
1188
+ push({ event: mapped });
1189
+ }
1190
+ }
1191
+ if (event.type === "session.idle") {
1192
+ if (thinkingTracker.endThinking()) {
1193
+ push({ event: { type: "thinking_end" } });
1194
+ }
1195
+ push({ done: true });
1196
+ } else if (event.type === "session.error") {
1197
+ console.error("[copilot] session.error:", JSON.stringify(event.data));
1198
+ push({
1199
+ error: new Error(
1200
+ String(event.data.message ?? "Session error")
1201
+ )
1202
+ });
1203
+ }
1204
+ });
1205
+ const onAbort = () => {
1206
+ session.abort().catch(() => {
1207
+ });
1208
+ push({ error: new AbortError() });
1209
+ };
1210
+ signal.addEventListener("abort", onAbort, { once: true });
1211
+ try {
1212
+ await session.send({ prompt });
1213
+ while (true) {
1214
+ while (queue.length === 0) await waitForItem();
1215
+ const item = queue.shift();
1216
+ if ("done" in item) break;
1217
+ if ("error" in item) {
1218
+ this.clearPersistentSession();
1219
+ throw item.error;
1220
+ }
1221
+ yield item.event;
1222
+ }
1223
+ } catch (error) {
1224
+ this.clearPersistentSession();
1225
+ throw error;
1226
+ } finally {
1227
+ this.activeSession = null;
1228
+ signal.removeEventListener("abort", onAbort);
1229
+ unsubscribe();
1230
+ tracker.clear();
1231
+ if (!this.isPersistent) {
1232
+ session.destroy().catch(() => {
1233
+ });
1234
+ }
1235
+ }
1236
+ }
1237
+ // ─── dispose ────────────────────────────────────────────────────
1238
+ dispose() {
1239
+ if (this.persistentSession) {
1240
+ this.persistentSession.destroy().catch(() => {
1241
+ });
1242
+ this.persistentSession = null;
1243
+ this._sessionId = void 0;
1244
+ }
1245
+ super.dispose();
1246
+ }
1247
+ };
1248
+ CopilotAgentService = class {
1249
+ name = "copilot";
1250
+ client = null;
1251
+ clientPromise = null;
1252
+ disposed = false;
1253
+ options;
1254
+ constructor(options) {
1255
+ this.options = options;
1256
+ }
1257
+ async ensureClient() {
1258
+ if (this.disposed) throw new DisposedError("CopilotAgentService");
1259
+ if (this.client) return this.client;
1260
+ if (this.clientPromise) return this.clientPromise;
1261
+ this.clientPromise = (async () => {
1262
+ try {
1263
+ const sdk = await loadSDK();
1264
+ const client = new sdk.CopilotClient({
1265
+ cliPath: this.options.cliPath,
1266
+ cwd: this.options.workingDirectory,
1267
+ useStdio: true,
1268
+ autoStart: false,
1269
+ autoRestart: true,
1270
+ logLevel: "error",
1271
+ githubToken: this.options.githubToken,
1272
+ useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
1273
+ ...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
1274
+ ...this.options.env ? { env: { ...process.env, ...this.options.env } } : {}
1275
+ });
1276
+ const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
1277
+ await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
1278
+ const auth = await withTimeout(
1279
+ client.getAuthStatus(),
1280
+ startupTimeout,
1281
+ "Auth status check timed out \u2014 token may be expired"
1282
+ );
1283
+ if (!auth.isAuthenticated) {
1284
+ await client.stop();
1285
+ throw new SubprocessError(
1286
+ "Not authenticated with GitHub Copilot. Run 'copilot auth login' or set GITHUB_TOKEN."
1287
+ );
1288
+ }
1289
+ this.client = client;
1290
+ return client;
1291
+ } catch (e) {
1292
+ this.clientPromise = null;
1293
+ throw e;
1294
+ }
1295
+ })();
1296
+ return this.clientPromise;
1297
+ }
1298
+ createAgent(config) {
1299
+ if (this.disposed) throw new DisposedError("CopilotAgentService");
1300
+ return new CopilotAgent(config, () => this.ensureClient(), this.options.timeout, this.options.resumeSessionId);
1301
+ }
1302
+ async listModels() {
1303
+ const client = await this.ensureClient();
1304
+ const models = await client.listModels();
1305
+ return models.map((m) => ({
1306
+ id: m.id,
1307
+ name: m.name,
1308
+ provider: "copilot",
1309
+ ...m.capabilities?.limits?.max_context_window_tokens != null && {
1310
+ contextWindow: m.capabilities.limits.max_context_window_tokens
1311
+ }
1312
+ }));
1313
+ }
1314
+ async validate() {
1315
+ const errors = [];
1316
+ try {
1317
+ const client = await this.ensureClient();
1318
+ const auth = await client.getAuthStatus();
1319
+ if (!auth.isAuthenticated) {
1320
+ errors.push(
1321
+ "Not authenticated with GitHub Copilot. Run 'copilot auth login'."
1322
+ );
1323
+ }
1324
+ } catch (e) {
1325
+ errors.push(
1326
+ `Failed to connect to Copilot CLI: ${e instanceof Error ? e.message : String(e)}`
1327
+ );
1328
+ }
1329
+ return { valid: errors.length === 0, errors };
1330
+ }
1331
+ async dispose() {
1332
+ if (this.disposed) return;
1333
+ this.disposed = true;
1334
+ if (this.clientPromise) {
1335
+ try {
1336
+ await this.clientPromise;
1337
+ } catch {
1338
+ }
1339
+ }
1340
+ if (this.client) {
1341
+ await this.client.stop();
1342
+ this.client = null;
1343
+ }
1344
+ this.clientPromise = null;
1345
+ }
1346
+ };
1347
+ }
1348
+ });
1349
+
1350
+ // src/backends/claude.ts
1351
+ var claude_exports = {};
1352
+ __export(claude_exports, {
1353
+ _injectSDK: () => _injectSDK2,
1354
+ _resetSDK: () => _resetSDK2,
1355
+ createClaudeService: () => createClaudeService
1356
+ });
1357
+ function mcpToolName(toolName) {
1358
+ return `${MCP_TOOL_PREFIX}${toolName}`;
1359
+ }
1360
+ function stripMcpPrefix(name) {
1361
+ return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
1362
+ }
1363
+ async function loadSDK2() {
1364
+ if (_sdkMock2) return _sdkMock2;
1365
+ try {
1366
+ return await import('@anthropic-ai/claude-agent-sdk');
1367
+ } catch {
1368
+ throw new SubprocessError(
1369
+ "@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
1370
+ );
1371
+ }
1372
+ }
1373
+ function _injectSDK2(mock) {
1374
+ _sdkMock2 = mock;
1375
+ }
1376
+ function _resetSDK2() {
1377
+ _sdkMock2 = null;
1378
+ }
1379
+ function normalizeAskUserInput(args) {
1380
+ if (typeof args.question === "string") {
1381
+ return {
1382
+ question: args.question,
1383
+ choices: Array.isArray(args.choices) ? args.choices : void 0,
1384
+ allowFreeform: args.allowFreeform !== false
1385
+ };
1386
+ }
1387
+ const questions = args.questions;
1388
+ if (questions && questions.length > 0) {
1389
+ const first = questions[0];
1390
+ return {
1391
+ question: first.question,
1392
+ choices: first.options?.map((o) => o.label),
1393
+ allowFreeform: true
1394
+ };
1395
+ }
1396
+ return { question: JSON.stringify(args), allowFreeform: true };
1397
+ }
1398
+ function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
1399
+ if (tools.length === 0 && !onAskUser) return void 0;
1400
+ const mcpTools = tools.map((tool) => {
1401
+ const zodSchema = tool.parameters;
1402
+ const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
1403
+ return sdk.tool(
1404
+ tool.name,
1405
+ tool.description ?? "",
1406
+ inputSchema,
1407
+ async (args) => {
1408
+ const result = await tool.execute(args);
1409
+ if (toolResultCapture) {
1410
+ toolResultCapture.set(tool.name, result);
1411
+ }
1412
+ return {
1413
+ content: [
1414
+ {
1415
+ type: "text",
1416
+ text: typeof result === "string" ? result : JSON.stringify(result)
1417
+ }
1418
+ ]
1419
+ };
1420
+ }
1421
+ );
1422
+ });
1423
+ if (onAskUser) {
1424
+ const askUserTool = sdk.tool(
1425
+ "ask_user",
1426
+ "Ask the user a question and wait for their response",
1427
+ {
1428
+ question: { type: "string", description: "The question to ask the user" },
1429
+ choices: {
1430
+ type: "array",
1431
+ items: { type: "string" },
1432
+ description: "Optional list of choices for multiple choice"
1433
+ },
1434
+ questions: {
1435
+ type: "array",
1436
+ items: {
1437
+ type: "object",
1438
+ properties: {
1439
+ question: { type: "string" },
1440
+ options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
1441
+ }
1442
+ },
1443
+ description: "Alternative nested question format"
1444
+ }
1445
+ },
1446
+ async (args) => {
1447
+ const normalized = normalizeAskUserInput(args);
1448
+ const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
1449
+ return {
1450
+ content: [{ type: "text", text: response.answer }]
1451
+ };
1452
+ }
1453
+ );
1454
+ mcpTools.push(askUserTool);
1455
+ }
1456
+ return sdk.createSdkMcpServer({
1457
+ name: MCP_SERVER_NAME,
1458
+ version: "1.0.0",
1459
+ tools: mcpTools
1460
+ });
1461
+ }
1462
+ function scopeToDestination(scope) {
1463
+ switch (scope) {
1464
+ case "once":
1465
+ return "session";
1466
+ case "session":
1467
+ return "session";
1468
+ case "project":
1469
+ return "projectSettings";
1470
+ case "always":
1471
+ return "userSettings";
1472
+ }
1473
+ }
1474
+ function destinationToScope(dest) {
1475
+ switch (dest) {
1476
+ case "session":
1477
+ case "cliArg":
1478
+ return "session";
1479
+ case "projectSettings":
1480
+ case "localSettings":
1481
+ return "project";
1482
+ case "userSettings":
1483
+ return "always";
1484
+ }
1485
+ }
1486
+ function extractSuggestedScope(suggestions) {
1487
+ if (!suggestions || suggestions.length === 0) return void 0;
1488
+ return destinationToScope(suggestions[0].destination);
1489
+ }
1490
+ function buildCanUseTool(config) {
1491
+ const onPermission = config.supervisor?.onPermission;
1492
+ if (!onPermission) return void 0;
1493
+ const permissionStore = config.permissionStore;
1494
+ return async (rawToolName, input, options) => {
1495
+ const toolName = stripMcpPrefix(rawToolName);
1496
+ if (permissionStore && await permissionStore.isApproved(toolName)) {
1497
+ return {
1498
+ behavior: "allow",
1499
+ toolUseID: options.toolUseID
1500
+ };
1501
+ }
1502
+ const unifiedRequest = {
1503
+ toolName,
1504
+ toolArgs: input,
1505
+ toolCallId: options.toolUseID,
1506
+ suggestedScope: extractSuggestedScope(options.suggestions),
1507
+ rawSDKRequest: { toolName, input, ...options }
1508
+ };
1509
+ const decision = await onPermission(
1510
+ unifiedRequest,
1511
+ options.signal
1512
+ );
1513
+ if (decision.allowed) {
1514
+ if (permissionStore && decision.scope) {
1515
+ await permissionStore.approve(toolName, decision.scope);
1516
+ }
1517
+ const result = {
1518
+ behavior: "allow",
1519
+ toolUseID: options.toolUseID
1520
+ };
1521
+ if (decision.modifiedInput) {
1522
+ result.updatedInput = decision.modifiedInput;
1523
+ }
1524
+ if (decision.scope && decision.scope !== "once" && options.suggestions) {
1525
+ result.updatedPermissions = options.suggestions.map((s) => ({
1526
+ ...s,
1527
+ destination: scopeToDestination(decision.scope)
1528
+ }));
1529
+ }
1530
+ return result;
1531
+ }
1532
+ return {
1533
+ behavior: "deny",
1534
+ message: decision.reason ?? "Permission denied",
1535
+ toolUseID: options.toolUseID
1536
+ };
1537
+ };
1538
+ }
1539
+ function aggregateUsage(modelUsage) {
1540
+ let promptTokens = 0;
1541
+ let completionTokens = 0;
1542
+ for (const u of Object.values(modelUsage ?? {})) {
1543
+ promptTokens += u.inputTokens ?? 0;
1544
+ completionTokens += u.outputTokens ?? 0;
1545
+ }
1546
+ return { promptTokens, completionTokens };
1547
+ }
1548
+ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
1549
+ switch (msg.type) {
1550
+ case "assistant": {
1551
+ const betaMessage = msg.message;
1552
+ if (!betaMessage?.content) return null;
1553
+ const events = [];
1554
+ for (const block of betaMessage.content) {
1555
+ if (block.type === "tool_use") {
1556
+ const toolCallId = String(block.id ?? "");
1557
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1558
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1559
+ if (toolCallTracker) {
1560
+ toolCallTracker.trackStart(toolCallId, toolName);
1561
+ }
1562
+ events.push({
1563
+ type: "tool_call_start",
1564
+ toolCallId,
1565
+ toolName,
1566
+ args: block.input ?? {}
1567
+ });
1568
+ }
1569
+ }
1570
+ return events.length > 0 ? events : null;
1571
+ }
1572
+ case "user": {
1573
+ const toolResult = msg.tool_use_result;
1574
+ if (toolResult !== void 0) {
1575
+ return null;
1576
+ }
1577
+ return null;
1578
+ }
1579
+ case "tool_use_summary": {
1580
+ const summary = msg.summary;
1581
+ const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
1582
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
1583
+ const precedingIds = msg.preceding_tool_use_ids;
1584
+ let toolCallId = "";
1585
+ if (precedingIds && precedingIds.length > 0) {
1586
+ toolCallId = precedingIds[0];
1587
+ if (toolCallTracker) toolCallTracker.consumeToolCallId(toolName);
1588
+ } else if (toolCallTracker) {
1589
+ toolCallId = toolCallTracker.consumeToolCallId(toolName);
1590
+ }
1591
+ return {
1592
+ type: "tool_call_end",
1593
+ toolCallId,
1594
+ toolName,
1595
+ result: summary ?? null
1596
+ };
1597
+ }
1598
+ case "stream_event": {
1599
+ const event = msg.event;
1600
+ if (!event) return null;
1601
+ if (event.type === "content_block_delta" && event.index !== void 0 && thinkingBlockIndices?.has(event.index)) {
1602
+ const thinkingText = String(event.delta?.thinking ?? event.delta?.text ?? "");
1603
+ if (thinkingText) {
1604
+ return { type: "thinking_delta", text: thinkingText };
1605
+ }
1606
+ return null;
1607
+ }
1608
+ if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && event.delta.text) {
1609
+ return { type: "text_delta", text: event.delta.text };
1610
+ }
1611
+ if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
1612
+ if (thinkingBlockIndices && event.index !== void 0) {
1613
+ thinkingBlockIndices.add(event.index);
1614
+ }
1615
+ return { type: "thinking_start" };
1616
+ }
1617
+ if (event.type === "content_block_stop" && event.index !== void 0 && thinkingBlockIndices?.has(event.index)) {
1618
+ thinkingBlockIndices.delete(event.index);
1619
+ return { type: "thinking_end" };
1620
+ }
1621
+ return null;
1622
+ }
1623
+ case "tool_progress": {
1624
+ return null;
1625
+ }
1626
+ case "result": {
1627
+ if (msg.subtype === "success") {
1628
+ const r = msg;
1629
+ return {
1630
+ type: "usage_update",
1631
+ ...aggregateUsage(r.modelUsage)
1632
+ };
1633
+ }
1634
+ if (msg.is_error) {
1635
+ const r = msg;
1636
+ const errorMsg = r.errors?.join("; ") ?? "Unknown error";
1637
+ const code = classifyAgentError(errorMsg);
1638
+ return {
1639
+ type: "error",
1640
+ error: errorMsg,
1641
+ recoverable: isRecoverableErrorCode(code),
1642
+ code
1643
+ };
1644
+ }
1645
+ return null;
1646
+ }
1647
+ default:
1648
+ return null;
1649
+ }
1650
+ }
1651
+ function createClaudeService(options) {
1652
+ return new ClaudeAgentService(options);
1653
+ }
1654
+ var MCP_SERVER_NAME, MCP_TOOL_PREFIX, CLAUDE_INTERNAL_TOOL_NAMES, _sdkMock2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
1655
+ var init_claude = __esm({
1656
+ "src/backends/claude.ts"() {
1657
+ init_types2();
1658
+ init_base_agent();
1659
+ init_errors2();
1660
+ init_schema();
1661
+ init_shared();
1662
+ MCP_SERVER_NAME = "agent-sdk-tools";
1663
+ MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
1664
+ CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
1665
+ _sdkMock2 = null;
1666
+ ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
1667
+ ANTHROPIC_API_VERSION = "2023-06-01";
1668
+ ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
1669
+ ClaudeToolCallTracker = class {
1670
+ queues = /* @__PURE__ */ new Map();
1671
+ trackStart(toolCallId, toolName) {
1672
+ if (!this.queues.has(toolName)) {
1673
+ this.queues.set(toolName, []);
1674
+ }
1675
+ this.queues.get(toolName).push(toolCallId);
1676
+ }
1677
+ /** Peek at the current tool call ID for a tool name (does not consume) */
1678
+ peekToolCallId(toolName) {
1679
+ const queue = this.queues.get(toolName);
1680
+ if (!queue || queue.length === 0) return "";
1681
+ return queue[0];
1682
+ }
1683
+ /** Consume and return the first tool call ID for a tool name */
1684
+ consumeToolCallId(toolName) {
1685
+ const queue = this.queues.get(toolName);
1686
+ if (!queue || queue.length === 0) return "";
1687
+ return queue.shift();
1688
+ }
1689
+ clear() {
1690
+ this.queues.clear();
1691
+ }
1692
+ };
1693
+ ClaudeAgent = class extends BaseAgent {
1694
+ backendName = "claude";
1695
+ options;
1696
+ tools;
1697
+ canUseTool;
1698
+ isPersistent;
1699
+ _sessionId;
1700
+ activeQuery = null;
1701
+ constructor(config, options) {
1702
+ super(config);
1703
+ this.options = options;
1704
+ this.tools = config.tools ?? [];
1705
+ this.canUseTool = buildCanUseTool(config);
1706
+ this.isPersistent = config.sessionMode === "persistent";
1707
+ if (options.resumeSessionId) {
1708
+ this._sessionId = options.resumeSessionId;
1709
+ }
1710
+ }
1711
+ get sessionId() {
1712
+ return this._sessionId;
1713
+ }
1714
+ async interrupt() {
1715
+ try {
1716
+ if (this.activeQuery) {
1717
+ await this.activeQuery.interrupt();
1718
+ }
1719
+ } catch {
1720
+ } finally {
1721
+ this.abort();
1722
+ }
1723
+ }
1724
+ /** Clear persistent session state after an error so next call starts fresh */
1725
+ clearPersistentSession() {
1726
+ this._sessionId = void 0;
1727
+ }
1728
+ emitSessionInfo(sessionId) {
1729
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
1730
+ const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
1731
+ return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
1732
+ }
1733
+ buildQueryOptions(signal, options) {
1734
+ const ac = new AbortController();
1735
+ signal.addEventListener("abort", () => ac.abort(), { once: true });
1736
+ const opts = {
1737
+ abortController: ac,
1738
+ model: options.model,
1739
+ maxTurns: this.options.maxTurns,
1740
+ cwd: this.options.workingDirectory,
1741
+ pathToClaudeCodeExecutable: this.options.cliPath,
1742
+ persistSession: this.isPersistent,
1743
+ includePartialMessages: true,
1744
+ canUseTool: this.canUseTool
1745
+ };
1746
+ if (this.isPersistent && this._sessionId) {
1747
+ opts.resume = this._sessionId;
1748
+ }
1749
+ if (this.config.systemPrompt) {
1750
+ opts.systemPrompt = this.config.systemPrompt;
1751
+ }
1752
+ if (this.options.oauthToken || this.options.env) {
1753
+ opts.env = {
1754
+ ...process.env,
1755
+ ...this.options.env ?? {},
1756
+ ...this.options.oauthToken ? { CLAUDE_CODE_OAUTH_TOKEN: this.options.oauthToken } : {}
1757
+ };
1758
+ }
1759
+ if (opts.canUseTool && !opts.permissionMode) {
1760
+ opts.permissionMode = "default";
1761
+ }
1762
+ if (this.config.availableTools) {
1763
+ opts.tools = [...this.config.availableTools];
1764
+ }
1765
+ return opts;
1766
+ }
1767
+ async buildMcpConfig(opts, toolResultCapture) {
1768
+ const onAskUser = this.config.supervisor?.onAskUser;
1769
+ if (this.tools.length === 0 && !onAskUser) return opts;
1770
+ const sdk = await loadSDK2();
1771
+ const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
1772
+ if (mcpServer) {
1773
+ opts.mcpServers = {
1774
+ [MCP_SERVER_NAME]: mcpServer
1775
+ };
1776
+ const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
1777
+ if (onAskUser) {
1778
+ mcpToolNames.push(mcpToolName("ask_user"));
1779
+ }
1780
+ opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
1781
+ }
1782
+ if (onAskUser) {
1783
+ opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
1784
+ }
1785
+ return opts;
1786
+ }
1787
+ // ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
1788
+ /** Setup a retry query: clear session, rebuild with full history */
1789
+ async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
1790
+ this.clearPersistentSession();
1791
+ const retryPrompt = buildContextualPrompt(messages);
1792
+ let retryOpts = this.buildQueryOptions(signal, options);
1793
+ toolResultCapture.clear();
1794
+ retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
1795
+ modifyOpts?.(retryOpts);
1796
+ const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
1797
+ this.activeQuery = retryQ;
1798
+ return retryQ;
1799
+ }
1800
+ /** Extract tool_use blocks from an assistant SDK message into toolCalls array */
1801
+ collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
1802
+ if (msg.type !== "assistant") return;
1803
+ const betaMessage = msg.message;
1804
+ if (!betaMessage?.content) return;
1805
+ for (const block of betaMessage.content) {
1806
+ if (block.type === "tool_use") {
1807
+ const toolName = stripMcpPrefix(block.name ?? "unknown");
1808
+ if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
1809
+ toolCalls.push({
1810
+ toolName,
1811
+ args: block.input ?? {},
1812
+ result: toolResultCapture.get(toolName) ?? null,
1813
+ approved: true
1814
+ });
1815
+ }
1816
+ }
1817
+ }
1818
+ /** Back-fill tool results from capture map on summary/result messages */
1819
+ backfillToolResults(msg, toolCalls, toolResultCapture) {
1820
+ if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
1821
+ for (const tc of toolCalls) {
1822
+ if (tc.result === null) {
1823
+ const captured = toolResultCapture.get(tc.toolName);
1824
+ if (captured !== void 0) tc.result = captured;
1825
+ }
1826
+ }
1827
+ }
1828
+ /** Wrap retry inner loop with shared error handling */
1829
+ async withRetryErrorHandling(signal, fn) {
1830
+ try {
1831
+ return await fn();
1832
+ } catch (retryError) {
1833
+ if (this.isPersistent) this.clearPersistentSession();
1834
+ if (signal.aborted) throw new AbortError();
1835
+ throw retryError;
1836
+ } finally {
1837
+ this.activeQuery = null;
1838
+ }
1839
+ }
1840
+ // ─── executeRun ─────────────────────────────────────────────────
1841
+ async executeRun(messages, options, signal) {
1842
+ this.checkAbort(signal);
1843
+ const sdk = await loadSDK2();
1844
+ const isResuming = this.isPersistent && this._sessionId !== void 0;
1845
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1846
+ let opts = this.buildQueryOptions(signal, options);
1847
+ const toolResultCapture = /* @__PURE__ */ new Map();
1848
+ opts = await this.buildMcpConfig(opts, toolResultCapture);
1849
+ const q = sdk.query({ prompt, options: opts });
1850
+ this.activeQuery = q;
1851
+ const toolCalls = [];
1852
+ let output = null;
1853
+ let usage;
1854
+ try {
1855
+ for await (const msg of q) {
1856
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1857
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1858
+ if (msg.type === "result") {
1859
+ if (msg.subtype === "success") {
1860
+ const r = msg;
1861
+ output = r.result;
1862
+ usage = aggregateUsage(r.modelUsage);
1863
+ if (this.isPersistent && r.session_id) {
1864
+ this._sessionId = r.session_id;
1865
+ }
1866
+ } else if (msg.is_error) {
1867
+ const r = msg;
1868
+ throw new Error(
1869
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
1870
+ );
1871
+ }
1872
+ }
1873
+ }
1874
+ } catch (e) {
1875
+ if (signal.aborted) throw new AbortError();
1876
+ if (isResuming && this.isPersistent) {
1877
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
1878
+ toolCalls.length = 0;
1879
+ output = null;
1880
+ return this.withRetryErrorHandling(signal, async () => {
1881
+ for await (const msg of retryQ) {
1882
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1883
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1884
+ if (msg.type === "result") {
1885
+ if (msg.subtype === "success") {
1886
+ const r = msg;
1887
+ output = r.result;
1888
+ usage = aggregateUsage(r.modelUsage);
1889
+ if (this.isPersistent && r.session_id) {
1890
+ this._sessionId = r.session_id;
1891
+ }
1892
+ } else if (msg.is_error) {
1893
+ const r = msg;
1894
+ throw new Error(
1895
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
1896
+ );
1897
+ }
1898
+ }
1899
+ }
1900
+ return {
1901
+ output,
1902
+ structuredOutput: void 0,
1903
+ toolCalls,
1904
+ messages: [
1905
+ ...messages,
1906
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1907
+ ],
1908
+ usage
1909
+ };
1910
+ });
1911
+ }
1912
+ if (this.isPersistent) this.clearPersistentSession();
1913
+ throw e;
1914
+ } finally {
1915
+ this.activeQuery = null;
1916
+ }
1917
+ return {
1918
+ output,
1919
+ structuredOutput: void 0,
1920
+ toolCalls,
1921
+ messages: [
1922
+ ...messages,
1923
+ ...output !== null ? [{ role: "assistant", content: output }] : []
1924
+ ],
1925
+ usage
1926
+ };
1927
+ }
1928
+ // ─── executeRunStructured ───────────────────────────────────────
1929
+ async executeRunStructured(messages, schema, options, signal) {
1930
+ this.checkAbort(signal);
1931
+ const sdk = await loadSDK2();
1932
+ const isResuming = this.isPersistent && this._sessionId !== void 0;
1933
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
1934
+ let opts = this.buildQueryOptions(signal, options);
1935
+ const toolResultCapture = /* @__PURE__ */ new Map();
1936
+ opts = await this.buildMcpConfig(opts, toolResultCapture);
1937
+ const jsonSchema = zodToJsonSchema(schema.schema);
1938
+ opts.outputFormat = {
1939
+ type: "json_schema",
1940
+ schema: jsonSchema
1941
+ };
1942
+ const q = sdk.query({ prompt, options: opts });
1943
+ this.activeQuery = q;
1944
+ const toolCalls = [];
1945
+ let output = null;
1946
+ let structuredOutput;
1947
+ let usage;
1948
+ try {
1949
+ for await (const msg of q) {
1950
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
1951
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
1952
+ if (msg.type === "result" && msg.subtype === "success") {
1953
+ const r = msg;
1954
+ output = r.result;
1955
+ if (r.structured_output !== void 0) {
1956
+ try {
1957
+ structuredOutput = schema.schema.parse(r.structured_output);
1958
+ } catch {
1959
+ try {
1960
+ structuredOutput = schema.schema.parse(JSON.parse(r.result));
1961
+ } catch {
1962
+ }
1963
+ }
1964
+ } else if (r.result) {
1965
+ try {
1966
+ const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
1967
+ const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
1968
+ structuredOutput = schema.schema.parse(JSON.parse(raw));
1969
+ } catch {
1970
+ }
1971
+ }
1972
+ usage = aggregateUsage(r.modelUsage);
1973
+ if (this.isPersistent && r.session_id) {
1974
+ this._sessionId = r.session_id;
1975
+ }
1976
+ } else if (msg.type === "result" && msg.is_error) {
1977
+ const r = msg;
1978
+ throw new Error(
1979
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
1980
+ );
1981
+ }
1982
+ }
1983
+ } catch (e) {
1984
+ if (signal.aborted) throw new AbortError();
1985
+ if (isResuming && this.isPersistent) {
1986
+ const retryQ = await this.prepareRetryQuery(
1987
+ sdk,
1988
+ messages,
1989
+ signal,
1990
+ options,
1991
+ toolResultCapture,
1992
+ (opts2) => {
1993
+ opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
1994
+ }
1995
+ );
1996
+ toolCalls.length = 0;
1997
+ output = null;
1998
+ structuredOutput = void 0;
1999
+ return this.withRetryErrorHandling(signal, async () => {
2000
+ for await (const msg of retryQ) {
2001
+ this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
2002
+ this.backfillToolResults(msg, toolCalls, toolResultCapture);
2003
+ if (msg.type === "result" && msg.subtype === "success") {
2004
+ const r = msg;
2005
+ output = r.result;
2006
+ if (r.structured_output !== void 0) {
2007
+ try {
2008
+ structuredOutput = schema.schema.parse(r.structured_output);
2009
+ } catch {
2010
+ try {
2011
+ structuredOutput = schema.schema.parse(JSON.parse(r.result));
2012
+ } catch {
2013
+ }
2014
+ }
2015
+ } else if (r.result) {
2016
+ try {
2017
+ const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
2018
+ const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
2019
+ structuredOutput = schema.schema.parse(JSON.parse(raw));
2020
+ } catch {
2021
+ }
2022
+ }
2023
+ usage = aggregateUsage(r.modelUsage);
2024
+ if (this.isPersistent && r.session_id) {
2025
+ this._sessionId = r.session_id;
2026
+ }
2027
+ } else if (msg.type === "result" && msg.is_error) {
2028
+ const r = msg;
2029
+ throw new Error(
2030
+ `Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
2031
+ );
2032
+ }
2033
+ }
2034
+ return {
2035
+ output,
2036
+ structuredOutput,
2037
+ toolCalls,
2038
+ messages: [
2039
+ ...messages,
2040
+ ...output !== null ? [{ role: "assistant", content: output }] : []
2041
+ ],
2042
+ usage
2043
+ };
2044
+ });
2045
+ }
2046
+ if (this.isPersistent) this.clearPersistentSession();
2047
+ throw e;
2048
+ } finally {
2049
+ this.activeQuery = null;
2050
+ }
2051
+ return {
2052
+ output,
2053
+ structuredOutput,
2054
+ toolCalls,
2055
+ messages: [
2056
+ ...messages,
2057
+ ...output !== null ? [{ role: "assistant", content: output }] : []
2058
+ ],
2059
+ usage
2060
+ };
2061
+ }
2062
+ // ─── executeStream ──────────────────────────────────────────────
2063
+ async *executeStream(messages, options, signal) {
2064
+ this.checkAbort(signal);
2065
+ const sdk = await loadSDK2();
2066
+ const isResuming = this.isPersistent && this._sessionId !== void 0;
2067
+ const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
2068
+ let opts = this.buildQueryOptions(signal, options);
2069
+ const toolResultCapture = /* @__PURE__ */ new Map();
2070
+ opts = await this.buildMcpConfig(opts, toolResultCapture);
2071
+ const q = sdk.query({ prompt, options: opts });
2072
+ this.activeQuery = q;
2073
+ const thinkingBlockIndices = /* @__PURE__ */ new Set();
2074
+ const toolCallTracker = new ClaudeToolCallTracker();
2075
+ const pendingStreamToolCalls = /* @__PURE__ */ new Map();
2076
+ let hasStreamedText = false;
2077
+ try {
2078
+ for await (const msg of q) {
2079
+ if (signal.aborted) throw new AbortError();
2080
+ const events = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
2081
+ if (events) {
2082
+ const mapped = Array.isArray(events) ? events : [events];
2083
+ for (const e of mapped) {
2084
+ if (e.type === "tool_call_start") {
2085
+ pendingStreamToolCalls.set(e.toolCallId, e.toolName);
2086
+ }
2087
+ if (e.type === "tool_call_end" && toolResultCapture.has(e.toolName)) {
2088
+ e.result = toolResultCapture.get(e.toolName);
2089
+ toolResultCapture.delete(e.toolName);
2090
+ pendingStreamToolCalls.delete(e.toolCallId);
2091
+ } else if (e.type === "tool_call_end") {
2092
+ pendingStreamToolCalls.delete(e.toolCallId);
2093
+ }
2094
+ if (e.type === "text_delta") hasStreamedText = true;
2095
+ yield e;
2096
+ }
2097
+ }
2098
+ if (msg.type === "result" && msg.subtype === "success") {
2099
+ const r = msg;
2100
+ for (const [toolCallId, toolName] of pendingStreamToolCalls) {
2101
+ if (toolResultCapture.has(toolName)) {
2102
+ yield {
2103
+ type: "tool_call_end",
2104
+ toolCallId,
2105
+ toolName,
2106
+ result: toolResultCapture.get(toolName)
2107
+ };
2108
+ toolResultCapture.delete(toolName);
2109
+ }
2110
+ }
2111
+ pendingStreamToolCalls.clear();
2112
+ if (r.session_id) {
2113
+ if (this.isPersistent) {
2114
+ this._sessionId = r.session_id;
2115
+ }
2116
+ yield this.emitSessionInfo(r.session_id);
2117
+ }
2118
+ yield {
2119
+ type: "done",
2120
+ finalOutput: hasStreamedText ? null : r.result,
2121
+ ...hasStreamedText ? { streamed: true } : {}
2122
+ };
2123
+ }
2124
+ }
2125
+ } catch (e) {
2126
+ if (signal.aborted) throw new AbortError();
2127
+ if (isResuming && this.isPersistent) {
2128
+ const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
2129
+ const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
2130
+ const retryToolCallTracker = new ClaudeToolCallTracker();
2131
+ const retryPendingToolCalls = /* @__PURE__ */ new Map();
2132
+ let retryHasStreamedText = false;
2133
+ try {
2134
+ for await (const msg of retryQ) {
2135
+ if (signal.aborted) throw new AbortError();
2136
+ const retryEvents = mapSDKMessage(msg, retryThinkingBlockIndices, retryToolCallTracker);
2137
+ if (retryEvents) {
2138
+ const mapped = Array.isArray(retryEvents) ? retryEvents : [retryEvents];
2139
+ for (const ev of mapped) {
2140
+ if (ev.type === "tool_call_start") {
2141
+ retryPendingToolCalls.set(ev.toolCallId, ev.toolName);
2142
+ }
2143
+ if (ev.type === "tool_call_end" && toolResultCapture.has(ev.toolName)) {
2144
+ ev.result = toolResultCapture.get(ev.toolName);
2145
+ toolResultCapture.delete(ev.toolName);
2146
+ retryPendingToolCalls.delete(ev.toolCallId);
2147
+ } else if (ev.type === "tool_call_end") {
2148
+ retryPendingToolCalls.delete(ev.toolCallId);
2149
+ }
2150
+ if (ev.type === "text_delta") retryHasStreamedText = true;
2151
+ yield ev;
2152
+ }
2153
+ }
2154
+ if (msg.type === "result" && msg.subtype === "success") {
2155
+ const r = msg;
2156
+ for (const [toolCallId, toolName] of retryPendingToolCalls) {
2157
+ if (toolResultCapture.has(toolName)) {
2158
+ yield {
2159
+ type: "tool_call_end",
2160
+ toolCallId,
2161
+ toolName,
2162
+ result: toolResultCapture.get(toolName)
2163
+ };
2164
+ toolResultCapture.delete(toolName);
2165
+ }
2166
+ }
2167
+ retryPendingToolCalls.clear();
2168
+ if (r.session_id) {
2169
+ if (this.isPersistent) {
2170
+ this._sessionId = r.session_id;
2171
+ }
2172
+ yield this.emitSessionInfo(r.session_id);
2173
+ }
2174
+ yield {
2175
+ type: "done",
2176
+ finalOutput: retryHasStreamedText ? null : r.result,
2177
+ ...retryHasStreamedText ? { streamed: true } : {}
2178
+ };
2179
+ }
2180
+ }
2181
+ } catch (retryError) {
2182
+ if (this.isPersistent) this.clearPersistentSession();
2183
+ if (signal.aborted) throw new AbortError();
2184
+ throw retryError;
2185
+ } finally {
2186
+ this.activeQuery = null;
2187
+ }
2188
+ return;
2189
+ }
2190
+ if (this.isPersistent) this.clearPersistentSession();
2191
+ throw e;
2192
+ } finally {
2193
+ this.activeQuery = null;
2194
+ }
2195
+ }
2196
+ dispose() {
2197
+ this._sessionId = void 0;
2198
+ super.dispose();
2199
+ }
2200
+ };
2201
+ ClaudeAgentService = class {
2202
+ name = "claude";
2203
+ disposed = false;
2204
+ options;
2205
+ cachedModels = null;
2206
+ constructor(options) {
2207
+ this.options = options;
2208
+ }
2209
+ createAgent(config) {
2210
+ if (this.disposed) throw new DisposedError("ClaudeAgentService");
2211
+ return new ClaudeAgent(config, this.options);
2212
+ }
2213
+ async listModels() {
2214
+ if (this.disposed) throw new DisposedError("ClaudeAgentService");
2215
+ if (this.cachedModels) return this.cachedModels;
2216
+ const token = this.options.oauthToken;
2217
+ if (!token) {
2218
+ return [];
2219
+ }
2220
+ const res = await globalThis.fetch(
2221
+ `${ANTHROPIC_MODELS_URL}?limit=100`,
2222
+ {
2223
+ headers: {
2224
+ Authorization: `Bearer ${token}`,
2225
+ "anthropic-version": ANTHROPIC_API_VERSION,
2226
+ "anthropic-beta": ANTHROPIC_OAUTH_BETA
2227
+ }
2228
+ }
2229
+ );
2230
+ if (!res.ok) {
2231
+ return [];
2232
+ }
2233
+ const body = await res.json();
2234
+ if (!body.data || body.data.length === 0) {
2235
+ return [];
2236
+ }
2237
+ this.cachedModels = body.data.map((m) => ({
2238
+ id: m.id,
2239
+ name: m.display_name,
2240
+ provider: "claude",
2241
+ ...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
2242
+ }));
2243
+ return this.cachedModels;
2244
+ }
2245
+ async validate() {
2246
+ if (this.disposed) throw new DisposedError("ClaudeAgentService");
2247
+ const errors = [];
2248
+ try {
2249
+ await loadSDK2();
2250
+ } catch (e) {
2251
+ errors.push(
2252
+ e instanceof Error ? e.message : String(e)
2253
+ );
2254
+ return { valid: false, errors };
2255
+ }
2256
+ try {
2257
+ const sdk = await loadSDK2();
2258
+ const q = sdk.query({
2259
+ prompt: "echo test",
2260
+ options: {
2261
+ model: "claude-sonnet-4-20250514",
2262
+ pathToClaudeCodeExecutable: this.options.cliPath,
2263
+ cwd: this.options.workingDirectory,
2264
+ persistSession: false,
2265
+ maxTurns: 1,
2266
+ permissionMode: "plan"
2267
+ }
2268
+ });
2269
+ const first = await q.next();
2270
+ q.close();
2271
+ if (first.done) {
2272
+ errors.push("Claude CLI returned no messages \u2014 may not be authenticated.");
2273
+ }
2274
+ } catch (e) {
2275
+ errors.push(
2276
+ `Failed to connect to Claude CLI: ${e instanceof Error ? e.message : String(e)}`
2277
+ );
2278
+ }
2279
+ return { valid: errors.length === 0, errors };
2280
+ }
2281
+ async dispose() {
2282
+ if (this.disposed) return;
2283
+ this.disposed = true;
2284
+ this.cachedModels = null;
2285
+ }
2286
+ };
2287
+ }
2288
+ });
2289
+
2290
+ // src/backends/vercel-ai.ts
2291
+ var vercel_ai_exports = {};
2292
+ __export(vercel_ai_exports, {
2293
+ _injectCompat: () => _injectCompat,
2294
+ _injectSDK: () => _injectSDK3,
2295
+ _resetSDK: () => _resetSDK3,
2296
+ createVercelAIService: () => createVercelAIService
2297
+ });
2298
+ async function loadSDK3() {
2299
+ if (_sdkMock3) return _sdkMock3;
2300
+ try {
2301
+ return await import('ai');
2302
+ } catch {
2303
+ throw new DependencyError("ai");
2304
+ }
2305
+ }
2306
+ async function loadCompat() {
2307
+ if (_compatMock) return _compatMock;
2308
+ try {
2309
+ return await import('@ai-sdk/openai-compatible');
2310
+ } catch {
2311
+ throw new DependencyError("@ai-sdk/openai-compatible");
2312
+ }
2313
+ }
2314
+ function _injectSDK3(mock) {
2315
+ _sdkMock3 = mock;
2316
+ }
2317
+ function _injectCompat(mock) {
2318
+ _compatMock = mock;
2319
+ }
2320
+ function _resetSDK3() {
2321
+ _sdkMock3 = null;
2322
+ _compatMock = null;
2323
+ }
2324
+ function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, signal) {
2325
+ const toolMap = {};
2326
+ const supervisor = config.supervisor;
2327
+ for (const ourTool of tools) {
2328
+ const jsonSchema = zodToJsonSchema(ourTool.parameters);
2329
+ toolMap[ourTool.name] = sdk.tool({
2330
+ description: ourTool.description,
2331
+ inputSchema: sdk.jsonSchema(jsonSchema),
2332
+ execute: wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal),
2333
+ ...ourTool.needsApproval && supervisor?.onPermission ? {
2334
+ needsApproval: async (_input) => {
2335
+ if (permissionStore && await permissionStore.isApproved(ourTool.name)) return false;
2336
+ if (sessionApprovals.has(ourTool.name)) return false;
2337
+ return true;
2338
+ }
2339
+ } : {}
2340
+ });
2341
+ }
2342
+ if (supervisor?.onAskUser) {
2343
+ const onAskUser = supervisor.onAskUser;
2344
+ toolMap["ask_user"] = sdk.tool({
2345
+ description: "Ask the user a question and wait for their response",
2346
+ inputSchema: sdk.jsonSchema({
2347
+ type: "object",
2348
+ properties: {
2349
+ question: { type: "string", description: "The question to ask the user" }
2350
+ },
2351
+ required: ["question"]
2352
+ }),
2353
+ execute: async (args) => {
2354
+ const response = await onAskUser(
2355
+ { question: args.question, allowFreeform: true },
2356
+ signal
2357
+ );
2358
+ return response.answer;
2359
+ }
2360
+ });
2361
+ }
2362
+ return toolMap;
2363
+ }
2364
+ function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
2365
+ return async (args, options) => {
2366
+ if (ourTool.needsApproval && supervisor?.onPermission) {
2367
+ const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
2368
+ if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
2369
+ const request = {
2370
+ toolName: ourTool.name,
2371
+ toolArgs: args ?? {},
2372
+ toolCallId: options?.toolCallId
2373
+ };
2374
+ const decision = await supervisor.onPermission(
2375
+ request,
2376
+ signal
2377
+ );
2378
+ if (!decision.allowed) {
2379
+ throw new ToolExecutionError(
2380
+ ourTool.name,
2381
+ decision.reason ?? "Permission denied"
2382
+ );
2383
+ }
2384
+ if (permissionStore && decision.scope) {
2385
+ await permissionStore.approve(ourTool.name, decision.scope);
2386
+ }
2387
+ if (decision.scope === "session" || decision.scope === "always" || decision.scope === "project") {
2388
+ sessionApprovals.add(ourTool.name);
2389
+ }
2390
+ if (decision.modifiedInput) {
2391
+ args = decision.modifiedInput;
2392
+ }
2393
+ }
2394
+ }
2395
+ try {
2396
+ const result = await ourTool.execute(args);
2397
+ return result;
2398
+ } catch (e) {
2399
+ if (e instanceof ToolExecutionError) throw e;
2400
+ throw new ToolExecutionError(
2401
+ ourTool.name,
2402
+ e instanceof Error ? e.message : String(e)
2403
+ );
2404
+ }
2405
+ };
2406
+ }
2407
+ function messagesToSDK(messages) {
2408
+ return messages.map((msg) => {
2409
+ switch (msg.role) {
2410
+ case "user":
2411
+ return { role: "user", content: getTextContent(msg.content) };
2412
+ case "assistant": {
2413
+ let content = getTextContent(msg.content);
2414
+ const thinking = msg.thinking;
2415
+ if (thinking) {
2416
+ content = `[reasoning: ${thinking}]
2417
+ ${content}`;
2418
+ }
2419
+ const mapped = { role: "assistant", content };
2420
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
2421
+ mapped.toolCalls = msg.toolCalls.map((tc) => ({
2422
+ id: tc.id,
2423
+ name: tc.name,
2424
+ args: tc.args
2425
+ }));
2426
+ }
2427
+ return mapped;
2428
+ }
2429
+ case "system":
2430
+ return { role: "system", content: msg.content };
2431
+ case "tool": {
2432
+ if (msg.toolResults && msg.toolResults.length > 0) {
2433
+ return {
2434
+ role: "tool",
2435
+ toolResults: msg.toolResults.map((tr) => ({
2436
+ toolCallId: tr.toolCallId,
2437
+ name: tr.name,
2438
+ result: tr.result,
2439
+ isError: tr.isError ?? false
2440
+ }))
2441
+ };
2442
+ }
2443
+ return { role: "tool", content: msg.content ?? "" };
2444
+ }
2445
+ default:
2446
+ return { role: "user", content: "" };
2447
+ }
2448
+ });
2449
+ }
2450
+ function mapStreamPart(part) {
2451
+ switch (part.type) {
2452
+ case "text-delta": {
2453
+ const p = part;
2454
+ return { type: "text_delta", text: p.text ?? "" };
2455
+ }
2456
+ case "tool-call": {
2457
+ const p = part;
2458
+ return {
2459
+ type: "tool_call_start",
2460
+ toolCallId: String(p.toolCallId ?? ""),
2461
+ toolName: p.toolName ?? "unknown",
2462
+ args: p.input ?? {}
2463
+ };
2464
+ }
2465
+ case "tool-result": {
2466
+ const p = part;
2467
+ return {
2468
+ type: "tool_call_end",
2469
+ toolCallId: String(p.toolCallId ?? ""),
2470
+ toolName: p.toolName ?? "unknown",
2471
+ result: p.output ?? null
2472
+ };
2473
+ }
2474
+ case "tool-error": {
2475
+ const p = part;
2476
+ return {
2477
+ type: "error",
2478
+ error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
2479
+ recoverable: true,
2480
+ code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
2481
+ };
2482
+ }
2483
+ case "reasoning-start":
2484
+ return { type: "thinking_start" };
2485
+ case "reasoning-end":
2486
+ return { type: "thinking_end" };
2487
+ case "reasoning-delta": {
2488
+ const p = part;
2489
+ return { type: "thinking_delta", text: p.text ?? "" };
2490
+ }
2491
+ case "finish-step": {
2492
+ const p = part;
2493
+ return {
2494
+ type: "usage_update",
2495
+ promptTokens: Number(p.usage?.inputTokens ?? 0),
2496
+ completionTokens: Number(p.usage?.outputTokens ?? 0)
2497
+ };
2498
+ }
2499
+ case "error": {
2500
+ const p = part;
2501
+ const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
2502
+ const code = classifyAgentError(errorMsg);
2503
+ return {
2504
+ type: "error",
2505
+ error: errorMsg,
2506
+ recoverable: isRecoverableErrorCode(code),
2507
+ code
2508
+ };
2509
+ }
2510
+ default:
2511
+ return null;
2512
+ }
2513
+ }
2514
+ function createVercelAIService(options) {
2515
+ return new VercelAIAgentService(options);
2516
+ }
2517
+ var _sdkMock3, _compatMock, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
2518
+ var init_vercel_ai = __esm({
2519
+ "src/backends/vercel-ai.ts"() {
2520
+ init_types2();
2521
+ init_base_agent();
2522
+ init_errors2();
2523
+ init_schema();
2524
+ _sdkMock3 = null;
2525
+ _compatMock = null;
2526
+ DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
2527
+ DEFAULT_PROVIDER = "openrouter";
2528
+ DEFAULT_MAX_TURNS = 10;
2529
+ VercelAIAgent = class extends BaseAgent {
2530
+ backendName = "vercel-ai";
2531
+ backendOptions;
2532
+ sessionApprovals = /* @__PURE__ */ new Set();
2533
+ model = null;
2534
+ constructor(config, backendOptions) {
2535
+ super(config);
2536
+ this.backendOptions = backendOptions;
2537
+ }
2538
+ async getModel(options) {
2539
+ const requestedModel = options.model;
2540
+ const defaultModel = this.config.model;
2541
+ if (requestedModel === defaultModel && this.model) return this.model;
2542
+ const compat = await loadCompat();
2543
+ const provider = compat.createOpenAICompatible({
2544
+ name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
2545
+ baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
2546
+ apiKey: this.backendOptions.apiKey
2547
+ });
2548
+ const model = provider.chatModel(requestedModel);
2549
+ if (requestedModel === defaultModel) {
2550
+ this.model = model;
2551
+ }
2552
+ return model;
2553
+ }
2554
+ async getSDKTools(signal, options) {
2555
+ const sdk = await loadSDK3();
2556
+ const tools = this.resolveTools(options);
2557
+ return mapToolsToSDK2(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
2558
+ }
2559
+ // ─── executeRun ─────────────────────────────────────────────────
2560
+ async executeRun(messages, options, signal) {
2561
+ this.checkAbort(signal);
2562
+ const sdk = await loadSDK3();
2563
+ const model = await this.getModel(options);
2564
+ const tools = await this.getSDKTools(signal, options);
2565
+ const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2566
+ const sdkMessages = messagesToSDK(messages);
2567
+ const hasTools = Object.keys(tools).length > 0;
2568
+ const result = await sdk.generateText({
2569
+ model,
2570
+ system: this.config.systemPrompt,
2571
+ messages: sdkMessages,
2572
+ tools: hasTools ? tools : void 0,
2573
+ stopWhen: sdk.stepCountIs(maxTurns),
2574
+ abortSignal: signal,
2575
+ ...this.config.modelParams?.temperature !== void 0 && {
2576
+ temperature: this.config.modelParams.temperature
2577
+ },
2578
+ ...this.config.modelParams?.maxTokens !== void 0 && {
2579
+ maxTokens: this.config.modelParams.maxTokens
2580
+ },
2581
+ ...this.config.modelParams?.topP !== void 0 && {
2582
+ topP: this.config.modelParams.topP
2583
+ },
2584
+ ...this.config.providerOptions && {
2585
+ providerOptions: this.config.providerOptions
2586
+ }
2587
+ });
2588
+ const toolCalls = [];
2589
+ for (const step of result.steps) {
2590
+ for (const tc of step.toolCalls) {
2591
+ const matchingResult = step.toolResults.find(
2592
+ (tr) => tr.toolCallId === tc.toolCallId
2593
+ );
2594
+ toolCalls.push({
2595
+ toolName: tc.toolName,
2596
+ args: tc.input ?? {},
2597
+ result: matchingResult?.output ?? null,
2598
+ approved: true
2599
+ });
2600
+ }
2601
+ }
2602
+ const usage = {
2603
+ promptTokens: Number(result.totalUsage?.inputTokens ?? 0),
2604
+ completionTokens: Number(result.totalUsage?.outputTokens ?? 0)
2605
+ };
2606
+ const lastStep = result.steps.length > 0 ? result.steps[result.steps.length - 1] : null;
2607
+ const outputText = lastStep?.text || null;
2608
+ return {
2609
+ output: outputText,
2610
+ structuredOutput: void 0,
2611
+ toolCalls,
2612
+ messages: [
2613
+ ...messages,
2614
+ ...outputText ? [{ role: "assistant", content: outputText }] : []
2615
+ ],
2616
+ usage
2617
+ };
2618
+ }
2619
+ // ─── executeRunStructured ───────────────────────────────────────
2620
+ async executeRunStructured(messages, schema, options, signal) {
2621
+ this.checkAbort(signal);
2622
+ const sdk = await loadSDK3();
2623
+ const model = await this.getModel(options);
2624
+ const sdkMessages = messagesToSDK(messages);
2625
+ const jsonSchema = zodToJsonSchema(schema.schema);
2626
+ const result = await sdk.generateObject({
2627
+ model,
2628
+ system: this.config.systemPrompt,
2629
+ messages: sdkMessages,
2630
+ schema: sdk.jsonSchema(jsonSchema),
2631
+ schemaName: schema.name,
2632
+ schemaDescription: schema.description,
2633
+ abortSignal: signal,
2634
+ ...this.config.modelParams?.temperature !== void 0 && {
2635
+ temperature: this.config.modelParams.temperature
2636
+ },
2637
+ ...this.config.modelParams?.maxTokens !== void 0 && {
2638
+ maxTokens: this.config.modelParams.maxTokens
2639
+ },
2640
+ ...this.config.providerOptions && {
2641
+ providerOptions: this.config.providerOptions
2642
+ }
2643
+ });
2644
+ let structuredOutput;
2645
+ try {
2646
+ structuredOutput = schema.schema.parse(result.object);
2647
+ } catch {
2648
+ }
2649
+ const usage = {
2650
+ promptTokens: Number(result.usage?.inputTokens ?? 0),
2651
+ completionTokens: Number(result.usage?.outputTokens ?? 0)
2652
+ };
2653
+ return {
2654
+ output: JSON.stringify(result.object),
2655
+ structuredOutput,
2656
+ toolCalls: [],
2657
+ messages: [
2658
+ ...messages,
2659
+ ...result.object != null ? [{ role: "assistant", content: JSON.stringify(result.object) }] : []
2660
+ ],
2661
+ usage
2662
+ };
2663
+ }
2664
+ // ─── executeStream ──────────────────────────────────────────────
2665
+ async *executeStream(messages, options, signal) {
2666
+ this.checkAbort(signal);
2667
+ const sdk = await loadSDK3();
2668
+ const model = await this.getModel(options);
2669
+ const tools = await this.getSDKTools(signal, options);
2670
+ const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
2671
+ const sdkMessages = messagesToSDK(messages);
2672
+ const hasTools = Object.keys(tools).length > 0;
2673
+ const result = sdk.streamText({
2674
+ model,
2675
+ system: this.config.systemPrompt,
2676
+ messages: sdkMessages,
2677
+ tools: hasTools ? tools : void 0,
2678
+ stopWhen: sdk.stepCountIs(maxTurns),
2679
+ abortSignal: signal,
2680
+ ...this.config.modelParams?.temperature !== void 0 && {
2681
+ temperature: this.config.modelParams.temperature
2682
+ },
2683
+ ...this.config.modelParams?.maxTokens !== void 0 && {
2684
+ maxTokens: this.config.modelParams.maxTokens
2685
+ },
2686
+ ...this.config.modelParams?.topP !== void 0 && {
2687
+ topP: this.config.modelParams.topP
2688
+ },
2689
+ ...this.config.providerOptions && {
2690
+ providerOptions: this.config.providerOptions
2691
+ }
2692
+ });
2693
+ let finalText = "";
2694
+ try {
2695
+ for await (const part of result.fullStream) {
2696
+ if (signal.aborted) throw new AbortError();
2697
+ const event = mapStreamPart(part);
2698
+ if (event) yield event;
2699
+ if (part.type === "text-delta") {
2700
+ finalText += part.text ?? "";
2701
+ }
2702
+ if (part.type === "finish-step") {
2703
+ const p = part;
2704
+ if (p.finishReason === "tool-calls") {
2705
+ finalText = "";
2706
+ }
2707
+ }
2708
+ }
2709
+ const totalUsage = await result.totalUsage;
2710
+ yield {
2711
+ type: "usage_update",
2712
+ promptTokens: Number(totalUsage?.inputTokens ?? 0),
2713
+ completionTokens: Number(totalUsage?.outputTokens ?? 0)
2714
+ };
2715
+ const hasStreamed = finalText.length > 0;
2716
+ yield {
2717
+ type: "done",
2718
+ finalOutput: hasStreamed ? null : finalText || null,
2719
+ ...hasStreamed ? { streamed: true } : {}
2720
+ };
2721
+ } catch (e) {
2722
+ if (signal.aborted) throw new AbortError();
2723
+ throw e;
2724
+ }
2725
+ }
2726
+ dispose() {
2727
+ this.sessionApprovals.clear();
2728
+ this.model = null;
2729
+ super.dispose();
2730
+ }
2731
+ };
2732
+ VercelAIAgentService = class {
2733
+ name = "vercel-ai";
2734
+ disposed = false;
2735
+ options;
2736
+ constructor(options) {
2737
+ this.options = options;
2738
+ }
2739
+ createAgent(config) {
2740
+ if (this.disposed) throw new DisposedError("VercelAIAgentService");
2741
+ return new VercelAIAgent(config, this.options);
2742
+ }
2743
+ async listModels() {
2744
+ if (this.disposed) throw new DisposedError("VercelAIAgentService");
2745
+ const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
2746
+ try {
2747
+ const res = await globalThis.fetch(`${baseUrl}/models`, {
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
+ }
2753
+ });
2754
+ if (!res.ok) {
2755
+ return [];
2756
+ }
2757
+ const body = await res.json();
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
+ }));
2773
+ }
2774
+ return [];
2775
+ } catch {
2776
+ return [];
2777
+ }
2778
+ }
2779
+ async validate() {
2780
+ if (this.disposed) throw new DisposedError("VercelAIAgentService");
2781
+ const errors = [];
2782
+ if (!this.options.apiKey) {
2783
+ errors.push("apiKey is required for Vercel AI backend.");
2784
+ }
2785
+ try {
2786
+ await loadSDK3();
2787
+ } catch (e) {
2788
+ errors.push(e instanceof Error ? e.message : String(e));
2789
+ }
2790
+ try {
2791
+ await loadCompat();
2792
+ } catch (e) {
2793
+ errors.push(e instanceof Error ? e.message : String(e));
2794
+ }
2795
+ return { valid: errors.length === 0, errors };
2796
+ }
2797
+ async dispose() {
2798
+ if (this.disposed) return;
2799
+ this.disposed = true;
2800
+ }
2801
+ };
2802
+ }
2803
+ });
2804
+
2805
+ // src/chat/backends/types.ts
2806
+ function isResumableBackend(adapter) {
2807
+ return "canResume" in adapter && typeof adapter.canResume === "function";
2808
+ }
2809
+
2810
+ // src/chat/types.ts
2811
+ function createChatId() {
2812
+ return crypto.randomUUID();
2813
+ }
2814
+
2815
+ // src/chat/chat-utils.ts
2816
+ function getMessageText(message) {
2817
+ return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
2818
+ }
2819
+ function getMessageToolCalls(message) {
2820
+ return message.parts.filter((p) => p.type === "tool_call");
2821
+ }
2822
+
2823
+ // src/chat/bridge.ts
2824
+ function agentEventToChatEvent(event, messageId) {
2825
+ switch (event.type) {
2826
+ case "text_delta":
2827
+ return { type: "message:delta", messageId, text: event.text };
2828
+ case "thinking_start":
2829
+ return { type: "thinking:start", messageId };
2830
+ case "thinking_delta":
2831
+ return { type: "thinking:delta", messageId, text: event.text };
2832
+ case "thinking_end":
2833
+ return { type: "thinking:end", messageId };
2834
+ case "tool_call_start":
2835
+ return {
2836
+ type: "tool:start",
2837
+ messageId,
2838
+ toolCallId: event.toolCallId,
2839
+ toolName: event.toolName,
2840
+ args: event.args
2841
+ };
2842
+ case "tool_call_end":
2843
+ return {
2844
+ type: "tool:complete",
2845
+ messageId,
2846
+ toolCallId: event.toolCallId,
2847
+ toolName: event.toolName,
2848
+ result: event.result
2849
+ };
2850
+ case "permission_request":
2851
+ return {
2852
+ type: "permission:request",
2853
+ messageId,
2854
+ toolName: event.request.toolName,
2855
+ toolArgs: event.request.toolArgs
2856
+ };
2857
+ case "permission_response":
2858
+ return {
2859
+ type: "permission:response",
2860
+ messageId,
2861
+ toolName: event.toolName,
2862
+ allowed: event.decision.allowed
2863
+ };
2864
+ case "usage_update":
2865
+ return {
2866
+ type: "usage",
2867
+ promptTokens: event.promptTokens,
2868
+ completionTokens: event.completionTokens,
2869
+ model: event.model
2870
+ };
2871
+ case "error":
2872
+ return {
2873
+ type: "error",
2874
+ error: event.error,
2875
+ recoverable: event.recoverable,
2876
+ code: event.code,
2877
+ messageId
2878
+ };
2879
+ case "heartbeat":
2880
+ return { type: "heartbeat" };
2881
+ case "ask_user":
2882
+ case "ask_user_response":
2883
+ case "session_info":
2884
+ case "done":
2885
+ return null;
2886
+ default:
2887
+ return null;
2888
+ }
2889
+ }
2890
+ async function* adaptAgentEvents(events, messageId) {
2891
+ for await (const event of events) {
2892
+ const chatEvent = agentEventToChatEvent(event, messageId);
2893
+ if (chatEvent !== null) {
2894
+ yield chatEvent;
2895
+ }
2896
+ }
2897
+ }
2898
+
2899
+ // src/chat/conversion.ts
2900
+ function toAgentMessage(message) {
2901
+ const textContent = getMessageText(message);
2902
+ const toolCallParts = getMessageToolCalls(message);
2903
+ switch (message.role) {
2904
+ case "user":
2905
+ return { role: "user", content: textContent };
2906
+ case "assistant": {
2907
+ const toolCalls = toolCallParts.length > 0 ? toolCallParts.map((p) => ({ id: p.toolCallId, name: p.name, args: p.args })) : void 0;
2908
+ return {
2909
+ role: "assistant",
2910
+ content: textContent,
2911
+ toolCalls
2912
+ };
2913
+ }
2914
+ case "system":
2915
+ return { role: "system", content: textContent };
2916
+ }
2917
+ }
2918
+
2919
+ // src/chat/errors.ts
2920
+ init_errors2();
2921
+ init_errors();
2922
+ var ChatError = class extends AgentSDKError {
2923
+ code;
2924
+ retryable;
2925
+ retryAfter;
2926
+ timestamp;
2927
+ constructor(message, options) {
2928
+ super(message, {
2929
+ cause: options.cause,
2930
+ code: options.code,
2931
+ retryable: options.retryable
2932
+ });
2933
+ this.name = "ChatError";
2934
+ this.code = options.code;
2935
+ this.retryable = options.retryable ?? false;
2936
+ this.retryAfter = options.retryAfter;
2937
+ this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
2938
+ }
2939
+ };
2940
+
2941
+ // src/chat/backends/base.ts
2942
+ var BaseBackendAdapter = class {
2943
+ name;
2944
+ _agentService = null;
2945
+ _agentServiceFactory = null;
2946
+ _disposed = false;
2947
+ _agentConfig;
2948
+ _ownsService;
2949
+ // Agent lifecycle: tracks current agent and the model it was created with.
2950
+ // For persistent sessions, reused across calls when model matches.
2951
+ // For non-persistent, recreated every call.
2952
+ _currentAgent = null;
2953
+ constructor(name, options) {
2954
+ this.name = name;
2955
+ this._agentConfig = options.agentConfig;
2956
+ if (options.agentService) {
2957
+ this._agentService = options.agentService;
2958
+ this._ownsService = false;
2959
+ } else if (options.agentServiceFactory) {
2960
+ this._agentServiceFactory = options.agentServiceFactory;
2961
+ this._ownsService = true;
2962
+ } else {
2963
+ this._agentService = this.createService();
2964
+ this._ownsService = true;
2965
+ }
2966
+ }
2967
+ get agentService() {
2968
+ if (!this._agentService) {
2969
+ if (this._agentServiceFactory) {
2970
+ this._agentService = this._agentServiceFactory();
2971
+ this._agentServiceFactory = null;
2972
+ } else {
2973
+ throw new ChatError("Agent service not available", {
2974
+ code: "BACKEND_NOT_INSTALLED" /* BACKEND_NOT_INSTALLED */
2975
+ });
2976
+ }
2977
+ }
2978
+ return this._agentService;
2979
+ }
2980
+ get currentModel() {
2981
+ return this._agentConfig.model;
2982
+ }
2983
+ /**
2984
+ * @deprecated No-op. Tools are passed per-call via SendMessageOptions.tools.
2985
+ * Kept for backward compatibility with code that calls setTools() directly.
2986
+ */
2987
+ setTools() {
2988
+ }
2989
+ async sendMessage(session, message, options) {
2990
+ this.assertNotDisposed();
2991
+ const events = this.streamMessage(session, message, options);
2992
+ let text = "";
2993
+ let lastMessage;
2994
+ for await (const event of events) {
2995
+ if (event.type === "message:delta") {
2996
+ text += event.text;
2997
+ }
2998
+ if (event.type === "message:complete") {
2999
+ lastMessage = event.message;
3000
+ }
3001
+ }
3002
+ if (lastMessage) return lastMessage;
3003
+ const messageId = createChatId();
3004
+ return {
3005
+ id: messageId,
3006
+ role: "assistant",
3007
+ parts: [{ type: "text", text, status: "complete" }],
3008
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3009
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3010
+ status: "complete"
3011
+ };
3012
+ }
3013
+ async *streamMessage(session, message, options) {
3014
+ this.assertNotDisposed();
3015
+ const agent = this.getOrCreateAgent(options);
3016
+ const messages = session.messages.map(toAgentMessage);
3017
+ messages.push({ role: "user", content: message });
3018
+ yield* this.streamAgentEvents(agent, messages, options);
3019
+ }
3020
+ /**
3021
+ * Shared streaming helper: bridges agent events to chat events.
3022
+ * Used by both streamMessage() and resume() to avoid duplication.
3023
+ */
3024
+ async *streamAgentEvents(agent, messages, options) {
3025
+ const messageId = createChatId();
3026
+ const model = options?.model ?? this._agentConfig.model ?? "";
3027
+ const agentEvents = agent.streamWithContext(messages, {
3028
+ model,
3029
+ signal: options?.signal,
3030
+ context: options?.context,
3031
+ tools: options?.tools,
3032
+ ...options?.systemPrompt ? { systemMessage: options.systemPrompt } : {}
3033
+ });
3034
+ yield { type: "message:start", messageId, role: "assistant" };
3035
+ let text = "";
3036
+ for await (const chatEvent of adaptAgentEvents(agentEvents, messageId)) {
3037
+ if (chatEvent.type === "message:delta") {
3038
+ text += chatEvent.text;
3039
+ }
3040
+ yield chatEvent;
3041
+ }
3042
+ this.captureSessionId(agent);
3043
+ yield {
3044
+ type: "message:complete",
3045
+ messageId,
3046
+ message: {
3047
+ id: messageId,
3048
+ role: "assistant",
3049
+ parts: [{ type: "text", text, status: "complete" }],
3050
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3051
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3052
+ status: "complete"
3053
+ }
3054
+ };
3055
+ }
3056
+ async listModels() {
3057
+ this.assertNotDisposed();
3058
+ return this.agentService.listModels();
3059
+ }
3060
+ async validate() {
3061
+ this.assertNotDisposed();
3062
+ return this.agentService.validate();
3063
+ }
3064
+ async dispose() {
3065
+ if (this._disposed) return;
3066
+ this._disposed = true;
3067
+ if (this._currentAgent) {
3068
+ this._currentAgent.instance.dispose();
3069
+ this._currentAgent = null;
3070
+ }
3071
+ if (this._ownsService && this._agentService && typeof this._agentService.dispose === "function") {
3072
+ await this._agentService.dispose();
3073
+ }
3074
+ }
3075
+ /** Get or create an agent. Model is passed per-call via RunOptions.
3076
+ * Tools are passed per-call via SendMessageOptions — not baked into config.
3077
+ * For persistent sessions, reuses agent when model matches. */
3078
+ getOrCreateAgent(options) {
3079
+ const model = options?.model ?? this._agentConfig.model;
3080
+ if (this._agentConfig.sessionMode === "persistent" && this._currentAgent) {
3081
+ if (this._currentAgent.model === model) {
3082
+ return this._currentAgent.instance;
3083
+ }
3084
+ this._currentAgent.instance.dispose();
3085
+ this._currentAgent = null;
3086
+ }
3087
+ if (this._currentAgent) {
3088
+ this._currentAgent.instance.dispose();
3089
+ this._currentAgent = null;
3090
+ }
3091
+ const config = {
3092
+ ...this._agentConfig,
3093
+ ...model !== void 0 && { model }
3094
+ };
3095
+ const agent = this.agentService.createAgent(config);
3096
+ this._currentAgent = { instance: agent, model };
3097
+ return agent;
3098
+ }
3099
+ assertNotDisposed() {
3100
+ if (this._disposed) {
3101
+ throw new ChatError("Adapter is disposed", {
3102
+ code: "DISPOSED" /* DISPOSED */
3103
+ });
3104
+ }
3105
+ }
3106
+ };
3107
+
3108
+ // src/chat/backends/copilot.ts
3109
+ var CopilotChatAdapter = class extends BaseBackendAdapter {
3110
+ _backendSessionId = null;
3111
+ _copilotOptions;
3112
+ constructor(options) {
3113
+ const agentConfig = {
3114
+ ...options.agentConfig,
3115
+ sessionMode: "persistent"
3116
+ };
3117
+ super("copilot", { ...options, agentConfig });
3118
+ this._copilotOptions = options.copilotOptions;
3119
+ }
3120
+ createService() {
3121
+ const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
3122
+ return createCopilotService2(this._copilotOptions || {});
3123
+ }
3124
+ get backendSessionId() {
3125
+ return this._backendSessionId;
3126
+ }
3127
+ canResume() {
3128
+ return this._backendSessionId !== null;
3129
+ }
3130
+ async *resume(session, backendSessionId, options) {
3131
+ this.assertNotDisposed();
3132
+ if (!backendSessionId) {
3133
+ throw new ChatError("Backend session ID is required for resume", {
3134
+ code: "INVALID_INPUT" /* INVALID_INPUT */
3135
+ });
3136
+ }
3137
+ const agent = this.getOrCreateAgent(options);
3138
+ const currentSessionId = agent.sessionId;
3139
+ if (!currentSessionId) {
3140
+ throw new ChatError(
3141
+ `No active session to resume (requested: ${backendSessionId})`,
3142
+ { code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
3143
+ );
3144
+ }
3145
+ if (currentSessionId !== backendSessionId) {
3146
+ throw new ChatError(
3147
+ `Session expired: expected ${backendSessionId}, got ${currentSessionId}`,
3148
+ { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
3149
+ );
3150
+ }
3151
+ const messages = session.messages.map(toAgentMessage);
3152
+ yield* this.streamAgentEvents(agent, messages, options);
3153
+ }
3154
+ captureSessionId(agent) {
3155
+ if (agent.sessionId) {
3156
+ this._backendSessionId = agent.sessionId;
3157
+ }
3158
+ }
3159
+ };
3160
+
3161
+ // src/chat/backends/claude.ts
3162
+ var ClaudeChatAdapter = class extends BaseBackendAdapter {
3163
+ _backendSessionId = null;
3164
+ _claudeOptions;
3165
+ constructor(options) {
3166
+ const agentConfig = {
3167
+ ...options.agentConfig,
3168
+ sessionMode: "persistent"
3169
+ };
3170
+ super("claude", { ...options, agentConfig });
3171
+ this._claudeOptions = options.claudeOptions;
3172
+ }
3173
+ createService() {
3174
+ const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
3175
+ return createClaudeService2(this._claudeOptions || {});
3176
+ }
3177
+ get backendSessionId() {
3178
+ return this._backendSessionId;
3179
+ }
3180
+ canResume() {
3181
+ return this._backendSessionId !== null;
3182
+ }
3183
+ async *resume(session, backendSessionId, options) {
3184
+ this.assertNotDisposed();
3185
+ if (!backendSessionId) {
3186
+ throw new ChatError("Backend session ID is required for resume", {
3187
+ code: "INVALID_INPUT" /* INVALID_INPUT */
3188
+ });
3189
+ }
3190
+ const agent = this.getOrCreateAgent(options);
3191
+ const currentSessionId = agent.sessionId;
3192
+ if (!currentSessionId) {
3193
+ throw new ChatError(
3194
+ `No active session to resume (requested: ${backendSessionId})`,
3195
+ { code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
3196
+ );
3197
+ }
3198
+ if (currentSessionId !== backendSessionId) {
3199
+ throw new ChatError(
3200
+ `Session expired: expected ${backendSessionId}, got ${currentSessionId}`,
3201
+ { code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
3202
+ );
3203
+ }
3204
+ const messages = session.messages.map(toAgentMessage);
3205
+ yield* this.streamAgentEvents(agent, messages, options);
3206
+ }
3207
+ captureSessionId(agent) {
3208
+ if (agent.sessionId) {
3209
+ this._backendSessionId = agent.sessionId;
3210
+ }
3211
+ }
3212
+ };
3213
+
3214
+ // src/chat/backends/vercel-ai.ts
3215
+ var VercelAIChatAdapter = class extends BaseBackendAdapter {
3216
+ _vercelOptions;
3217
+ constructor(options) {
3218
+ super("vercel-ai", options);
3219
+ this._vercelOptions = options.vercelOptions;
3220
+ }
3221
+ createService() {
3222
+ const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
3223
+ return createVercelAIService2(this._vercelOptions || {});
3224
+ }
3225
+ captureSessionId(_agent) {
3226
+ }
3227
+ };
3228
+
3229
+ // src/chat/backends/transport.ts
3230
+ var SSEChatTransport = class {
3231
+ res;
3232
+ _open;
3233
+ _heartbeatTimer;
3234
+ constructor(res, options) {
3235
+ this.res = res;
3236
+ this._open = true;
3237
+ res.writeHead(200, {
3238
+ "Content-Type": "text/event-stream",
3239
+ "Cache-Control": "no-cache",
3240
+ "Connection": "keep-alive",
3241
+ "X-Accel-Buffering": "no"
3242
+ });
3243
+ if (options?.request) {
3244
+ options.request.on("close", () => {
3245
+ this._cleanup();
3246
+ });
3247
+ }
3248
+ const heartbeatMs = options?.heartbeatMs;
3249
+ if (heartbeatMs && heartbeatMs > 0) {
3250
+ this._heartbeatTimer = setInterval(() => {
3251
+ if (!this.isOpen) {
3252
+ this._clearHeartbeat();
3253
+ return;
3254
+ }
3255
+ this.res.write(": heartbeat\n\n");
3256
+ }, heartbeatMs);
3257
+ }
3258
+ }
3259
+ get isOpen() {
3260
+ return this._open && !this.res.writableEnded;
3261
+ }
3262
+ send(event) {
3263
+ if (!this.isOpen) return;
3264
+ this.res.write(`data: ${JSON.stringify(event)}
3265
+
3266
+ `);
3267
+ }
3268
+ close() {
3269
+ if (!this.isOpen) return;
3270
+ this._open = false;
3271
+ this._clearHeartbeat();
3272
+ this.res.write(`data: [DONE]
3273
+
3274
+ `);
3275
+ this.res.end();
3276
+ }
3277
+ error(err) {
3278
+ if (!this.isOpen) return;
3279
+ this._open = false;
3280
+ this._clearHeartbeat();
3281
+ const errorEvent = {
3282
+ type: "error",
3283
+ error: err.message,
3284
+ recoverable: false
3285
+ };
3286
+ this.res.write(`data: ${JSON.stringify(errorEvent)}
3287
+
3288
+ `);
3289
+ this.res.end();
3290
+ }
3291
+ _cleanup() {
3292
+ this._open = false;
3293
+ this._clearHeartbeat();
3294
+ }
3295
+ _clearHeartbeat() {
3296
+ if (this._heartbeatTimer !== void 0) {
3297
+ clearInterval(this._heartbeatTimer);
3298
+ this._heartbeatTimer = void 0;
3299
+ }
3300
+ }
3301
+ };
3302
+ async function streamToTransport(events, transport) {
3303
+ try {
3304
+ let accumulatedText = "";
3305
+ for await (const event of events) {
3306
+ if (!transport.isOpen) break;
3307
+ transport.send(event);
3308
+ if (event.type === "message:delta") {
3309
+ accumulatedText += event.text;
3310
+ }
3311
+ }
3312
+ if (transport.isOpen) {
3313
+ transport.send({ type: "done", finalOutput: accumulatedText || void 0 });
3314
+ }
3315
+ transport.close();
3316
+ } catch (err) {
3317
+ transport.error(err instanceof Error ? err : new Error(String(err)));
3318
+ }
3319
+ }
3320
+
3321
+ // src/chat/backends/ws-transport.ts
3322
+ var WS_READY_STATE = {
3323
+ CONNECTING: 0,
3324
+ OPEN: 1,
3325
+ CLOSING: 2,
3326
+ CLOSED: 3
3327
+ };
3328
+ var WsChatTransport = class {
3329
+ ws;
3330
+ serialize;
3331
+ _open;
3332
+ _heartbeatTimer;
3333
+ constructor(ws, options) {
3334
+ this.ws = ws;
3335
+ this.serialize = options?.serialize ?? JSON.stringify;
3336
+ this._open = ws.readyState === WS_READY_STATE.OPEN;
3337
+ ws.addEventListener("close", () => {
3338
+ this._cleanup();
3339
+ });
3340
+ ws.addEventListener("error", () => {
3341
+ this._cleanup();
3342
+ });
3343
+ const heartbeatMs = options?.heartbeatMs;
3344
+ if (heartbeatMs && heartbeatMs > 0) {
3345
+ this._heartbeatTimer = setInterval(() => {
3346
+ if (!this.isOpen) {
3347
+ this._clearHeartbeat();
3348
+ return;
3349
+ }
3350
+ this.ws.send(this.serialize({ type: "heartbeat" }));
3351
+ }, heartbeatMs);
3352
+ }
3353
+ }
3354
+ get isOpen() {
3355
+ return this._open && this.ws.readyState === WS_READY_STATE.OPEN;
3356
+ }
3357
+ send(event) {
3358
+ if (!this.isOpen) return;
3359
+ this.ws.send(this.serialize(event));
3360
+ }
3361
+ close() {
3362
+ if (!this.isOpen) return;
3363
+ this._open = false;
3364
+ this._clearHeartbeat();
3365
+ this.ws.send(this.serialize({ type: "done" }));
3366
+ this.ws.close(1e3, "stream complete");
3367
+ }
3368
+ error(err) {
3369
+ if (!this.isOpen) return;
3370
+ this._open = false;
3371
+ this._clearHeartbeat();
3372
+ const errorEvent = {
3373
+ type: "error",
3374
+ error: err.message,
3375
+ recoverable: false
3376
+ };
3377
+ this.ws.send(this.serialize(errorEvent));
3378
+ this.ws.close(1011, err.message);
3379
+ }
3380
+ _cleanup() {
3381
+ this._open = false;
3382
+ this._clearHeartbeat();
3383
+ }
3384
+ _clearHeartbeat() {
3385
+ if (this._heartbeatTimer !== void 0) {
3386
+ clearInterval(this._heartbeatTimer);
3387
+ this._heartbeatTimer = void 0;
3388
+ }
3389
+ }
3390
+ };
3391
+
3392
+ // src/chat/backends/in-process-transport.ts
3393
+ var InProcessChatTransport = class {
3394
+ _open = true;
3395
+ _buffer = [];
3396
+ _resolve = null;
3397
+ _error = null;
3398
+ get isOpen() {
3399
+ return this._open;
3400
+ }
3401
+ send(event) {
3402
+ if (!this._open) return;
3403
+ if (this._resolve) {
3404
+ const resolve = this._resolve;
3405
+ this._resolve = null;
3406
+ resolve({ value: event, done: false });
3407
+ } else {
3408
+ this._buffer.push(event);
3409
+ }
3410
+ }
3411
+ close() {
3412
+ if (!this._open) return;
3413
+ this._open = false;
3414
+ if (this._resolve) {
3415
+ const resolve = this._resolve;
3416
+ this._resolve = null;
3417
+ resolve({ value: void 0, done: true });
3418
+ }
3419
+ }
3420
+ error(err) {
3421
+ if (!this._open) return;
3422
+ this._open = false;
3423
+ const errorEvent = {
3424
+ type: "error",
3425
+ error: err.message,
3426
+ recoverable: false
3427
+ };
3428
+ if (this._resolve) {
3429
+ const resolve = this._resolve;
3430
+ this._resolve = null;
3431
+ resolve({ value: errorEvent, done: false });
3432
+ } else {
3433
+ this._error = err;
3434
+ }
3435
+ }
3436
+ // ─── AsyncIterable protocol ────────────────────────────────
3437
+ [Symbol.asyncIterator]() {
3438
+ return {
3439
+ next: () => {
3440
+ if (this._buffer.length > 0) {
3441
+ return Promise.resolve({ value: this._buffer.shift(), done: false });
3442
+ }
3443
+ if (this._error) {
3444
+ const err = this._error;
3445
+ this._error = null;
3446
+ const errorEvent = {
3447
+ type: "error",
3448
+ error: err.message,
3449
+ recoverable: false
3450
+ };
3451
+ return Promise.resolve({ value: errorEvent, done: false });
3452
+ }
3453
+ if (!this._open) {
3454
+ return Promise.resolve({ value: void 0, done: true });
3455
+ }
3456
+ return new Promise((resolve) => {
3457
+ this._resolve = resolve;
3458
+ });
3459
+ }
3460
+ };
3461
+ }
3462
+ };
3463
+
3464
+ // src/chat/backends/interceptors.ts
3465
+ var InterceptedTransport = class {
3466
+ _inner;
3467
+ _interceptors;
3468
+ constructor(inner, interceptors) {
3469
+ this._inner = inner;
3470
+ this._interceptors = interceptors;
3471
+ }
3472
+ get isOpen() {
3473
+ return this._inner.isOpen;
3474
+ }
3475
+ send(event) {
3476
+ if (!this.isOpen) return;
3477
+ let current = event;
3478
+ for (const interceptor of this._interceptors) {
3479
+ if (!interceptor.beforeSend) continue;
3480
+ current = interceptor.beforeSend(current, this._inner);
3481
+ if (current === null) return;
3482
+ }
3483
+ this._inner.send(current);
3484
+ for (const interceptor of this._interceptors) {
3485
+ if (interceptor.afterSend) {
3486
+ interceptor.afterSend(current, this._inner);
3487
+ }
3488
+ }
3489
+ }
3490
+ close() {
3491
+ for (const interceptor of this._interceptors) {
3492
+ if (interceptor.beforeClose) {
3493
+ interceptor.beforeClose(this._inner);
3494
+ }
3495
+ }
3496
+ this._inner.close();
3497
+ }
3498
+ error(err) {
3499
+ for (const interceptor of this._interceptors) {
3500
+ if (interceptor.onError) {
3501
+ interceptor.onError(err, this._inner);
3502
+ }
3503
+ }
3504
+ this._inner.error(err);
3505
+ }
3506
+ };
3507
+ function withInterceptors(transport, interceptors) {
3508
+ if (interceptors.length === 0) return transport;
3509
+ return new InterceptedTransport(transport, interceptors);
3510
+ }
3511
+
3512
+ exports.BaseBackendAdapter = BaseBackendAdapter;
3513
+ exports.ClaudeChatAdapter = ClaudeChatAdapter;
3514
+ exports.CopilotChatAdapter = CopilotChatAdapter;
3515
+ exports.InProcessChatTransport = InProcessChatTransport;
3516
+ exports.SSEChatTransport = SSEChatTransport;
3517
+ exports.VercelAIChatAdapter = VercelAIChatAdapter;
3518
+ exports.WS_READY_STATE = WS_READY_STATE;
3519
+ exports.WsChatTransport = WsChatTransport;
3520
+ exports.isResumableBackend = isResumableBackend;
3521
+ exports.streamToTransport = streamToTransport;
3522
+ exports.withInterceptors = withInterceptors;
3523
+ //# sourceMappingURL=backends.cjs.map
3524
+ //# sourceMappingURL=backends.cjs.map