@witqq/agent-sdk 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -34
- package/dist/{types-CqvUAYxt.d.cts → agent-CW9XbmG_.d.ts} +137 -102
- package/dist/{types-CqvUAYxt.d.ts → agent-DxY68NZL.d.cts} +137 -102
- package/dist/auth/index.cjs +72 -1
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +21 -154
- package/dist/auth/index.d.ts +21 -154
- package/dist/auth/index.js +72 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/backends/claude.cjs +480 -261
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +3 -1
- package/dist/backends/claude.d.ts +3 -1
- package/dist/backends/claude.js +480 -261
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +329 -97
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +12 -4
- package/dist/backends/copilot.d.ts +12 -4
- package/dist/backends/copilot.js +329 -97
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +294 -61
- 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 +294 -61
- 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 +1 -1
- package/dist/chat/accumulator.cjs.map +1 -1
- package/dist/chat/accumulator.d.cts +5 -2
- package/dist/chat/accumulator.d.ts +5 -2
- package/dist/chat/accumulator.js +1 -1
- package/dist/chat/accumulator.js.map +1 -1
- package/dist/chat/backends.cjs +736 -746
- package/dist/chat/backends.cjs.map +1 -1
- package/dist/chat/backends.d.cts +10 -6
- package/dist/chat/backends.d.ts +10 -6
- package/dist/chat/backends.js +736 -725
- package/dist/chat/backends.js.map +1 -1
- package/dist/chat/context.cjs +50 -0
- package/dist/chat/context.cjs.map +1 -1
- package/dist/chat/context.d.cts +27 -3
- package/dist/chat/context.d.ts +27 -3
- package/dist/chat/context.js +50 -0
- package/dist/chat/context.js.map +1 -1
- package/dist/chat/core.cjs +25 -2
- package/dist/chat/core.cjs.map +1 -1
- package/dist/chat/core.d.cts +30 -381
- package/dist/chat/core.d.ts +30 -381
- package/dist/chat/core.js +24 -3
- package/dist/chat/core.js.map +1 -1
- package/dist/chat/errors.cjs +48 -26
- package/dist/chat/errors.cjs.map +1 -1
- package/dist/chat/errors.d.cts +6 -31
- package/dist/chat/errors.d.ts +6 -31
- package/dist/chat/errors.js +48 -25
- package/dist/chat/errors.js.map +1 -1
- package/dist/chat/events.cjs.map +1 -1
- package/dist/chat/events.d.cts +6 -2
- package/dist/chat/events.d.ts +6 -2
- package/dist/chat/events.js.map +1 -1
- package/dist/chat/index.cjs +1199 -1008
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +35 -10
- package/dist/chat/index.d.ts +35 -10
- package/dist/chat/index.js +1196 -987
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +2003 -1153
- package/dist/chat/react.cjs.map +1 -1
- package/dist/chat/react.d.cts +590 -121
- package/dist/chat/react.d.ts +590 -121
- package/dist/chat/react.js +1984 -1151
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/runtime.cjs +401 -186
- package/dist/chat/runtime.cjs.map +1 -1
- package/dist/chat/runtime.d.cts +92 -28
- package/dist/chat/runtime.d.ts +92 -28
- package/dist/chat/runtime.js +401 -186
- package/dist/chat/runtime.js.map +1 -1
- package/dist/chat/server.cjs +2234 -209
- package/dist/chat/server.cjs.map +1 -1
- package/dist/chat/server.d.cts +451 -90
- package/dist/chat/server.d.ts +451 -90
- package/dist/chat/server.js +2221 -210
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/sessions.cjs +25 -43
- package/dist/chat/sessions.cjs.map +1 -1
- package/dist/chat/sessions.d.cts +37 -118
- package/dist/chat/sessions.d.ts +37 -118
- package/dist/chat/sessions.js +25 -43
- package/dist/chat/sessions.js.map +1 -1
- 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 +14 -1
- package/dist/chat/state.cjs.map +1 -1
- package/dist/chat/state.d.cts +5 -2
- package/dist/chat/state.d.ts +5 -2
- package/dist/chat/state.js +14 -1
- package/dist/chat/state.js.map +1 -1
- package/dist/chat/storage.cjs +19 -10
- package/dist/chat/storage.cjs.map +1 -1
- package/dist/chat/storage.d.cts +11 -5
- package/dist/chat/storage.d.ts +11 -5
- package/dist/chat/storage.js +19 -10
- package/dist/chat/storage.js.map +1 -1
- 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-C2oPTYs6.d.ts → in-process-transport-C1JnJGVR.d.ts} +28 -23
- package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-C7DSqPyX.d.cts} +28 -23
- package/dist/index.cjs +340 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +292 -123
- package/dist/index.d.ts +292 -123
- package/dist/index.js +334 -47
- 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-DX1Nhm4N.d.cts → transport-Cdh3M0tS.d.cts} +5 -4
- package/dist/{transport-D1OaUgRk.d.ts → transport-Ciap4PWK.d.ts} +5 -4
- package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
- package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
- package/dist/types-DRgd_9R7.d.cts +363 -0
- package/dist/types-ajANVzf7.d.ts +363 -0
- package/package.json +31 -6
- package/dist/errors-BDLbNu9w.d.cts +0 -13
- package/dist/errors-BDLbNu9w.d.ts +0 -13
- package/dist/types-DLZzlJxt.d.ts +0 -39
- package/dist/types-tE0CXwBl.d.cts +0 -39
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");
|
|
@@ -10,9 +48,18 @@ function getTextContent(content) {
|
|
|
10
48
|
var AgentSDKError = class extends Error {
|
|
11
49
|
/** @internal Marker for cross-bundle identity checks */
|
|
12
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;
|
|
13
57
|
constructor(message, options) {
|
|
14
58
|
super(message, options);
|
|
15
59
|
this.name = "AgentSDKError";
|
|
60
|
+
this.code = options?.code;
|
|
61
|
+
this.retryable = options?.retryable ?? false;
|
|
62
|
+
this.httpStatus = options?.httpStatus;
|
|
16
63
|
}
|
|
17
64
|
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
18
65
|
static is(error) {
|
|
@@ -21,28 +68,41 @@ var AgentSDKError = class extends Error {
|
|
|
21
68
|
};
|
|
22
69
|
var ReentrancyError = class extends AgentSDKError {
|
|
23
70
|
constructor() {
|
|
24
|
-
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
|
+
});
|
|
25
74
|
this.name = "ReentrancyError";
|
|
26
75
|
}
|
|
27
76
|
};
|
|
28
77
|
var DisposedError = class extends AgentSDKError {
|
|
29
78
|
constructor(entity) {
|
|
30
|
-
super(`${entity} has been disposed and cannot be used
|
|
79
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
80
|
+
code: "DISPOSED" /* DISPOSED */
|
|
81
|
+
});
|
|
31
82
|
this.name = "DisposedError";
|
|
32
83
|
}
|
|
33
84
|
};
|
|
34
85
|
var SubprocessError = class extends AgentSDKError {
|
|
35
86
|
constructor(message, options) {
|
|
36
|
-
super(message, options);
|
|
87
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
37
88
|
this.name = "SubprocessError";
|
|
38
89
|
}
|
|
39
90
|
};
|
|
40
91
|
var AbortError = class extends AgentSDKError {
|
|
41
92
|
constructor() {
|
|
42
|
-
super("Agent run was aborted.");
|
|
93
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
43
94
|
this.name = "AbortError";
|
|
44
95
|
}
|
|
45
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
|
+
};
|
|
46
106
|
|
|
47
107
|
// src/base-agent.ts
|
|
48
108
|
var BaseAgent = class {
|
|
@@ -50,6 +110,7 @@ var BaseAgent = class {
|
|
|
50
110
|
abortController = null;
|
|
51
111
|
config;
|
|
52
112
|
_cleanupExternalSignal = null;
|
|
113
|
+
_streamMiddleware = [];
|
|
53
114
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
54
115
|
get sessionId() {
|
|
55
116
|
return void 0;
|
|
@@ -65,8 +126,11 @@ var BaseAgent = class {
|
|
|
65
126
|
this.state = "running";
|
|
66
127
|
try {
|
|
67
128
|
const messages = [{ role: "user", content: prompt }];
|
|
68
|
-
const result = await this.
|
|
69
|
-
|
|
129
|
+
const result = await this.withRetry(
|
|
130
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
131
|
+
options
|
|
132
|
+
);
|
|
133
|
+
this.enrichAndNotifyUsage(result, options);
|
|
70
134
|
return result;
|
|
71
135
|
} finally {
|
|
72
136
|
this.cleanupRun();
|
|
@@ -78,8 +142,11 @@ var BaseAgent = class {
|
|
|
78
142
|
const ac = this.createAbortController(options?.signal);
|
|
79
143
|
this.state = "running";
|
|
80
144
|
try {
|
|
81
|
-
const result = await this.
|
|
82
|
-
|
|
145
|
+
const result = await this.withRetry(
|
|
146
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
147
|
+
options
|
|
148
|
+
);
|
|
149
|
+
this.enrichAndNotifyUsage(result, options);
|
|
83
150
|
return result;
|
|
84
151
|
} finally {
|
|
85
152
|
this.cleanupRun();
|
|
@@ -92,13 +159,11 @@ var BaseAgent = class {
|
|
|
92
159
|
this.state = "running";
|
|
93
160
|
try {
|
|
94
161
|
const messages = [{ role: "user", content: prompt }];
|
|
95
|
-
const result = await this.
|
|
96
|
-
messages,
|
|
97
|
-
|
|
98
|
-
options,
|
|
99
|
-
ac.signal
|
|
162
|
+
const result = await this.withRetry(
|
|
163
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
164
|
+
options
|
|
100
165
|
);
|
|
101
|
-
this.enrichAndNotifyUsage(result);
|
|
166
|
+
this.enrichAndNotifyUsage(result, options);
|
|
102
167
|
return result;
|
|
103
168
|
} finally {
|
|
104
169
|
this.cleanupRun();
|
|
@@ -111,8 +176,10 @@ var BaseAgent = class {
|
|
|
111
176
|
this.state = "streaming";
|
|
112
177
|
try {
|
|
113
178
|
const messages = [{ role: "user", content: prompt }];
|
|
114
|
-
|
|
115
|
-
|
|
179
|
+
yield* this.streamWithRetry(
|
|
180
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
181
|
+
options
|
|
182
|
+
);
|
|
116
183
|
} finally {
|
|
117
184
|
this.cleanupRun();
|
|
118
185
|
}
|
|
@@ -123,12 +190,37 @@ var BaseAgent = class {
|
|
|
123
190
|
const ac = this.createAbortController(options?.signal);
|
|
124
191
|
this.state = "streaming";
|
|
125
192
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
193
|
+
yield* this.streamWithRetry(
|
|
194
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
195
|
+
options
|
|
196
|
+
);
|
|
128
197
|
} finally {
|
|
129
198
|
this.cleanupRun();
|
|
130
199
|
}
|
|
131
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
|
+
}
|
|
221
|
+
}
|
|
222
|
+
yield* stream;
|
|
223
|
+
}
|
|
132
224
|
abort() {
|
|
133
225
|
if (this.abortController) {
|
|
134
226
|
this.abortController.abort();
|
|
@@ -151,26 +243,109 @@ var BaseAgent = class {
|
|
|
151
243
|
this.abort();
|
|
152
244
|
this.state = "disposed";
|
|
153
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
|
+
}
|
|
154
328
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
155
329
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
156
|
-
enrichAndNotifyUsage(result) {
|
|
330
|
+
enrichAndNotifyUsage(result, options) {
|
|
157
331
|
if (result.usage) {
|
|
158
332
|
result.usage = {
|
|
159
333
|
...result.usage,
|
|
160
|
-
model:
|
|
334
|
+
model: options.model,
|
|
161
335
|
backend: this.backendName
|
|
162
336
|
};
|
|
163
337
|
this.callOnUsage(result.usage);
|
|
164
338
|
}
|
|
165
339
|
}
|
|
166
340
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
167
|
-
async *enrichStream(source) {
|
|
341
|
+
async *enrichStream(source, options) {
|
|
342
|
+
const model = options.model;
|
|
168
343
|
for await (const event of source) {
|
|
169
344
|
if (event.type === "usage_update") {
|
|
170
345
|
const usage = {
|
|
171
346
|
promptTokens: event.promptTokens,
|
|
172
347
|
completionTokens: event.completionTokens,
|
|
173
|
-
model
|
|
348
|
+
model,
|
|
174
349
|
backend: this.backendName
|
|
175
350
|
};
|
|
176
351
|
this.callOnUsage(usage);
|
|
@@ -240,6 +415,35 @@ var BaseAgent = class {
|
|
|
240
415
|
heartbeatResolve = null;
|
|
241
416
|
}
|
|
242
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
|
+
}
|
|
243
447
|
// ─── Guards ───────────────────────────────────────────────────
|
|
244
448
|
guardReentrancy() {
|
|
245
449
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -338,6 +542,61 @@ function extractSchemaFromDef(schema) {
|
|
|
338
542
|
}
|
|
339
543
|
}
|
|
340
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
|
+
|
|
341
600
|
// src/backends/claude.ts
|
|
342
601
|
var MCP_SERVER_NAME = "agent-sdk-tools";
|
|
343
602
|
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
@@ -347,12 +606,12 @@ function mcpToolName(toolName) {
|
|
|
347
606
|
function stripMcpPrefix(name) {
|
|
348
607
|
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
349
608
|
}
|
|
350
|
-
var
|
|
609
|
+
var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
610
|
+
var _sdkMock = null;
|
|
351
611
|
async function loadSDK() {
|
|
352
|
-
if (
|
|
612
|
+
if (_sdkMock) return _sdkMock;
|
|
353
613
|
try {
|
|
354
|
-
|
|
355
|
-
return sdkModule;
|
|
614
|
+
return await import('@anthropic-ai/claude-agent-sdk');
|
|
356
615
|
} catch {
|
|
357
616
|
throw new SubprocessError(
|
|
358
617
|
"@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
|
|
@@ -360,16 +619,35 @@ async function loadSDK() {
|
|
|
360
619
|
}
|
|
361
620
|
}
|
|
362
621
|
function _injectSDK(mock) {
|
|
363
|
-
|
|
622
|
+
_sdkMock = mock;
|
|
364
623
|
}
|
|
365
624
|
function _resetSDK() {
|
|
366
|
-
|
|
625
|
+
_sdkMock = null;
|
|
367
626
|
}
|
|
368
627
|
var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
|
|
369
628
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
370
629
|
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
371
|
-
function
|
|
372
|
-
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;
|
|
373
651
|
const mcpTools = tools.map((tool) => {
|
|
374
652
|
const zodSchema = tool.parameters;
|
|
375
653
|
const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
|
|
@@ -393,6 +671,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
|
393
671
|
}
|
|
394
672
|
);
|
|
395
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
|
+
}
|
|
396
707
|
return sdk.createSdkMcpServer({
|
|
397
708
|
name: MCP_SERVER_NAME,
|
|
398
709
|
version: "1.0.0",
|
|
@@ -442,6 +753,7 @@ function buildCanUseTool(config) {
|
|
|
442
753
|
const unifiedRequest = {
|
|
443
754
|
toolName,
|
|
444
755
|
toolArgs: input,
|
|
756
|
+
toolCallId: options.toolUseID,
|
|
445
757
|
suggestedScope: extractSuggestedScope(options.suggestions),
|
|
446
758
|
rawSDKRequest: { toolName, input, ...options }
|
|
447
759
|
};
|
|
@@ -518,6 +830,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
518
830
|
if (block.type === "tool_use") {
|
|
519
831
|
const toolCallId = String(block.id ?? "");
|
|
520
832
|
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
833
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
521
834
|
if (toolCallTracker) {
|
|
522
835
|
toolCallTracker.trackStart(toolCallId, toolName);
|
|
523
836
|
}
|
|
@@ -541,6 +854,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
541
854
|
case "tool_use_summary": {
|
|
542
855
|
const summary = msg.summary;
|
|
543
856
|
const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
|
|
857
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
|
|
544
858
|
const precedingIds = msg.preceding_tool_use_ids;
|
|
545
859
|
let toolCallId = "";
|
|
546
860
|
if (precedingIds && precedingIds.length > 0) {
|
|
@@ -594,10 +908,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
594
908
|
}
|
|
595
909
|
if (msg.is_error) {
|
|
596
910
|
const r = msg;
|
|
911
|
+
const errorMsg = r.errors?.join("; ") ?? "Unknown error";
|
|
912
|
+
const code = classifyAgentError(errorMsg);
|
|
597
913
|
return {
|
|
598
914
|
type: "error",
|
|
599
|
-
error:
|
|
600
|
-
recoverable:
|
|
915
|
+
error: errorMsg,
|
|
916
|
+
recoverable: isRecoverableErrorCode(code),
|
|
917
|
+
code
|
|
601
918
|
};
|
|
602
919
|
}
|
|
603
920
|
return null;
|
|
@@ -623,11 +940,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
623
940
|
if (options.resumeSessionId) {
|
|
624
941
|
this._sessionId = options.resumeSessionId;
|
|
625
942
|
}
|
|
626
|
-
if (config.supervisor?.onAskUser) {
|
|
627
|
-
console.warn(
|
|
628
|
-
"[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
943
|
}
|
|
632
944
|
get sessionId() {
|
|
633
945
|
return this._sessionId;
|
|
@@ -651,12 +963,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
651
963
|
const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
|
|
652
964
|
return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
|
|
653
965
|
}
|
|
654
|
-
buildQueryOptions(signal) {
|
|
966
|
+
buildQueryOptions(signal, options) {
|
|
655
967
|
const ac = new AbortController();
|
|
656
968
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
657
969
|
const opts = {
|
|
658
970
|
abortController: ac,
|
|
659
|
-
model:
|
|
971
|
+
model: options.model,
|
|
660
972
|
maxTurns: this.options.maxTurns,
|
|
661
973
|
cwd: this.options.workingDirectory,
|
|
662
974
|
pathToClaudeCodeExecutable: this.options.cliPath,
|
|
@@ -686,25 +998,85 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
686
998
|
return opts;
|
|
687
999
|
}
|
|
688
1000
|
async buildMcpConfig(opts, toolResultCapture) {
|
|
689
|
-
|
|
1001
|
+
const onAskUser = this.config.supervisor?.onAskUser;
|
|
1002
|
+
if (this.tools.length === 0 && !onAskUser) return opts;
|
|
690
1003
|
const sdk = await loadSDK();
|
|
691
|
-
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
|
|
1004
|
+
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
|
|
692
1005
|
if (mcpServer) {
|
|
693
1006
|
opts.mcpServers = {
|
|
694
1007
|
[MCP_SERVER_NAME]: mcpServer
|
|
695
1008
|
};
|
|
696
1009
|
const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
|
|
1010
|
+
if (onAskUser) {
|
|
1011
|
+
mcpToolNames.push(mcpToolName("ask_user"));
|
|
1012
|
+
}
|
|
697
1013
|
opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
|
|
698
1014
|
}
|
|
1015
|
+
if (onAskUser) {
|
|
1016
|
+
opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
|
|
1017
|
+
}
|
|
699
1018
|
return opts;
|
|
700
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
|
+
}
|
|
701
1073
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
702
|
-
async executeRun(messages,
|
|
1074
|
+
async executeRun(messages, options, signal) {
|
|
703
1075
|
this.checkAbort(signal);
|
|
704
1076
|
const sdk = await loadSDK();
|
|
705
1077
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
706
1078
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
707
|
-
let opts = this.buildQueryOptions(signal);
|
|
1079
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
708
1080
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
709
1081
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
710
1082
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -714,30 +1086,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
714
1086
|
let usage;
|
|
715
1087
|
try {
|
|
716
1088
|
for await (const msg of q) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (betaMessage?.content) {
|
|
720
|
-
for (const block of betaMessage.content) {
|
|
721
|
-
if (block.type === "tool_use") {
|
|
722
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
723
|
-
toolCalls.push({
|
|
724
|
-
toolName,
|
|
725
|
-
args: block.input ?? {},
|
|
726
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
727
|
-
approved: true
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
734
|
-
for (const tc of toolCalls) {
|
|
735
|
-
if (tc.result === null) {
|
|
736
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
737
|
-
if (captured !== void 0) tc.result = captured;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
1089
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1090
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
741
1091
|
if (msg.type === "result") {
|
|
742
1092
|
if (msg.subtype === "success") {
|
|
743
1093
|
const r = msg;
|
|
@@ -757,41 +1107,13 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
757
1107
|
} catch (e) {
|
|
758
1108
|
if (signal.aborted) throw new AbortError();
|
|
759
1109
|
if (isResuming && this.isPersistent) {
|
|
760
|
-
this.
|
|
761
|
-
const retryPrompt = buildContextualPrompt(messages);
|
|
762
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
763
|
-
toolResultCapture.clear();
|
|
764
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
765
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
766
|
-
this.activeQuery = retryQ;
|
|
1110
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
767
1111
|
toolCalls.length = 0;
|
|
768
1112
|
output = null;
|
|
769
|
-
|
|
1113
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
770
1114
|
for await (const msg of retryQ) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if (betaMessage?.content) {
|
|
774
|
-
for (const block of betaMessage.content) {
|
|
775
|
-
if (block.type === "tool_use") {
|
|
776
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
777
|
-
toolCalls.push({
|
|
778
|
-
toolName,
|
|
779
|
-
args: block.input ?? {},
|
|
780
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
781
|
-
approved: true
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
788
|
-
for (const tc of toolCalls) {
|
|
789
|
-
if (tc.result === null) {
|
|
790
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
791
|
-
if (captured !== void 0) tc.result = captured;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
1115
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1116
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
795
1117
|
if (msg.type === "result") {
|
|
796
1118
|
if (msg.subtype === "success") {
|
|
797
1119
|
const r = msg;
|
|
@@ -808,23 +1130,17 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
808
1130
|
}
|
|
809
1131
|
}
|
|
810
1132
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
messages: [
|
|
823
|
-
...messages,
|
|
824
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
825
|
-
],
|
|
826
|
-
usage
|
|
827
|
-
};
|
|
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
|
+
});
|
|
828
1144
|
}
|
|
829
1145
|
if (this.isPersistent) this.clearPersistentSession();
|
|
830
1146
|
throw e;
|
|
@@ -843,12 +1159,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
843
1159
|
};
|
|
844
1160
|
}
|
|
845
1161
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
846
|
-
async executeRunStructured(messages, schema,
|
|
1162
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
847
1163
|
this.checkAbort(signal);
|
|
848
1164
|
const sdk = await loadSDK();
|
|
849
1165
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
850
1166
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
851
|
-
let opts = this.buildQueryOptions(signal);
|
|
1167
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
852
1168
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
853
1169
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
854
1170
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
@@ -864,30 +1180,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
864
1180
|
let usage;
|
|
865
1181
|
try {
|
|
866
1182
|
for await (const msg of q) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
if (betaMessage?.content) {
|
|
870
|
-
for (const block of betaMessage.content) {
|
|
871
|
-
if (block.type === "tool_use") {
|
|
872
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
873
|
-
toolCalls.push({
|
|
874
|
-
toolName,
|
|
875
|
-
args: block.input ?? {},
|
|
876
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
877
|
-
approved: true
|
|
878
|
-
});
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
884
|
-
for (const tc of toolCalls) {
|
|
885
|
-
if (tc.result === null) {
|
|
886
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
887
|
-
if (captured !== void 0) tc.result = captured;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
1183
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1184
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
891
1185
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
892
1186
|
const r = msg;
|
|
893
1187
|
output = r.result;
|
|
@@ -922,46 +1216,23 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
922
1216
|
} catch (e) {
|
|
923
1217
|
if (signal.aborted) throw new AbortError();
|
|
924
1218
|
if (isResuming && this.isPersistent) {
|
|
925
|
-
this.
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
this.activeQuery = retryQ;
|
|
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
|
+
);
|
|
936
1229
|
toolCalls.length = 0;
|
|
937
1230
|
output = null;
|
|
938
1231
|
structuredOutput = void 0;
|
|
939
|
-
|
|
1232
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
940
1233
|
for await (const msg of retryQ) {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (betaMessage?.content) {
|
|
944
|
-
for (const block of betaMessage.content) {
|
|
945
|
-
if (block.type === "tool_use") {
|
|
946
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
947
|
-
toolCalls.push({
|
|
948
|
-
toolName,
|
|
949
|
-
args: block.input ?? {},
|
|
950
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
951
|
-
approved: true
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
958
|
-
for (const tc of toolCalls) {
|
|
959
|
-
if (tc.result === null) {
|
|
960
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
961
|
-
if (captured !== void 0) tc.result = captured;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
1234
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1235
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
965
1236
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
966
1237
|
const r = msg;
|
|
967
1238
|
output = r.result;
|
|
@@ -993,23 +1264,17 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
993
1264
|
);
|
|
994
1265
|
}
|
|
995
1266
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
messages: [
|
|
1008
|
-
...messages,
|
|
1009
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1010
|
-
],
|
|
1011
|
-
usage
|
|
1012
|
-
};
|
|
1267
|
+
return {
|
|
1268
|
+
output,
|
|
1269
|
+
structuredOutput,
|
|
1270
|
+
toolCalls,
|
|
1271
|
+
messages: [
|
|
1272
|
+
...messages,
|
|
1273
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1274
|
+
],
|
|
1275
|
+
usage
|
|
1276
|
+
};
|
|
1277
|
+
});
|
|
1013
1278
|
}
|
|
1014
1279
|
if (this.isPersistent) this.clearPersistentSession();
|
|
1015
1280
|
throw e;
|
|
@@ -1028,12 +1293,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1028
1293
|
};
|
|
1029
1294
|
}
|
|
1030
1295
|
// ─── executeStream ──────────────────────────────────────────────
|
|
1031
|
-
async *executeStream(messages,
|
|
1296
|
+
async *executeStream(messages, options, signal) {
|
|
1032
1297
|
this.checkAbort(signal);
|
|
1033
1298
|
const sdk = await loadSDK();
|
|
1034
1299
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
1035
1300
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
1036
|
-
let opts = this.buildQueryOptions(signal);
|
|
1301
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1037
1302
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1038
1303
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
1039
1304
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -1041,6 +1306,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1041
1306
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1042
1307
|
const toolCallTracker = new ClaudeToolCallTracker();
|
|
1043
1308
|
const pendingStreamToolCalls = /* @__PURE__ */ new Map();
|
|
1309
|
+
let hasStreamedText = false;
|
|
1044
1310
|
try {
|
|
1045
1311
|
for await (const msg of q) {
|
|
1046
1312
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1058,6 +1324,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1058
1324
|
} else if (e.type === "tool_call_end") {
|
|
1059
1325
|
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1060
1326
|
}
|
|
1327
|
+
if (e.type === "text_delta") hasStreamedText = true;
|
|
1061
1328
|
yield e;
|
|
1062
1329
|
}
|
|
1063
1330
|
}
|
|
@@ -1081,22 +1348,21 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1081
1348
|
}
|
|
1082
1349
|
yield this.emitSessionInfo(r.session_id);
|
|
1083
1350
|
}
|
|
1084
|
-
yield {
|
|
1351
|
+
yield {
|
|
1352
|
+
type: "done",
|
|
1353
|
+
finalOutput: hasStreamedText ? null : r.result,
|
|
1354
|
+
...hasStreamedText ? { streamed: true } : {}
|
|
1355
|
+
};
|
|
1085
1356
|
}
|
|
1086
1357
|
}
|
|
1087
1358
|
} catch (e) {
|
|
1088
1359
|
if (signal.aborted) throw new AbortError();
|
|
1089
1360
|
if (isResuming && this.isPersistent) {
|
|
1090
|
-
this.
|
|
1091
|
-
const retryPrompt = buildContextualPrompt(messages);
|
|
1092
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
1093
|
-
toolResultCapture.clear();
|
|
1094
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1095
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1096
|
-
this.activeQuery = retryQ;
|
|
1361
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1097
1362
|
const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1098
1363
|
const retryToolCallTracker = new ClaudeToolCallTracker();
|
|
1099
1364
|
const retryPendingToolCalls = /* @__PURE__ */ new Map();
|
|
1365
|
+
let retryHasStreamedText = false;
|
|
1100
1366
|
try {
|
|
1101
1367
|
for await (const msg of retryQ) {
|
|
1102
1368
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1114,6 +1380,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1114
1380
|
} else if (ev.type === "tool_call_end") {
|
|
1115
1381
|
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1116
1382
|
}
|
|
1383
|
+
if (ev.type === "text_delta") retryHasStreamedText = true;
|
|
1117
1384
|
yield ev;
|
|
1118
1385
|
}
|
|
1119
1386
|
}
|
|
@@ -1137,7 +1404,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1137
1404
|
}
|
|
1138
1405
|
yield this.emitSessionInfo(r.session_id);
|
|
1139
1406
|
}
|
|
1140
|
-
yield {
|
|
1407
|
+
yield {
|
|
1408
|
+
type: "done",
|
|
1409
|
+
finalOutput: retryHasStreamedText ? null : r.result,
|
|
1410
|
+
...retryHasStreamedText ? { streamed: true } : {}
|
|
1411
|
+
};
|
|
1141
1412
|
}
|
|
1142
1413
|
}
|
|
1143
1414
|
} catch (retryError) {
|
|
@@ -1160,59 +1431,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1160
1431
|
super.dispose();
|
|
1161
1432
|
}
|
|
1162
1433
|
};
|
|
1163
|
-
function extractLastUserPrompt(messages) {
|
|
1164
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1165
|
-
const msg = messages[i];
|
|
1166
|
-
if (msg.role === "user") {
|
|
1167
|
-
return getTextContent(msg.content);
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
return "";
|
|
1171
|
-
}
|
|
1172
|
-
function serializeToolCall(tc) {
|
|
1173
|
-
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
1174
|
-
return ` Tool call: ${tc.name}(${args})`;
|
|
1175
|
-
}
|
|
1176
|
-
function serializeToolResult(tr) {
|
|
1177
|
-
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
1178
|
-
const prefix = tr.isError ? "[ERROR] " : "";
|
|
1179
|
-
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
1180
|
-
}
|
|
1181
|
-
function buildContextualPrompt(messages) {
|
|
1182
|
-
if (messages.length <= 1) {
|
|
1183
|
-
return extractLastUserPrompt(messages);
|
|
1184
|
-
}
|
|
1185
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
1186
|
-
if (msg.role === "user") {
|
|
1187
|
-
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
1188
|
-
}
|
|
1189
|
-
if (msg.role === "tool" && msg.toolResults) {
|
|
1190
|
-
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
1191
|
-
return `Tool results:
|
|
1192
|
-
${results}`;
|
|
1193
|
-
}
|
|
1194
|
-
if (msg.role === "assistant") {
|
|
1195
|
-
const parts = [];
|
|
1196
|
-
const thinking = msg.thinking;
|
|
1197
|
-
if (thinking) {
|
|
1198
|
-
parts.push(`[reasoning: ${thinking}]`);
|
|
1199
|
-
}
|
|
1200
|
-
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
1201
|
-
if (text2) parts.push(text2);
|
|
1202
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
1203
|
-
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
1204
|
-
}
|
|
1205
|
-
return `Assistant: ${parts.join("\n")}`;
|
|
1206
|
-
}
|
|
1207
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
1208
|
-
return `${msg.role}: ${text}`;
|
|
1209
|
-
}).join("\n");
|
|
1210
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
1211
|
-
return `Conversation history:
|
|
1212
|
-
${history}
|
|
1213
|
-
|
|
1214
|
-
User: ${lastPrompt}`;
|
|
1215
|
-
}
|
|
1216
1434
|
var ClaudeAgentService = class {
|
|
1217
1435
|
name = "claude";
|
|
1218
1436
|
disposed = false;
|
|
@@ -1252,7 +1470,8 @@ var ClaudeAgentService = class {
|
|
|
1252
1470
|
this.cachedModels = body.data.map((m) => ({
|
|
1253
1471
|
id: m.id,
|
|
1254
1472
|
name: m.display_name,
|
|
1255
|
-
provider: "claude"
|
|
1473
|
+
provider: "claude",
|
|
1474
|
+
...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
|
|
1256
1475
|
}));
|
|
1257
1476
|
return this.cachedModels;
|
|
1258
1477
|
}
|