@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.
- package/README.md +539 -6
- package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
- package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
- package/dist/auth/index.cjs +260 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +21 -138
- package/dist/auth/index.d.ts +21 -138
- package/dist/auth/index.js +260 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/backends/claude.cjs +653 -140
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +4 -1
- package/dist/backends/claude.d.ts +4 -1
- package/dist/backends/claude.js +653 -140
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +428 -88
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +13 -4
- package/dist/backends/copilot.d.ts +13 -4
- package/dist/backends/copilot.js +428 -88
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +349 -77
- package/dist/backends/vercel-ai.cjs.map +1 -1
- package/dist/backends/vercel-ai.d.cts +3 -1
- package/dist/backends/vercel-ai.d.ts +3 -1
- package/dist/backends/vercel-ai.js +349 -77
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/backends-BSrsBYFn.d.cts +39 -0
- package/dist/backends-BSrsBYFn.d.ts +39 -0
- package/dist/chat/accumulator.cjs +147 -0
- package/dist/chat/accumulator.cjs.map +1 -0
- package/dist/chat/accumulator.d.cts +64 -0
- package/dist/chat/accumulator.d.ts +64 -0
- package/dist/chat/accumulator.js +145 -0
- package/dist/chat/accumulator.js.map +1 -0
- package/dist/chat/backends.cjs +3524 -0
- package/dist/chat/backends.cjs.map +1 -0
- package/dist/chat/backends.d.cts +66 -0
- package/dist/chat/backends.d.ts +66 -0
- package/dist/chat/backends.js +3512 -0
- package/dist/chat/backends.js.map +1 -0
- package/dist/chat/context.cjs +280 -0
- package/dist/chat/context.cjs.map +1 -0
- package/dist/chat/context.d.cts +191 -0
- package/dist/chat/context.d.ts +191 -0
- package/dist/chat/context.js +277 -0
- package/dist/chat/context.js.map +1 -0
- package/dist/chat/core.cjs +305 -0
- package/dist/chat/core.cjs.map +1 -0
- package/dist/chat/core.d.cts +84 -0
- package/dist/chat/core.d.ts +84 -0
- package/dist/chat/core.js +282 -0
- package/dist/chat/core.js.map +1 -0
- package/dist/chat/errors.cjs +273 -0
- package/dist/chat/errors.cjs.map +1 -0
- package/dist/chat/errors.d.cts +97 -0
- package/dist/chat/errors.d.ts +97 -0
- package/dist/chat/errors.js +266 -0
- package/dist/chat/errors.js.map +1 -0
- package/dist/chat/events.cjs +203 -0
- package/dist/chat/events.cjs.map +1 -0
- package/dist/chat/events.d.cts +245 -0
- package/dist/chat/events.d.ts +245 -0
- package/dist/chat/events.js +196 -0
- package/dist/chat/events.js.map +1 -0
- package/dist/chat/index.cjs +5550 -0
- package/dist/chat/index.cjs.map +1 -0
- package/dist/chat/index.d.cts +77 -0
- package/dist/chat/index.d.ts +77 -0
- package/dist/chat/index.js +5505 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +3589 -0
- package/dist/chat/react.cjs.map +1 -0
- package/dist/chat/react.d.cts +1088 -0
- package/dist/chat/react.d.ts +1088 -0
- package/dist/chat/react.js +3547 -0
- package/dist/chat/react.js.map +1 -0
- package/dist/chat/runtime.cjs +1245 -0
- package/dist/chat/runtime.cjs.map +1 -0
- package/dist/chat/runtime.d.cts +182 -0
- package/dist/chat/runtime.d.ts +182 -0
- package/dist/chat/runtime.js +1243 -0
- package/dist/chat/runtime.js.map +1 -0
- package/dist/chat/server.cjs +2668 -0
- package/dist/chat/server.cjs.map +1 -0
- package/dist/chat/server.d.cts +648 -0
- package/dist/chat/server.d.ts +648 -0
- package/dist/chat/server.js +2628 -0
- package/dist/chat/server.js.map +1 -0
- package/dist/chat/sessions.cjs +380 -0
- package/dist/chat/sessions.cjs.map +1 -0
- package/dist/chat/sessions.d.cts +158 -0
- package/dist/chat/sessions.d.ts +158 -0
- package/dist/chat/sessions.js +376 -0
- package/dist/chat/sessions.js.map +1 -0
- package/dist/chat/sqlite.cjs +441 -0
- package/dist/chat/sqlite.cjs.map +1 -0
- package/dist/chat/sqlite.d.cts +128 -0
- package/dist/chat/sqlite.d.ts +128 -0
- package/dist/chat/sqlite.js +435 -0
- package/dist/chat/sqlite.js.map +1 -0
- package/dist/chat/state.cjs +190 -0
- package/dist/chat/state.cjs.map +1 -0
- package/dist/chat/state.d.cts +95 -0
- package/dist/chat/state.d.ts +95 -0
- package/dist/chat/state.js +180 -0
- package/dist/chat/state.js.map +1 -0
- package/dist/chat/storage.cjs +249 -0
- package/dist/chat/storage.cjs.map +1 -0
- package/dist/chat/storage.d.cts +197 -0
- package/dist/chat/storage.d.ts +197 -0
- package/dist/chat/storage.js +245 -0
- package/dist/chat/storage.js.map +1 -0
- package/dist/errors-C-so0M4t.d.cts +33 -0
- package/dist/errors-C-so0M4t.d.ts +33 -0
- package/dist/errors-CmVvczxZ.d.cts +28 -0
- package/dist/errors-CmVvczxZ.d.ts +28 -0
- package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
- package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
- package/dist/index.cjs +365 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +322 -125
- package/dist/index.d.ts +322 -125
- package/dist/index.js +359 -60
- package/dist/index.js.map +1 -1
- package/dist/provider-types-PTSlRPNB.d.cts +39 -0
- package/dist/provider-types-PTSlRPNB.d.ts +39 -0
- package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
- package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
- package/dist/testing.cjs +383 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +132 -0
- package/dist/testing.d.ts +132 -0
- package/dist/testing.js +377 -0
- package/dist/testing.js.map +1 -0
- package/dist/token-store-CSUBgYwn.d.ts +48 -0
- package/dist/token-store-CuC4hB9Z.d.cts +48 -0
- package/dist/transport-Cdh3M0tS.d.cts +68 -0
- package/dist/transport-Ciap4PWK.d.ts +68 -0
- package/dist/types-4vbcmPTp.d.cts +143 -0
- package/dist/types-BxggH0Yh.d.ts +143 -0
- package/dist/types-DRgd_9R7.d.cts +363 -0
- package/dist/types-ajANVzf7.d.ts +363 -0
- package/package.json +178 -6
package/dist/backends/claude.cjs
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// src/types.ts
|
|
3
|
+
// src/types/errors.ts
|
|
4
|
+
var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
|
|
5
|
+
"TIMEOUT" /* TIMEOUT */,
|
|
6
|
+
"RATE_LIMIT" /* RATE_LIMIT */,
|
|
7
|
+
"NETWORK" /* NETWORK */,
|
|
8
|
+
"TOOL_EXECUTION" /* TOOL_EXECUTION */,
|
|
9
|
+
"MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
|
|
10
|
+
"PROVIDER_ERROR" /* PROVIDER_ERROR */
|
|
11
|
+
]);
|
|
12
|
+
function isRecoverableErrorCode(code) {
|
|
13
|
+
return RECOVERABLE_CODES.has(code);
|
|
14
|
+
}
|
|
15
|
+
function classifyAgentError(error) {
|
|
16
|
+
const msg = (error instanceof Error ? error.message : error).toLowerCase();
|
|
17
|
+
if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
|
|
18
|
+
return "TIMEOUT" /* TIMEOUT */;
|
|
19
|
+
}
|
|
20
|
+
if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
|
|
21
|
+
return "RATE_LIMIT" /* RATE_LIMIT */;
|
|
22
|
+
}
|
|
23
|
+
if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
|
|
24
|
+
return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
|
|
25
|
+
}
|
|
26
|
+
if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
|
|
27
|
+
return "NETWORK" /* NETWORK */;
|
|
28
|
+
}
|
|
29
|
+
if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
|
|
30
|
+
return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
|
|
31
|
+
}
|
|
32
|
+
if (msg.includes("abort") || msg.includes("cancel")) {
|
|
33
|
+
return "ABORTED" /* ABORTED */;
|
|
34
|
+
}
|
|
35
|
+
if (msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("internal server error") || msg.includes("service unavailable") || msg.includes("bad gateway") || msg.includes("overloaded")) {
|
|
36
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
37
|
+
}
|
|
38
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/types/guards.ts
|
|
4
42
|
function getTextContent(content) {
|
|
5
43
|
if (typeof content === "string") return content;
|
|
6
44
|
return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
@@ -8,41 +46,71 @@ function getTextContent(content) {
|
|
|
8
46
|
|
|
9
47
|
// src/errors.ts
|
|
10
48
|
var AgentSDKError = class extends Error {
|
|
49
|
+
/** @internal Marker for cross-bundle identity checks */
|
|
50
|
+
_agentSDKError = true;
|
|
51
|
+
/** Machine-readable error code. Prefer values from the ErrorCode enum. */
|
|
52
|
+
code;
|
|
53
|
+
/** Whether this error is safe to retry */
|
|
54
|
+
retryable;
|
|
55
|
+
/** HTTP status code hint for error classification */
|
|
56
|
+
httpStatus;
|
|
11
57
|
constructor(message, options) {
|
|
12
58
|
super(message, options);
|
|
13
59
|
this.name = "AgentSDKError";
|
|
60
|
+
this.code = options?.code;
|
|
61
|
+
this.retryable = options?.retryable ?? false;
|
|
62
|
+
this.httpStatus = options?.httpStatus;
|
|
63
|
+
}
|
|
64
|
+
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
65
|
+
static is(error) {
|
|
66
|
+
return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
|
|
14
67
|
}
|
|
15
68
|
};
|
|
16
69
|
var ReentrancyError = class extends AgentSDKError {
|
|
17
70
|
constructor() {
|
|
18
|
-
super("Agent is already running. Await the current run before starting another."
|
|
71
|
+
super("Agent is already running. Await the current run before starting another.", {
|
|
72
|
+
code: "REENTRANCY" /* REENTRANCY */
|
|
73
|
+
});
|
|
19
74
|
this.name = "ReentrancyError";
|
|
20
75
|
}
|
|
21
76
|
};
|
|
22
77
|
var DisposedError = class extends AgentSDKError {
|
|
23
78
|
constructor(entity) {
|
|
24
|
-
super(`${entity} has been disposed and cannot be used
|
|
79
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
80
|
+
code: "DISPOSED" /* DISPOSED */
|
|
81
|
+
});
|
|
25
82
|
this.name = "DisposedError";
|
|
26
83
|
}
|
|
27
84
|
};
|
|
28
85
|
var SubprocessError = class extends AgentSDKError {
|
|
29
86
|
constructor(message, options) {
|
|
30
|
-
super(message, options);
|
|
87
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
31
88
|
this.name = "SubprocessError";
|
|
32
89
|
}
|
|
33
90
|
};
|
|
34
91
|
var AbortError = class extends AgentSDKError {
|
|
35
92
|
constructor() {
|
|
36
|
-
super("Agent run was aborted.");
|
|
93
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
37
94
|
this.name = "AbortError";
|
|
38
95
|
}
|
|
39
96
|
};
|
|
97
|
+
var ActivityTimeoutError = class extends AgentSDKError {
|
|
98
|
+
constructor(timeoutMs) {
|
|
99
|
+
super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
|
|
100
|
+
code: "TIMEOUT" /* TIMEOUT */,
|
|
101
|
+
retryable: true
|
|
102
|
+
});
|
|
103
|
+
this.name = "ActivityTimeoutError";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
40
106
|
|
|
41
107
|
// src/base-agent.ts
|
|
42
108
|
var BaseAgent = class {
|
|
43
109
|
state = "idle";
|
|
44
110
|
abortController = null;
|
|
45
111
|
config;
|
|
112
|
+
_cleanupExternalSignal = null;
|
|
113
|
+
_streamMiddleware = [];
|
|
46
114
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
47
115
|
get sessionId() {
|
|
48
116
|
return void 0;
|
|
@@ -58,12 +126,14 @@ var BaseAgent = class {
|
|
|
58
126
|
this.state = "running";
|
|
59
127
|
try {
|
|
60
128
|
const messages = [{ role: "user", content: prompt }];
|
|
61
|
-
const result = await this.
|
|
62
|
-
|
|
129
|
+
const result = await this.withRetry(
|
|
130
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
131
|
+
options
|
|
132
|
+
);
|
|
133
|
+
this.enrichAndNotifyUsage(result, options);
|
|
63
134
|
return result;
|
|
64
135
|
} finally {
|
|
65
|
-
this.
|
|
66
|
-
this.abortController = null;
|
|
136
|
+
this.cleanupRun();
|
|
67
137
|
}
|
|
68
138
|
}
|
|
69
139
|
async runWithContext(messages, options) {
|
|
@@ -72,12 +142,14 @@ var BaseAgent = class {
|
|
|
72
142
|
const ac = this.createAbortController(options?.signal);
|
|
73
143
|
this.state = "running";
|
|
74
144
|
try {
|
|
75
|
-
const result = await this.
|
|
76
|
-
|
|
145
|
+
const result = await this.withRetry(
|
|
146
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
147
|
+
options
|
|
148
|
+
);
|
|
149
|
+
this.enrichAndNotifyUsage(result, options);
|
|
77
150
|
return result;
|
|
78
151
|
} finally {
|
|
79
|
-
this.
|
|
80
|
-
this.abortController = null;
|
|
152
|
+
this.cleanupRun();
|
|
81
153
|
}
|
|
82
154
|
}
|
|
83
155
|
async runStructured(prompt, schema, options) {
|
|
@@ -87,17 +159,14 @@ var BaseAgent = class {
|
|
|
87
159
|
this.state = "running";
|
|
88
160
|
try {
|
|
89
161
|
const messages = [{ role: "user", content: prompt }];
|
|
90
|
-
const result = await this.
|
|
91
|
-
messages,
|
|
92
|
-
|
|
93
|
-
options,
|
|
94
|
-
ac.signal
|
|
162
|
+
const result = await this.withRetry(
|
|
163
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
164
|
+
options
|
|
95
165
|
);
|
|
96
|
-
this.enrichAndNotifyUsage(result);
|
|
166
|
+
this.enrichAndNotifyUsage(result, options);
|
|
97
167
|
return result;
|
|
98
168
|
} finally {
|
|
99
|
-
this.
|
|
100
|
-
this.abortController = null;
|
|
169
|
+
this.cleanupRun();
|
|
101
170
|
}
|
|
102
171
|
}
|
|
103
172
|
async *stream(prompt, options) {
|
|
@@ -107,11 +176,12 @@ var BaseAgent = class {
|
|
|
107
176
|
this.state = "streaming";
|
|
108
177
|
try {
|
|
109
178
|
const messages = [{ role: "user", content: prompt }];
|
|
110
|
-
|
|
111
|
-
|
|
179
|
+
yield* this.streamWithRetry(
|
|
180
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
181
|
+
options
|
|
182
|
+
);
|
|
112
183
|
} finally {
|
|
113
|
-
this.
|
|
114
|
-
this.abortController = null;
|
|
184
|
+
this.cleanupRun();
|
|
115
185
|
}
|
|
116
186
|
}
|
|
117
187
|
async *streamWithContext(messages, options) {
|
|
@@ -120,12 +190,36 @@ var BaseAgent = class {
|
|
|
120
190
|
const ac = this.createAbortController(options?.signal);
|
|
121
191
|
this.state = "streaming";
|
|
122
192
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
193
|
+
yield* this.streamWithRetry(
|
|
194
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
195
|
+
options
|
|
196
|
+
);
|
|
125
197
|
} finally {
|
|
126
|
-
this.
|
|
127
|
-
|
|
198
|
+
this.cleanupRun();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** Register a stream middleware. Applied in registration order after built-in transforms. */
|
|
202
|
+
addStreamMiddleware(middleware) {
|
|
203
|
+
this.guardDisposed();
|
|
204
|
+
this._streamMiddleware.push(middleware);
|
|
205
|
+
}
|
|
206
|
+
/** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
|
|
207
|
+
async *applyStreamPipeline(source, options, ac) {
|
|
208
|
+
let stream = this.enrichStream(source, options);
|
|
209
|
+
stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
|
|
210
|
+
stream = this.heartbeatStream(stream);
|
|
211
|
+
if (this._streamMiddleware.length > 0) {
|
|
212
|
+
const ctx = {
|
|
213
|
+
model: options.model,
|
|
214
|
+
backend: this.backendName,
|
|
215
|
+
abortController: ac,
|
|
216
|
+
config: Object.freeze({ ...this.config })
|
|
217
|
+
};
|
|
218
|
+
for (const mw of this._streamMiddleware) {
|
|
219
|
+
stream = mw(stream, ctx);
|
|
220
|
+
}
|
|
128
221
|
}
|
|
222
|
+
yield* stream;
|
|
129
223
|
}
|
|
130
224
|
abort() {
|
|
131
225
|
if (this.abortController) {
|
|
@@ -144,29 +238,114 @@ var BaseAgent = class {
|
|
|
144
238
|
}
|
|
145
239
|
/** Mark agent as disposed. Override to add cleanup. */
|
|
146
240
|
dispose() {
|
|
241
|
+
this._cleanupExternalSignal?.();
|
|
242
|
+
this._cleanupExternalSignal = null;
|
|
147
243
|
this.abort();
|
|
148
244
|
this.state = "disposed";
|
|
149
245
|
}
|
|
246
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
247
|
+
/** Check if an error should be retried given the retry configuration. */
|
|
248
|
+
isRetryableError(error, retry) {
|
|
249
|
+
if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (AgentSDKError.is(error)) {
|
|
253
|
+
if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
|
|
254
|
+
return retry.retryableErrors.includes(error.code);
|
|
255
|
+
}
|
|
256
|
+
if (error.retryable) return true;
|
|
257
|
+
if (error.code) return isRecoverableErrorCode(error.code);
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
/** Execute a function with retry logic per RetryConfig. */
|
|
262
|
+
async withRetry(fn, options) {
|
|
263
|
+
const retry = options?.retry;
|
|
264
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
265
|
+
return fn();
|
|
266
|
+
}
|
|
267
|
+
const maxRetries = retry.maxRetries;
|
|
268
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
269
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
270
|
+
let lastError;
|
|
271
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
272
|
+
try {
|
|
273
|
+
return await fn();
|
|
274
|
+
} catch (err) {
|
|
275
|
+
lastError = err;
|
|
276
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
279
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
280
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
281
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
throw lastError;
|
|
287
|
+
}
|
|
288
|
+
/** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
|
|
289
|
+
async *streamWithRetry(factory, options) {
|
|
290
|
+
const retry = options?.retry;
|
|
291
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
292
|
+
yield* factory();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const maxRetries = retry.maxRetries;
|
|
296
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
297
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
298
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
299
|
+
try {
|
|
300
|
+
const stream = factory();
|
|
301
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
302
|
+
const first = await iterator.next();
|
|
303
|
+
if (first.done) return;
|
|
304
|
+
yield first.value;
|
|
305
|
+
while (true) {
|
|
306
|
+
const next = await iterator.next();
|
|
307
|
+
if (next.done) break;
|
|
308
|
+
yield next.value;
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
316
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
317
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ─── CallOptions Resolution ──────────────────────────────────
|
|
324
|
+
/** Resolve tools to use for this call (per-call override > config default) */
|
|
325
|
+
resolveTools(options) {
|
|
326
|
+
return options?.tools ?? this.config.tools ?? [];
|
|
327
|
+
}
|
|
150
328
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
151
329
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
152
|
-
enrichAndNotifyUsage(result) {
|
|
330
|
+
enrichAndNotifyUsage(result, options) {
|
|
153
331
|
if (result.usage) {
|
|
154
332
|
result.usage = {
|
|
155
333
|
...result.usage,
|
|
156
|
-
model:
|
|
334
|
+
model: options.model,
|
|
157
335
|
backend: this.backendName
|
|
158
336
|
};
|
|
159
337
|
this.callOnUsage(result.usage);
|
|
160
338
|
}
|
|
161
339
|
}
|
|
162
340
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
163
|
-
async *enrichStream(source) {
|
|
341
|
+
async *enrichStream(source, options) {
|
|
342
|
+
const model = options.model;
|
|
164
343
|
for await (const event of source) {
|
|
165
344
|
if (event.type === "usage_update") {
|
|
166
345
|
const usage = {
|
|
167
346
|
promptTokens: event.promptTokens,
|
|
168
347
|
completionTokens: event.completionTokens,
|
|
169
|
-
model
|
|
348
|
+
model,
|
|
170
349
|
backend: this.backendName
|
|
171
350
|
};
|
|
172
351
|
this.callOnUsage(usage);
|
|
@@ -236,6 +415,35 @@ var BaseAgent = class {
|
|
|
236
415
|
heartbeatResolve = null;
|
|
237
416
|
}
|
|
238
417
|
}
|
|
418
|
+
// ─── Activity Timeout ────────────────────────────────────────
|
|
419
|
+
/** Wrap a stream to abort on inactivity. Resets timer on every event.
|
|
420
|
+
* When timeoutMs is not set, passes through directly. */
|
|
421
|
+
async *activityTimeoutStream(source, timeoutMs, ac) {
|
|
422
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
423
|
+
yield* source;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
427
|
+
let timerId;
|
|
428
|
+
try {
|
|
429
|
+
while (true) {
|
|
430
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
431
|
+
timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
|
|
432
|
+
});
|
|
433
|
+
const result = await Promise.race([iterator.next(), timeoutPromise]);
|
|
434
|
+
clearTimeout(timerId);
|
|
435
|
+
if (result.done) break;
|
|
436
|
+
yield result.value;
|
|
437
|
+
}
|
|
438
|
+
} catch (err) {
|
|
439
|
+
if (err instanceof ActivityTimeoutError) {
|
|
440
|
+
ac.abort(err);
|
|
441
|
+
}
|
|
442
|
+
throw err;
|
|
443
|
+
} finally {
|
|
444
|
+
clearTimeout(timerId);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
239
447
|
// ─── Guards ───────────────────────────────────────────────────
|
|
240
448
|
guardReentrancy() {
|
|
241
449
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -254,16 +462,24 @@ var BaseAgent = class {
|
|
|
254
462
|
}
|
|
255
463
|
}
|
|
256
464
|
// ─── Internal Helpers ─────────────────────────────────────────
|
|
465
|
+
/** Clean up after a run completes (success, error, or abort). */
|
|
466
|
+
cleanupRun() {
|
|
467
|
+
this._cleanupExternalSignal?.();
|
|
468
|
+
this._cleanupExternalSignal = null;
|
|
469
|
+
this.state = "idle";
|
|
470
|
+
this.abortController = null;
|
|
471
|
+
}
|
|
257
472
|
createAbortController(externalSignal) {
|
|
258
473
|
const ac = new AbortController();
|
|
259
474
|
this.abortController = ac;
|
|
475
|
+
this._cleanupExternalSignal = null;
|
|
260
476
|
if (externalSignal) {
|
|
261
477
|
if (externalSignal.aborted) {
|
|
262
478
|
ac.abort();
|
|
263
479
|
} else {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
480
|
+
const listener = () => ac.abort();
|
|
481
|
+
externalSignal.addEventListener("abort", listener, { once: true });
|
|
482
|
+
this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
|
|
267
483
|
}
|
|
268
484
|
}
|
|
269
485
|
return ac;
|
|
@@ -326,6 +542,61 @@ function extractSchemaFromDef(schema) {
|
|
|
326
542
|
}
|
|
327
543
|
}
|
|
328
544
|
|
|
545
|
+
// src/backends/shared.ts
|
|
546
|
+
function extractLastUserPrompt(messages) {
|
|
547
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
548
|
+
const msg = messages[i];
|
|
549
|
+
if (msg.role === "user") {
|
|
550
|
+
return getTextContent(msg.content);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return "";
|
|
554
|
+
}
|
|
555
|
+
function serializeToolCall(tc) {
|
|
556
|
+
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
557
|
+
return ` Tool call: ${tc.name}(${args})`;
|
|
558
|
+
}
|
|
559
|
+
function serializeToolResult(tr) {
|
|
560
|
+
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
561
|
+
const prefix = tr.isError ? "[ERROR] " : "";
|
|
562
|
+
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
563
|
+
}
|
|
564
|
+
function buildContextualPrompt(messages) {
|
|
565
|
+
if (messages.length <= 1) {
|
|
566
|
+
return extractLastUserPrompt(messages);
|
|
567
|
+
}
|
|
568
|
+
const history = messages.slice(0, -1).map((msg) => {
|
|
569
|
+
if (msg.role === "user") {
|
|
570
|
+
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
571
|
+
}
|
|
572
|
+
if (msg.role === "tool" && msg.toolResults) {
|
|
573
|
+
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
574
|
+
return `Tool results:
|
|
575
|
+
${results}`;
|
|
576
|
+
}
|
|
577
|
+
if (msg.role === "assistant") {
|
|
578
|
+
const parts = [];
|
|
579
|
+
const thinking = msg.thinking;
|
|
580
|
+
if (thinking) {
|
|
581
|
+
parts.push(`[reasoning: ${thinking}]`);
|
|
582
|
+
}
|
|
583
|
+
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
584
|
+
if (text2) parts.push(text2);
|
|
585
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
586
|
+
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
587
|
+
}
|
|
588
|
+
return `Assistant: ${parts.join("\n")}`;
|
|
589
|
+
}
|
|
590
|
+
const text = msg.content ? getTextContent(msg.content) : "";
|
|
591
|
+
return `${msg.role}: ${text}`;
|
|
592
|
+
}).join("\n");
|
|
593
|
+
const lastPrompt = extractLastUserPrompt(messages);
|
|
594
|
+
return `Conversation history:
|
|
595
|
+
${history}
|
|
596
|
+
|
|
597
|
+
User: ${lastPrompt}`;
|
|
598
|
+
}
|
|
599
|
+
|
|
329
600
|
// src/backends/claude.ts
|
|
330
601
|
var MCP_SERVER_NAME = "agent-sdk-tools";
|
|
331
602
|
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
@@ -335,12 +606,12 @@ function mcpToolName(toolName) {
|
|
|
335
606
|
function stripMcpPrefix(name) {
|
|
336
607
|
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
337
608
|
}
|
|
338
|
-
var
|
|
609
|
+
var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
610
|
+
var _sdkMock = null;
|
|
339
611
|
async function loadSDK() {
|
|
340
|
-
if (
|
|
612
|
+
if (_sdkMock) return _sdkMock;
|
|
341
613
|
try {
|
|
342
|
-
|
|
343
|
-
return sdkModule;
|
|
614
|
+
return await import('@anthropic-ai/claude-agent-sdk');
|
|
344
615
|
} catch {
|
|
345
616
|
throw new SubprocessError(
|
|
346
617
|
"@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
|
|
@@ -348,16 +619,35 @@ async function loadSDK() {
|
|
|
348
619
|
}
|
|
349
620
|
}
|
|
350
621
|
function _injectSDK(mock) {
|
|
351
|
-
|
|
622
|
+
_sdkMock = mock;
|
|
352
623
|
}
|
|
353
624
|
function _resetSDK() {
|
|
354
|
-
|
|
625
|
+
_sdkMock = null;
|
|
355
626
|
}
|
|
356
627
|
var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
|
|
357
628
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
358
629
|
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
359
|
-
function
|
|
360
|
-
if (
|
|
630
|
+
function normalizeAskUserInput(args) {
|
|
631
|
+
if (typeof args.question === "string") {
|
|
632
|
+
return {
|
|
633
|
+
question: args.question,
|
|
634
|
+
choices: Array.isArray(args.choices) ? args.choices : void 0,
|
|
635
|
+
allowFreeform: args.allowFreeform !== false
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
const questions = args.questions;
|
|
639
|
+
if (questions && questions.length > 0) {
|
|
640
|
+
const first = questions[0];
|
|
641
|
+
return {
|
|
642
|
+
question: first.question,
|
|
643
|
+
choices: first.options?.map((o) => o.label),
|
|
644
|
+
allowFreeform: true
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
return { question: JSON.stringify(args), allowFreeform: true };
|
|
648
|
+
}
|
|
649
|
+
function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
|
|
650
|
+
if (tools.length === 0 && !onAskUser) return void 0;
|
|
361
651
|
const mcpTools = tools.map((tool) => {
|
|
362
652
|
const zodSchema = tool.parameters;
|
|
363
653
|
const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
|
|
@@ -381,6 +671,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
|
381
671
|
}
|
|
382
672
|
);
|
|
383
673
|
});
|
|
674
|
+
if (onAskUser) {
|
|
675
|
+
const askUserTool = sdk.tool(
|
|
676
|
+
"ask_user",
|
|
677
|
+
"Ask the user a question and wait for their response",
|
|
678
|
+
{
|
|
679
|
+
question: { type: "string", description: "The question to ask the user" },
|
|
680
|
+
choices: {
|
|
681
|
+
type: "array",
|
|
682
|
+
items: { type: "string" },
|
|
683
|
+
description: "Optional list of choices for multiple choice"
|
|
684
|
+
},
|
|
685
|
+
questions: {
|
|
686
|
+
type: "array",
|
|
687
|
+
items: {
|
|
688
|
+
type: "object",
|
|
689
|
+
properties: {
|
|
690
|
+
question: { type: "string" },
|
|
691
|
+
options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
description: "Alternative nested question format"
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
async (args) => {
|
|
698
|
+
const normalized = normalizeAskUserInput(args);
|
|
699
|
+
const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
|
|
700
|
+
return {
|
|
701
|
+
content: [{ type: "text", text: response.answer }]
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
mcpTools.push(askUserTool);
|
|
706
|
+
}
|
|
384
707
|
return sdk.createSdkMcpServer({
|
|
385
708
|
name: MCP_SERVER_NAME,
|
|
386
709
|
version: "1.0.0",
|
|
@@ -430,6 +753,7 @@ function buildCanUseTool(config) {
|
|
|
430
753
|
const unifiedRequest = {
|
|
431
754
|
toolName,
|
|
432
755
|
toolArgs: input,
|
|
756
|
+
toolCallId: options.toolUseID,
|
|
433
757
|
suggestedScope: extractSuggestedScope(options.suggestions),
|
|
434
758
|
rawSDKRequest: { toolName, input, ...options }
|
|
435
759
|
};
|
|
@@ -506,6 +830,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
506
830
|
if (block.type === "tool_use") {
|
|
507
831
|
const toolCallId = String(block.id ?? "");
|
|
508
832
|
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
833
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
509
834
|
if (toolCallTracker) {
|
|
510
835
|
toolCallTracker.trackStart(toolCallId, toolName);
|
|
511
836
|
}
|
|
@@ -529,6 +854,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
529
854
|
case "tool_use_summary": {
|
|
530
855
|
const summary = msg.summary;
|
|
531
856
|
const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
|
|
857
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
|
|
532
858
|
const precedingIds = msg.preceding_tool_use_ids;
|
|
533
859
|
let toolCallId = "";
|
|
534
860
|
if (precedingIds && precedingIds.length > 0) {
|
|
@@ -582,10 +908,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
582
908
|
}
|
|
583
909
|
if (msg.is_error) {
|
|
584
910
|
const r = msg;
|
|
911
|
+
const errorMsg = r.errors?.join("; ") ?? "Unknown error";
|
|
912
|
+
const code = classifyAgentError(errorMsg);
|
|
585
913
|
return {
|
|
586
914
|
type: "error",
|
|
587
|
-
error:
|
|
588
|
-
recoverable:
|
|
915
|
+
error: errorMsg,
|
|
916
|
+
recoverable: isRecoverableErrorCode(code),
|
|
917
|
+
code
|
|
589
918
|
};
|
|
590
919
|
}
|
|
591
920
|
return null;
|
|
@@ -605,13 +934,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
605
934
|
constructor(config, options) {
|
|
606
935
|
super(config);
|
|
607
936
|
this.options = options;
|
|
608
|
-
this.tools = config.tools;
|
|
937
|
+
this.tools = config.tools ?? [];
|
|
609
938
|
this.canUseTool = buildCanUseTool(config);
|
|
610
939
|
this.isPersistent = config.sessionMode === "persistent";
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
"[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
|
|
614
|
-
);
|
|
940
|
+
if (options.resumeSessionId) {
|
|
941
|
+
this._sessionId = options.resumeSessionId;
|
|
615
942
|
}
|
|
616
943
|
}
|
|
617
944
|
get sessionId() {
|
|
@@ -636,12 +963,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
636
963
|
const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
|
|
637
964
|
return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
|
|
638
965
|
}
|
|
639
|
-
buildQueryOptions(signal) {
|
|
966
|
+
buildQueryOptions(signal, options) {
|
|
640
967
|
const ac = new AbortController();
|
|
641
968
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
642
969
|
const opts = {
|
|
643
970
|
abortController: ac,
|
|
644
|
-
model:
|
|
971
|
+
model: options.model,
|
|
645
972
|
maxTurns: this.options.maxTurns,
|
|
646
973
|
cwd: this.options.workingDirectory,
|
|
647
974
|
pathToClaudeCodeExecutable: this.options.cliPath,
|
|
@@ -666,30 +993,90 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
666
993
|
opts.permissionMode = "default";
|
|
667
994
|
}
|
|
668
995
|
if (this.config.availableTools) {
|
|
669
|
-
opts.
|
|
996
|
+
opts.tools = [...this.config.availableTools];
|
|
670
997
|
}
|
|
671
998
|
return opts;
|
|
672
999
|
}
|
|
673
1000
|
async buildMcpConfig(opts, toolResultCapture) {
|
|
674
|
-
|
|
1001
|
+
const onAskUser = this.config.supervisor?.onAskUser;
|
|
1002
|
+
if (this.tools.length === 0 && !onAskUser) return opts;
|
|
675
1003
|
const sdk = await loadSDK();
|
|
676
|
-
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
|
|
1004
|
+
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
|
|
677
1005
|
if (mcpServer) {
|
|
678
1006
|
opts.mcpServers = {
|
|
679
1007
|
[MCP_SERVER_NAME]: mcpServer
|
|
680
1008
|
};
|
|
681
1009
|
const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
|
|
1010
|
+
if (onAskUser) {
|
|
1011
|
+
mcpToolNames.push(mcpToolName("ask_user"));
|
|
1012
|
+
}
|
|
682
1013
|
opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
|
|
683
1014
|
}
|
|
1015
|
+
if (onAskUser) {
|
|
1016
|
+
opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
|
|
1017
|
+
}
|
|
684
1018
|
return opts;
|
|
685
1019
|
}
|
|
1020
|
+
// ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
|
|
1021
|
+
/** Setup a retry query: clear session, rebuild with full history */
|
|
1022
|
+
async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
|
|
1023
|
+
this.clearPersistentSession();
|
|
1024
|
+
const retryPrompt = buildContextualPrompt(messages);
|
|
1025
|
+
let retryOpts = this.buildQueryOptions(signal, options);
|
|
1026
|
+
toolResultCapture.clear();
|
|
1027
|
+
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1028
|
+
modifyOpts?.(retryOpts);
|
|
1029
|
+
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1030
|
+
this.activeQuery = retryQ;
|
|
1031
|
+
return retryQ;
|
|
1032
|
+
}
|
|
1033
|
+
/** Extract tool_use blocks from an assistant SDK message into toolCalls array */
|
|
1034
|
+
collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
|
|
1035
|
+
if (msg.type !== "assistant") return;
|
|
1036
|
+
const betaMessage = msg.message;
|
|
1037
|
+
if (!betaMessage?.content) return;
|
|
1038
|
+
for (const block of betaMessage.content) {
|
|
1039
|
+
if (block.type === "tool_use") {
|
|
1040
|
+
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1041
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
1042
|
+
toolCalls.push({
|
|
1043
|
+
toolName,
|
|
1044
|
+
args: block.input ?? {},
|
|
1045
|
+
result: toolResultCapture.get(toolName) ?? null,
|
|
1046
|
+
approved: true
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/** Back-fill tool results from capture map on summary/result messages */
|
|
1052
|
+
backfillToolResults(msg, toolCalls, toolResultCapture) {
|
|
1053
|
+
if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
|
|
1054
|
+
for (const tc of toolCalls) {
|
|
1055
|
+
if (tc.result === null) {
|
|
1056
|
+
const captured = toolResultCapture.get(tc.toolName);
|
|
1057
|
+
if (captured !== void 0) tc.result = captured;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
/** Wrap retry inner loop with shared error handling */
|
|
1062
|
+
async withRetryErrorHandling(signal, fn) {
|
|
1063
|
+
try {
|
|
1064
|
+
return await fn();
|
|
1065
|
+
} catch (retryError) {
|
|
1066
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
1067
|
+
if (signal.aborted) throw new AbortError();
|
|
1068
|
+
throw retryError;
|
|
1069
|
+
} finally {
|
|
1070
|
+
this.activeQuery = null;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
686
1073
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
687
|
-
async executeRun(messages,
|
|
1074
|
+
async executeRun(messages, options, signal) {
|
|
688
1075
|
this.checkAbort(signal);
|
|
689
1076
|
const sdk = await loadSDK();
|
|
690
1077
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
691
1078
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
692
|
-
let opts = this.buildQueryOptions(signal);
|
|
1079
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
693
1080
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
694
1081
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
695
1082
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -699,30 +1086,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
699
1086
|
let usage;
|
|
700
1087
|
try {
|
|
701
1088
|
for await (const msg of q) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (betaMessage?.content) {
|
|
705
|
-
for (const block of betaMessage.content) {
|
|
706
|
-
if (block.type === "tool_use") {
|
|
707
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
708
|
-
toolCalls.push({
|
|
709
|
-
toolName,
|
|
710
|
-
args: block.input ?? {},
|
|
711
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
712
|
-
approved: true
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
719
|
-
for (const tc of toolCalls) {
|
|
720
|
-
if (tc.result === null) {
|
|
721
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
722
|
-
if (captured !== void 0) tc.result = captured;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
1089
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1090
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
726
1091
|
if (msg.type === "result") {
|
|
727
1092
|
if (msg.subtype === "success") {
|
|
728
1093
|
const r = msg;
|
|
@@ -740,8 +1105,44 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
740
1105
|
}
|
|
741
1106
|
}
|
|
742
1107
|
} catch (e) {
|
|
743
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
744
1108
|
if (signal.aborted) throw new AbortError();
|
|
1109
|
+
if (isResuming && this.isPersistent) {
|
|
1110
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1111
|
+
toolCalls.length = 0;
|
|
1112
|
+
output = null;
|
|
1113
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1114
|
+
for await (const msg of retryQ) {
|
|
1115
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1116
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1117
|
+
if (msg.type === "result") {
|
|
1118
|
+
if (msg.subtype === "success") {
|
|
1119
|
+
const r = msg;
|
|
1120
|
+
output = r.result;
|
|
1121
|
+
usage = aggregateUsage(r.modelUsage);
|
|
1122
|
+
if (this.isPersistent && r.session_id) {
|
|
1123
|
+
this._sessionId = r.session_id;
|
|
1124
|
+
}
|
|
1125
|
+
} else if (msg.is_error) {
|
|
1126
|
+
const r = msg;
|
|
1127
|
+
throw new Error(
|
|
1128
|
+
`Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return {
|
|
1134
|
+
output,
|
|
1135
|
+
structuredOutput: void 0,
|
|
1136
|
+
toolCalls,
|
|
1137
|
+
messages: [
|
|
1138
|
+
...messages,
|
|
1139
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1140
|
+
],
|
|
1141
|
+
usage
|
|
1142
|
+
};
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
745
1146
|
throw e;
|
|
746
1147
|
} finally {
|
|
747
1148
|
this.activeQuery = null;
|
|
@@ -758,12 +1159,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
758
1159
|
};
|
|
759
1160
|
}
|
|
760
1161
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
761
|
-
async executeRunStructured(messages, schema,
|
|
1162
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
762
1163
|
this.checkAbort(signal);
|
|
763
1164
|
const sdk = await loadSDK();
|
|
764
1165
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
765
1166
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
766
|
-
let opts = this.buildQueryOptions(signal);
|
|
1167
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
767
1168
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
768
1169
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
769
1170
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
@@ -779,30 +1180,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
779
1180
|
let usage;
|
|
780
1181
|
try {
|
|
781
1182
|
for await (const msg of q) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (betaMessage?.content) {
|
|
785
|
-
for (const block of betaMessage.content) {
|
|
786
|
-
if (block.type === "tool_use") {
|
|
787
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
788
|
-
toolCalls.push({
|
|
789
|
-
toolName,
|
|
790
|
-
args: block.input ?? {},
|
|
791
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
792
|
-
approved: true
|
|
793
|
-
});
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
799
|
-
for (const tc of toolCalls) {
|
|
800
|
-
if (tc.result === null) {
|
|
801
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
802
|
-
if (captured !== void 0) tc.result = captured;
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
1183
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1184
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
806
1185
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
807
1186
|
const r = msg;
|
|
808
1187
|
output = r.result;
|
|
@@ -835,8 +1214,69 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
835
1214
|
}
|
|
836
1215
|
}
|
|
837
1216
|
} catch (e) {
|
|
838
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
839
1217
|
if (signal.aborted) throw new AbortError();
|
|
1218
|
+
if (isResuming && this.isPersistent) {
|
|
1219
|
+
const retryQ = await this.prepareRetryQuery(
|
|
1220
|
+
sdk,
|
|
1221
|
+
messages,
|
|
1222
|
+
signal,
|
|
1223
|
+
options,
|
|
1224
|
+
toolResultCapture,
|
|
1225
|
+
(opts2) => {
|
|
1226
|
+
opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
|
|
1227
|
+
}
|
|
1228
|
+
);
|
|
1229
|
+
toolCalls.length = 0;
|
|
1230
|
+
output = null;
|
|
1231
|
+
structuredOutput = void 0;
|
|
1232
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1233
|
+
for await (const msg of retryQ) {
|
|
1234
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1235
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1236
|
+
if (msg.type === "result" && msg.subtype === "success") {
|
|
1237
|
+
const r = msg;
|
|
1238
|
+
output = r.result;
|
|
1239
|
+
if (r.structured_output !== void 0) {
|
|
1240
|
+
try {
|
|
1241
|
+
structuredOutput = schema.schema.parse(r.structured_output);
|
|
1242
|
+
} catch {
|
|
1243
|
+
try {
|
|
1244
|
+
structuredOutput = schema.schema.parse(JSON.parse(r.result));
|
|
1245
|
+
} catch {
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
} else if (r.result) {
|
|
1249
|
+
try {
|
|
1250
|
+
const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1251
|
+
const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
|
|
1252
|
+
structuredOutput = schema.schema.parse(JSON.parse(raw));
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
usage = aggregateUsage(r.modelUsage);
|
|
1257
|
+
if (this.isPersistent && r.session_id) {
|
|
1258
|
+
this._sessionId = r.session_id;
|
|
1259
|
+
}
|
|
1260
|
+
} else if (msg.type === "result" && msg.is_error) {
|
|
1261
|
+
const r = msg;
|
|
1262
|
+
throw new Error(
|
|
1263
|
+
`Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return {
|
|
1268
|
+
output,
|
|
1269
|
+
structuredOutput,
|
|
1270
|
+
toolCalls,
|
|
1271
|
+
messages: [
|
|
1272
|
+
...messages,
|
|
1273
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1274
|
+
],
|
|
1275
|
+
usage
|
|
1276
|
+
};
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
840
1280
|
throw e;
|
|
841
1281
|
} finally {
|
|
842
1282
|
this.activeQuery = null;
|
|
@@ -853,39 +1293,134 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
853
1293
|
};
|
|
854
1294
|
}
|
|
855
1295
|
// ─── executeStream ──────────────────────────────────────────────
|
|
856
|
-
async *executeStream(messages,
|
|
1296
|
+
async *executeStream(messages, options, signal) {
|
|
857
1297
|
this.checkAbort(signal);
|
|
858
1298
|
const sdk = await loadSDK();
|
|
859
1299
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
860
1300
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
861
|
-
let opts = this.buildQueryOptions(signal);
|
|
862
|
-
|
|
1301
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1302
|
+
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1303
|
+
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
863
1304
|
const q = sdk.query({ prompt, options: opts });
|
|
864
1305
|
this.activeQuery = q;
|
|
865
1306
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
866
1307
|
const toolCallTracker = new ClaudeToolCallTracker();
|
|
1308
|
+
const pendingStreamToolCalls = /* @__PURE__ */ new Map();
|
|
1309
|
+
let hasStreamedText = false;
|
|
867
1310
|
try {
|
|
868
1311
|
for await (const msg of q) {
|
|
869
1312
|
if (signal.aborted) throw new AbortError();
|
|
870
1313
|
const events = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
|
|
871
1314
|
if (events) {
|
|
872
1315
|
const mapped = Array.isArray(events) ? events : [events];
|
|
873
|
-
for (const e of mapped)
|
|
1316
|
+
for (const e of mapped) {
|
|
1317
|
+
if (e.type === "tool_call_start") {
|
|
1318
|
+
pendingStreamToolCalls.set(e.toolCallId, e.toolName);
|
|
1319
|
+
}
|
|
1320
|
+
if (e.type === "tool_call_end" && toolResultCapture.has(e.toolName)) {
|
|
1321
|
+
e.result = toolResultCapture.get(e.toolName);
|
|
1322
|
+
toolResultCapture.delete(e.toolName);
|
|
1323
|
+
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1324
|
+
} else if (e.type === "tool_call_end") {
|
|
1325
|
+
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1326
|
+
}
|
|
1327
|
+
if (e.type === "text_delta") hasStreamedText = true;
|
|
1328
|
+
yield e;
|
|
1329
|
+
}
|
|
874
1330
|
}
|
|
875
1331
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
876
1332
|
const r = msg;
|
|
1333
|
+
for (const [toolCallId, toolName] of pendingStreamToolCalls) {
|
|
1334
|
+
if (toolResultCapture.has(toolName)) {
|
|
1335
|
+
yield {
|
|
1336
|
+
type: "tool_call_end",
|
|
1337
|
+
toolCallId,
|
|
1338
|
+
toolName,
|
|
1339
|
+
result: toolResultCapture.get(toolName)
|
|
1340
|
+
};
|
|
1341
|
+
toolResultCapture.delete(toolName);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
pendingStreamToolCalls.clear();
|
|
877
1345
|
if (r.session_id) {
|
|
878
1346
|
if (this.isPersistent) {
|
|
879
1347
|
this._sessionId = r.session_id;
|
|
880
1348
|
}
|
|
881
1349
|
yield this.emitSessionInfo(r.session_id);
|
|
882
1350
|
}
|
|
883
|
-
yield {
|
|
1351
|
+
yield {
|
|
1352
|
+
type: "done",
|
|
1353
|
+
finalOutput: hasStreamedText ? null : r.result,
|
|
1354
|
+
...hasStreamedText ? { streamed: true } : {}
|
|
1355
|
+
};
|
|
884
1356
|
}
|
|
885
1357
|
}
|
|
886
1358
|
} catch (e) {
|
|
887
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
888
1359
|
if (signal.aborted) throw new AbortError();
|
|
1360
|
+
if (isResuming && this.isPersistent) {
|
|
1361
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1362
|
+
const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1363
|
+
const retryToolCallTracker = new ClaudeToolCallTracker();
|
|
1364
|
+
const retryPendingToolCalls = /* @__PURE__ */ new Map();
|
|
1365
|
+
let retryHasStreamedText = false;
|
|
1366
|
+
try {
|
|
1367
|
+
for await (const msg of retryQ) {
|
|
1368
|
+
if (signal.aborted) throw new AbortError();
|
|
1369
|
+
const retryEvents = mapSDKMessage(msg, retryThinkingBlockIndices, retryToolCallTracker);
|
|
1370
|
+
if (retryEvents) {
|
|
1371
|
+
const mapped = Array.isArray(retryEvents) ? retryEvents : [retryEvents];
|
|
1372
|
+
for (const ev of mapped) {
|
|
1373
|
+
if (ev.type === "tool_call_start") {
|
|
1374
|
+
retryPendingToolCalls.set(ev.toolCallId, ev.toolName);
|
|
1375
|
+
}
|
|
1376
|
+
if (ev.type === "tool_call_end" && toolResultCapture.has(ev.toolName)) {
|
|
1377
|
+
ev.result = toolResultCapture.get(ev.toolName);
|
|
1378
|
+
toolResultCapture.delete(ev.toolName);
|
|
1379
|
+
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1380
|
+
} else if (ev.type === "tool_call_end") {
|
|
1381
|
+
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1382
|
+
}
|
|
1383
|
+
if (ev.type === "text_delta") retryHasStreamedText = true;
|
|
1384
|
+
yield ev;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
if (msg.type === "result" && msg.subtype === "success") {
|
|
1388
|
+
const r = msg;
|
|
1389
|
+
for (const [toolCallId, toolName] of retryPendingToolCalls) {
|
|
1390
|
+
if (toolResultCapture.has(toolName)) {
|
|
1391
|
+
yield {
|
|
1392
|
+
type: "tool_call_end",
|
|
1393
|
+
toolCallId,
|
|
1394
|
+
toolName,
|
|
1395
|
+
result: toolResultCapture.get(toolName)
|
|
1396
|
+
};
|
|
1397
|
+
toolResultCapture.delete(toolName);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
retryPendingToolCalls.clear();
|
|
1401
|
+
if (r.session_id) {
|
|
1402
|
+
if (this.isPersistent) {
|
|
1403
|
+
this._sessionId = r.session_id;
|
|
1404
|
+
}
|
|
1405
|
+
yield this.emitSessionInfo(r.session_id);
|
|
1406
|
+
}
|
|
1407
|
+
yield {
|
|
1408
|
+
type: "done",
|
|
1409
|
+
finalOutput: retryHasStreamedText ? null : r.result,
|
|
1410
|
+
...retryHasStreamedText ? { streamed: true } : {}
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
} catch (retryError) {
|
|
1415
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
1416
|
+
if (signal.aborted) throw new AbortError();
|
|
1417
|
+
throw retryError;
|
|
1418
|
+
} finally {
|
|
1419
|
+
this.activeQuery = null;
|
|
1420
|
+
}
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
889
1424
|
throw e;
|
|
890
1425
|
} finally {
|
|
891
1426
|
this.activeQuery = null;
|
|
@@ -896,29 +1431,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
896
1431
|
super.dispose();
|
|
897
1432
|
}
|
|
898
1433
|
};
|
|
899
|
-
function extractLastUserPrompt(messages) {
|
|
900
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
901
|
-
const msg = messages[i];
|
|
902
|
-
if (msg.role === "user") {
|
|
903
|
-
return getTextContent(msg.content);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
return "";
|
|
907
|
-
}
|
|
908
|
-
function buildContextualPrompt(messages) {
|
|
909
|
-
if (messages.length <= 1) {
|
|
910
|
-
return extractLastUserPrompt(messages);
|
|
911
|
-
}
|
|
912
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
913
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
914
|
-
return msg.role === "user" ? `User: ${text}` : `Assistant: ${text}`;
|
|
915
|
-
}).join("\n");
|
|
916
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
917
|
-
return `Conversation history:
|
|
918
|
-
${history}
|
|
919
|
-
|
|
920
|
-
User: ${lastPrompt}`;
|
|
921
|
-
}
|
|
922
1434
|
var ClaudeAgentService = class {
|
|
923
1435
|
name = "claude";
|
|
924
1436
|
disposed = false;
|
|
@@ -958,7 +1470,8 @@ var ClaudeAgentService = class {
|
|
|
958
1470
|
this.cachedModels = body.data.map((m) => ({
|
|
959
1471
|
id: m.id,
|
|
960
1472
|
name: m.display_name,
|
|
961
|
-
provider: "claude"
|
|
1473
|
+
provider: "claude",
|
|
1474
|
+
...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
|
|
962
1475
|
}));
|
|
963
1476
|
return this.cachedModels;
|
|
964
1477
|
}
|