@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.js
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
// src/types.ts
|
|
1
|
+
// src/types/errors.ts
|
|
2
|
+
var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
|
|
3
|
+
"TIMEOUT" /* TIMEOUT */,
|
|
4
|
+
"RATE_LIMIT" /* RATE_LIMIT */,
|
|
5
|
+
"NETWORK" /* NETWORK */,
|
|
6
|
+
"TOOL_EXECUTION" /* TOOL_EXECUTION */,
|
|
7
|
+
"MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
|
|
8
|
+
"PROVIDER_ERROR" /* PROVIDER_ERROR */
|
|
9
|
+
]);
|
|
10
|
+
function isRecoverableErrorCode(code) {
|
|
11
|
+
return RECOVERABLE_CODES.has(code);
|
|
12
|
+
}
|
|
13
|
+
function classifyAgentError(error) {
|
|
14
|
+
const msg = (error instanceof Error ? error.message : error).toLowerCase();
|
|
15
|
+
if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
|
|
16
|
+
return "TIMEOUT" /* TIMEOUT */;
|
|
17
|
+
}
|
|
18
|
+
if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
|
|
19
|
+
return "RATE_LIMIT" /* RATE_LIMIT */;
|
|
20
|
+
}
|
|
21
|
+
if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
|
|
22
|
+
return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
|
|
23
|
+
}
|
|
24
|
+
if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
|
|
25
|
+
return "NETWORK" /* NETWORK */;
|
|
26
|
+
}
|
|
27
|
+
if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
|
|
28
|
+
return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
|
|
29
|
+
}
|
|
30
|
+
if (msg.includes("abort") || msg.includes("cancel")) {
|
|
31
|
+
return "ABORTED" /* ABORTED */;
|
|
32
|
+
}
|
|
33
|
+
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")) {
|
|
34
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
35
|
+
}
|
|
36
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/types/guards.ts
|
|
2
40
|
function getTextContent(content) {
|
|
3
41
|
if (typeof content === "string") return content;
|
|
4
42
|
return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
@@ -8,9 +46,18 @@ function getTextContent(content) {
|
|
|
8
46
|
var AgentSDKError = class extends Error {
|
|
9
47
|
/** @internal Marker for cross-bundle identity checks */
|
|
10
48
|
_agentSDKError = true;
|
|
49
|
+
/** Machine-readable error code. Prefer values from the ErrorCode enum. */
|
|
50
|
+
code;
|
|
51
|
+
/** Whether this error is safe to retry */
|
|
52
|
+
retryable;
|
|
53
|
+
/** HTTP status code hint for error classification */
|
|
54
|
+
httpStatus;
|
|
11
55
|
constructor(message, options) {
|
|
12
56
|
super(message, options);
|
|
13
57
|
this.name = "AgentSDKError";
|
|
58
|
+
this.code = options?.code;
|
|
59
|
+
this.retryable = options?.retryable ?? false;
|
|
60
|
+
this.httpStatus = options?.httpStatus;
|
|
14
61
|
}
|
|
15
62
|
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
16
63
|
static is(error) {
|
|
@@ -19,28 +66,41 @@ var AgentSDKError = class extends Error {
|
|
|
19
66
|
};
|
|
20
67
|
var ReentrancyError = class extends AgentSDKError {
|
|
21
68
|
constructor() {
|
|
22
|
-
super("Agent is already running. Await the current run before starting another."
|
|
69
|
+
super("Agent is already running. Await the current run before starting another.", {
|
|
70
|
+
code: "REENTRANCY" /* REENTRANCY */
|
|
71
|
+
});
|
|
23
72
|
this.name = "ReentrancyError";
|
|
24
73
|
}
|
|
25
74
|
};
|
|
26
75
|
var DisposedError = class extends AgentSDKError {
|
|
27
76
|
constructor(entity) {
|
|
28
|
-
super(`${entity} has been disposed and cannot be used
|
|
77
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
78
|
+
code: "DISPOSED" /* DISPOSED */
|
|
79
|
+
});
|
|
29
80
|
this.name = "DisposedError";
|
|
30
81
|
}
|
|
31
82
|
};
|
|
32
83
|
var SubprocessError = class extends AgentSDKError {
|
|
33
84
|
constructor(message, options) {
|
|
34
|
-
super(message, options);
|
|
85
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
35
86
|
this.name = "SubprocessError";
|
|
36
87
|
}
|
|
37
88
|
};
|
|
38
89
|
var AbortError = class extends AgentSDKError {
|
|
39
90
|
constructor() {
|
|
40
|
-
super("Agent run was aborted.");
|
|
91
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
41
92
|
this.name = "AbortError";
|
|
42
93
|
}
|
|
43
94
|
};
|
|
95
|
+
var ActivityTimeoutError = class extends AgentSDKError {
|
|
96
|
+
constructor(timeoutMs) {
|
|
97
|
+
super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
|
|
98
|
+
code: "TIMEOUT" /* TIMEOUT */,
|
|
99
|
+
retryable: true
|
|
100
|
+
});
|
|
101
|
+
this.name = "ActivityTimeoutError";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
44
104
|
|
|
45
105
|
// src/base-agent.ts
|
|
46
106
|
var BaseAgent = class {
|
|
@@ -48,6 +108,7 @@ var BaseAgent = class {
|
|
|
48
108
|
abortController = null;
|
|
49
109
|
config;
|
|
50
110
|
_cleanupExternalSignal = null;
|
|
111
|
+
_streamMiddleware = [];
|
|
51
112
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
52
113
|
get sessionId() {
|
|
53
114
|
return void 0;
|
|
@@ -63,8 +124,11 @@ var BaseAgent = class {
|
|
|
63
124
|
this.state = "running";
|
|
64
125
|
try {
|
|
65
126
|
const messages = [{ role: "user", content: prompt }];
|
|
66
|
-
const result = await this.
|
|
67
|
-
|
|
127
|
+
const result = await this.withRetry(
|
|
128
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
129
|
+
options
|
|
130
|
+
);
|
|
131
|
+
this.enrichAndNotifyUsage(result, options);
|
|
68
132
|
return result;
|
|
69
133
|
} finally {
|
|
70
134
|
this.cleanupRun();
|
|
@@ -76,8 +140,11 @@ var BaseAgent = class {
|
|
|
76
140
|
const ac = this.createAbortController(options?.signal);
|
|
77
141
|
this.state = "running";
|
|
78
142
|
try {
|
|
79
|
-
const result = await this.
|
|
80
|
-
|
|
143
|
+
const result = await this.withRetry(
|
|
144
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
145
|
+
options
|
|
146
|
+
);
|
|
147
|
+
this.enrichAndNotifyUsage(result, options);
|
|
81
148
|
return result;
|
|
82
149
|
} finally {
|
|
83
150
|
this.cleanupRun();
|
|
@@ -90,13 +157,11 @@ var BaseAgent = class {
|
|
|
90
157
|
this.state = "running";
|
|
91
158
|
try {
|
|
92
159
|
const messages = [{ role: "user", content: prompt }];
|
|
93
|
-
const result = await this.
|
|
94
|
-
messages,
|
|
95
|
-
|
|
96
|
-
options,
|
|
97
|
-
ac.signal
|
|
160
|
+
const result = await this.withRetry(
|
|
161
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
162
|
+
options
|
|
98
163
|
);
|
|
99
|
-
this.enrichAndNotifyUsage(result);
|
|
164
|
+
this.enrichAndNotifyUsage(result, options);
|
|
100
165
|
return result;
|
|
101
166
|
} finally {
|
|
102
167
|
this.cleanupRun();
|
|
@@ -109,8 +174,10 @@ var BaseAgent = class {
|
|
|
109
174
|
this.state = "streaming";
|
|
110
175
|
try {
|
|
111
176
|
const messages = [{ role: "user", content: prompt }];
|
|
112
|
-
|
|
113
|
-
|
|
177
|
+
yield* this.streamWithRetry(
|
|
178
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
179
|
+
options
|
|
180
|
+
);
|
|
114
181
|
} finally {
|
|
115
182
|
this.cleanupRun();
|
|
116
183
|
}
|
|
@@ -121,12 +188,37 @@ var BaseAgent = class {
|
|
|
121
188
|
const ac = this.createAbortController(options?.signal);
|
|
122
189
|
this.state = "streaming";
|
|
123
190
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
191
|
+
yield* this.streamWithRetry(
|
|
192
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
193
|
+
options
|
|
194
|
+
);
|
|
126
195
|
} finally {
|
|
127
196
|
this.cleanupRun();
|
|
128
197
|
}
|
|
129
198
|
}
|
|
199
|
+
/** Register a stream middleware. Applied in registration order after built-in transforms. */
|
|
200
|
+
addStreamMiddleware(middleware) {
|
|
201
|
+
this.guardDisposed();
|
|
202
|
+
this._streamMiddleware.push(middleware);
|
|
203
|
+
}
|
|
204
|
+
/** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
|
|
205
|
+
async *applyStreamPipeline(source, options, ac) {
|
|
206
|
+
let stream = this.enrichStream(source, options);
|
|
207
|
+
stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
|
|
208
|
+
stream = this.heartbeatStream(stream);
|
|
209
|
+
if (this._streamMiddleware.length > 0) {
|
|
210
|
+
const ctx = {
|
|
211
|
+
model: options.model,
|
|
212
|
+
backend: this.backendName,
|
|
213
|
+
abortController: ac,
|
|
214
|
+
config: Object.freeze({ ...this.config })
|
|
215
|
+
};
|
|
216
|
+
for (const mw of this._streamMiddleware) {
|
|
217
|
+
stream = mw(stream, ctx);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
yield* stream;
|
|
221
|
+
}
|
|
130
222
|
abort() {
|
|
131
223
|
if (this.abortController) {
|
|
132
224
|
this.abortController.abort();
|
|
@@ -149,26 +241,109 @@ var BaseAgent = class {
|
|
|
149
241
|
this.abort();
|
|
150
242
|
this.state = "disposed";
|
|
151
243
|
}
|
|
244
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
245
|
+
/** Check if an error should be retried given the retry configuration. */
|
|
246
|
+
isRetryableError(error, retry) {
|
|
247
|
+
if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
if (AgentSDKError.is(error)) {
|
|
251
|
+
if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
|
|
252
|
+
return retry.retryableErrors.includes(error.code);
|
|
253
|
+
}
|
|
254
|
+
if (error.retryable) return true;
|
|
255
|
+
if (error.code) return isRecoverableErrorCode(error.code);
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
/** Execute a function with retry logic per RetryConfig. */
|
|
260
|
+
async withRetry(fn, options) {
|
|
261
|
+
const retry = options?.retry;
|
|
262
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
263
|
+
return fn();
|
|
264
|
+
}
|
|
265
|
+
const maxRetries = retry.maxRetries;
|
|
266
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
267
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
268
|
+
let lastError;
|
|
269
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
270
|
+
try {
|
|
271
|
+
return await fn();
|
|
272
|
+
} catch (err) {
|
|
273
|
+
lastError = err;
|
|
274
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
278
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
279
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
280
|
+
throw err;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
throw lastError;
|
|
285
|
+
}
|
|
286
|
+
/** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
|
|
287
|
+
async *streamWithRetry(factory, options) {
|
|
288
|
+
const retry = options?.retry;
|
|
289
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
290
|
+
yield* factory();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const maxRetries = retry.maxRetries;
|
|
294
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
295
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
296
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
297
|
+
try {
|
|
298
|
+
const stream = factory();
|
|
299
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
300
|
+
const first = await iterator.next();
|
|
301
|
+
if (first.done) return;
|
|
302
|
+
yield first.value;
|
|
303
|
+
while (true) {
|
|
304
|
+
const next = await iterator.next();
|
|
305
|
+
if (next.done) break;
|
|
306
|
+
yield next.value;
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
} catch (err) {
|
|
310
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
314
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
315
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
316
|
+
throw err;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ─── CallOptions Resolution ──────────────────────────────────
|
|
322
|
+
/** Resolve tools to use for this call (per-call override > config default) */
|
|
323
|
+
resolveTools(options) {
|
|
324
|
+
return options?.tools ?? this.config.tools ?? [];
|
|
325
|
+
}
|
|
152
326
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
153
327
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
154
|
-
enrichAndNotifyUsage(result) {
|
|
328
|
+
enrichAndNotifyUsage(result, options) {
|
|
155
329
|
if (result.usage) {
|
|
156
330
|
result.usage = {
|
|
157
331
|
...result.usage,
|
|
158
|
-
model:
|
|
332
|
+
model: options.model,
|
|
159
333
|
backend: this.backendName
|
|
160
334
|
};
|
|
161
335
|
this.callOnUsage(result.usage);
|
|
162
336
|
}
|
|
163
337
|
}
|
|
164
338
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
165
|
-
async *enrichStream(source) {
|
|
339
|
+
async *enrichStream(source, options) {
|
|
340
|
+
const model = options.model;
|
|
166
341
|
for await (const event of source) {
|
|
167
342
|
if (event.type === "usage_update") {
|
|
168
343
|
const usage = {
|
|
169
344
|
promptTokens: event.promptTokens,
|
|
170
345
|
completionTokens: event.completionTokens,
|
|
171
|
-
model
|
|
346
|
+
model,
|
|
172
347
|
backend: this.backendName
|
|
173
348
|
};
|
|
174
349
|
this.callOnUsage(usage);
|
|
@@ -238,6 +413,35 @@ var BaseAgent = class {
|
|
|
238
413
|
heartbeatResolve = null;
|
|
239
414
|
}
|
|
240
415
|
}
|
|
416
|
+
// ─── Activity Timeout ────────────────────────────────────────
|
|
417
|
+
/** Wrap a stream to abort on inactivity. Resets timer on every event.
|
|
418
|
+
* When timeoutMs is not set, passes through directly. */
|
|
419
|
+
async *activityTimeoutStream(source, timeoutMs, ac) {
|
|
420
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
421
|
+
yield* source;
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
425
|
+
let timerId;
|
|
426
|
+
try {
|
|
427
|
+
while (true) {
|
|
428
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
429
|
+
timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
|
|
430
|
+
});
|
|
431
|
+
const result = await Promise.race([iterator.next(), timeoutPromise]);
|
|
432
|
+
clearTimeout(timerId);
|
|
433
|
+
if (result.done) break;
|
|
434
|
+
yield result.value;
|
|
435
|
+
}
|
|
436
|
+
} catch (err) {
|
|
437
|
+
if (err instanceof ActivityTimeoutError) {
|
|
438
|
+
ac.abort(err);
|
|
439
|
+
}
|
|
440
|
+
throw err;
|
|
441
|
+
} finally {
|
|
442
|
+
clearTimeout(timerId);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
241
445
|
// ─── Guards ───────────────────────────────────────────────────
|
|
242
446
|
guardReentrancy() {
|
|
243
447
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -336,6 +540,61 @@ function extractSchemaFromDef(schema) {
|
|
|
336
540
|
}
|
|
337
541
|
}
|
|
338
542
|
|
|
543
|
+
// src/backends/shared.ts
|
|
544
|
+
function extractLastUserPrompt(messages) {
|
|
545
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
546
|
+
const msg = messages[i];
|
|
547
|
+
if (msg.role === "user") {
|
|
548
|
+
return getTextContent(msg.content);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return "";
|
|
552
|
+
}
|
|
553
|
+
function serializeToolCall(tc) {
|
|
554
|
+
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
555
|
+
return ` Tool call: ${tc.name}(${args})`;
|
|
556
|
+
}
|
|
557
|
+
function serializeToolResult(tr) {
|
|
558
|
+
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
559
|
+
const prefix = tr.isError ? "[ERROR] " : "";
|
|
560
|
+
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
561
|
+
}
|
|
562
|
+
function buildContextualPrompt(messages) {
|
|
563
|
+
if (messages.length <= 1) {
|
|
564
|
+
return extractLastUserPrompt(messages);
|
|
565
|
+
}
|
|
566
|
+
const history = messages.slice(0, -1).map((msg) => {
|
|
567
|
+
if (msg.role === "user") {
|
|
568
|
+
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
569
|
+
}
|
|
570
|
+
if (msg.role === "tool" && msg.toolResults) {
|
|
571
|
+
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
572
|
+
return `Tool results:
|
|
573
|
+
${results}`;
|
|
574
|
+
}
|
|
575
|
+
if (msg.role === "assistant") {
|
|
576
|
+
const parts = [];
|
|
577
|
+
const thinking = msg.thinking;
|
|
578
|
+
if (thinking) {
|
|
579
|
+
parts.push(`[reasoning: ${thinking}]`);
|
|
580
|
+
}
|
|
581
|
+
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
582
|
+
if (text2) parts.push(text2);
|
|
583
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
584
|
+
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
585
|
+
}
|
|
586
|
+
return `Assistant: ${parts.join("\n")}`;
|
|
587
|
+
}
|
|
588
|
+
const text = msg.content ? getTextContent(msg.content) : "";
|
|
589
|
+
return `${msg.role}: ${text}`;
|
|
590
|
+
}).join("\n");
|
|
591
|
+
const lastPrompt = extractLastUserPrompt(messages);
|
|
592
|
+
return `Conversation history:
|
|
593
|
+
${history}
|
|
594
|
+
|
|
595
|
+
User: ${lastPrompt}`;
|
|
596
|
+
}
|
|
597
|
+
|
|
339
598
|
// src/backends/claude.ts
|
|
340
599
|
var MCP_SERVER_NAME = "agent-sdk-tools";
|
|
341
600
|
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
@@ -345,12 +604,12 @@ function mcpToolName(toolName) {
|
|
|
345
604
|
function stripMcpPrefix(name) {
|
|
346
605
|
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
347
606
|
}
|
|
348
|
-
var
|
|
607
|
+
var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
608
|
+
var _sdkMock = null;
|
|
349
609
|
async function loadSDK() {
|
|
350
|
-
if (
|
|
610
|
+
if (_sdkMock) return _sdkMock;
|
|
351
611
|
try {
|
|
352
|
-
|
|
353
|
-
return sdkModule;
|
|
612
|
+
return await import('@anthropic-ai/claude-agent-sdk');
|
|
354
613
|
} catch {
|
|
355
614
|
throw new SubprocessError(
|
|
356
615
|
"@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
|
|
@@ -358,16 +617,35 @@ async function loadSDK() {
|
|
|
358
617
|
}
|
|
359
618
|
}
|
|
360
619
|
function _injectSDK(mock) {
|
|
361
|
-
|
|
620
|
+
_sdkMock = mock;
|
|
362
621
|
}
|
|
363
622
|
function _resetSDK() {
|
|
364
|
-
|
|
623
|
+
_sdkMock = null;
|
|
365
624
|
}
|
|
366
625
|
var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
|
|
367
626
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
368
627
|
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
369
|
-
function
|
|
370
|
-
if (
|
|
628
|
+
function normalizeAskUserInput(args) {
|
|
629
|
+
if (typeof args.question === "string") {
|
|
630
|
+
return {
|
|
631
|
+
question: args.question,
|
|
632
|
+
choices: Array.isArray(args.choices) ? args.choices : void 0,
|
|
633
|
+
allowFreeform: args.allowFreeform !== false
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const questions = args.questions;
|
|
637
|
+
if (questions && questions.length > 0) {
|
|
638
|
+
const first = questions[0];
|
|
639
|
+
return {
|
|
640
|
+
question: first.question,
|
|
641
|
+
choices: first.options?.map((o) => o.label),
|
|
642
|
+
allowFreeform: true
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return { question: JSON.stringify(args), allowFreeform: true };
|
|
646
|
+
}
|
|
647
|
+
function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
|
|
648
|
+
if (tools.length === 0 && !onAskUser) return void 0;
|
|
371
649
|
const mcpTools = tools.map((tool) => {
|
|
372
650
|
const zodSchema = tool.parameters;
|
|
373
651
|
const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
|
|
@@ -391,6 +669,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
|
391
669
|
}
|
|
392
670
|
);
|
|
393
671
|
});
|
|
672
|
+
if (onAskUser) {
|
|
673
|
+
const askUserTool = sdk.tool(
|
|
674
|
+
"ask_user",
|
|
675
|
+
"Ask the user a question and wait for their response",
|
|
676
|
+
{
|
|
677
|
+
question: { type: "string", description: "The question to ask the user" },
|
|
678
|
+
choices: {
|
|
679
|
+
type: "array",
|
|
680
|
+
items: { type: "string" },
|
|
681
|
+
description: "Optional list of choices for multiple choice"
|
|
682
|
+
},
|
|
683
|
+
questions: {
|
|
684
|
+
type: "array",
|
|
685
|
+
items: {
|
|
686
|
+
type: "object",
|
|
687
|
+
properties: {
|
|
688
|
+
question: { type: "string" },
|
|
689
|
+
options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
description: "Alternative nested question format"
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
async (args) => {
|
|
696
|
+
const normalized = normalizeAskUserInput(args);
|
|
697
|
+
const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
|
|
698
|
+
return {
|
|
699
|
+
content: [{ type: "text", text: response.answer }]
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
mcpTools.push(askUserTool);
|
|
704
|
+
}
|
|
394
705
|
return sdk.createSdkMcpServer({
|
|
395
706
|
name: MCP_SERVER_NAME,
|
|
396
707
|
version: "1.0.0",
|
|
@@ -440,6 +751,7 @@ function buildCanUseTool(config) {
|
|
|
440
751
|
const unifiedRequest = {
|
|
441
752
|
toolName,
|
|
442
753
|
toolArgs: input,
|
|
754
|
+
toolCallId: options.toolUseID,
|
|
443
755
|
suggestedScope: extractSuggestedScope(options.suggestions),
|
|
444
756
|
rawSDKRequest: { toolName, input, ...options }
|
|
445
757
|
};
|
|
@@ -516,6 +828,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
516
828
|
if (block.type === "tool_use") {
|
|
517
829
|
const toolCallId = String(block.id ?? "");
|
|
518
830
|
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
831
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
519
832
|
if (toolCallTracker) {
|
|
520
833
|
toolCallTracker.trackStart(toolCallId, toolName);
|
|
521
834
|
}
|
|
@@ -539,6 +852,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
539
852
|
case "tool_use_summary": {
|
|
540
853
|
const summary = msg.summary;
|
|
541
854
|
const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
|
|
855
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
|
|
542
856
|
const precedingIds = msg.preceding_tool_use_ids;
|
|
543
857
|
let toolCallId = "";
|
|
544
858
|
if (precedingIds && precedingIds.length > 0) {
|
|
@@ -592,10 +906,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
592
906
|
}
|
|
593
907
|
if (msg.is_error) {
|
|
594
908
|
const r = msg;
|
|
909
|
+
const errorMsg = r.errors?.join("; ") ?? "Unknown error";
|
|
910
|
+
const code = classifyAgentError(errorMsg);
|
|
595
911
|
return {
|
|
596
912
|
type: "error",
|
|
597
|
-
error:
|
|
598
|
-
recoverable:
|
|
913
|
+
error: errorMsg,
|
|
914
|
+
recoverable: isRecoverableErrorCode(code),
|
|
915
|
+
code
|
|
599
916
|
};
|
|
600
917
|
}
|
|
601
918
|
return null;
|
|
@@ -621,11 +938,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
621
938
|
if (options.resumeSessionId) {
|
|
622
939
|
this._sessionId = options.resumeSessionId;
|
|
623
940
|
}
|
|
624
|
-
if (config.supervisor?.onAskUser) {
|
|
625
|
-
console.warn(
|
|
626
|
-
"[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
941
|
}
|
|
630
942
|
get sessionId() {
|
|
631
943
|
return this._sessionId;
|
|
@@ -649,12 +961,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
649
961
|
const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
|
|
650
962
|
return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
|
|
651
963
|
}
|
|
652
|
-
buildQueryOptions(signal) {
|
|
964
|
+
buildQueryOptions(signal, options) {
|
|
653
965
|
const ac = new AbortController();
|
|
654
966
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
655
967
|
const opts = {
|
|
656
968
|
abortController: ac,
|
|
657
|
-
model:
|
|
969
|
+
model: options.model,
|
|
658
970
|
maxTurns: this.options.maxTurns,
|
|
659
971
|
cwd: this.options.workingDirectory,
|
|
660
972
|
pathToClaudeCodeExecutable: this.options.cliPath,
|
|
@@ -684,25 +996,85 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
684
996
|
return opts;
|
|
685
997
|
}
|
|
686
998
|
async buildMcpConfig(opts, toolResultCapture) {
|
|
687
|
-
|
|
999
|
+
const onAskUser = this.config.supervisor?.onAskUser;
|
|
1000
|
+
if (this.tools.length === 0 && !onAskUser) return opts;
|
|
688
1001
|
const sdk = await loadSDK();
|
|
689
|
-
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
|
|
1002
|
+
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
|
|
690
1003
|
if (mcpServer) {
|
|
691
1004
|
opts.mcpServers = {
|
|
692
1005
|
[MCP_SERVER_NAME]: mcpServer
|
|
693
1006
|
};
|
|
694
1007
|
const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
|
|
1008
|
+
if (onAskUser) {
|
|
1009
|
+
mcpToolNames.push(mcpToolName("ask_user"));
|
|
1010
|
+
}
|
|
695
1011
|
opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
|
|
696
1012
|
}
|
|
1013
|
+
if (onAskUser) {
|
|
1014
|
+
opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
|
|
1015
|
+
}
|
|
697
1016
|
return opts;
|
|
698
1017
|
}
|
|
1018
|
+
// ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
|
|
1019
|
+
/** Setup a retry query: clear session, rebuild with full history */
|
|
1020
|
+
async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
|
|
1021
|
+
this.clearPersistentSession();
|
|
1022
|
+
const retryPrompt = buildContextualPrompt(messages);
|
|
1023
|
+
let retryOpts = this.buildQueryOptions(signal, options);
|
|
1024
|
+
toolResultCapture.clear();
|
|
1025
|
+
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1026
|
+
modifyOpts?.(retryOpts);
|
|
1027
|
+
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1028
|
+
this.activeQuery = retryQ;
|
|
1029
|
+
return retryQ;
|
|
1030
|
+
}
|
|
1031
|
+
/** Extract tool_use blocks from an assistant SDK message into toolCalls array */
|
|
1032
|
+
collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
|
|
1033
|
+
if (msg.type !== "assistant") return;
|
|
1034
|
+
const betaMessage = msg.message;
|
|
1035
|
+
if (!betaMessage?.content) return;
|
|
1036
|
+
for (const block of betaMessage.content) {
|
|
1037
|
+
if (block.type === "tool_use") {
|
|
1038
|
+
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1039
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
1040
|
+
toolCalls.push({
|
|
1041
|
+
toolName,
|
|
1042
|
+
args: block.input ?? {},
|
|
1043
|
+
result: toolResultCapture.get(toolName) ?? null,
|
|
1044
|
+
approved: true
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
/** Back-fill tool results from capture map on summary/result messages */
|
|
1050
|
+
backfillToolResults(msg, toolCalls, toolResultCapture) {
|
|
1051
|
+
if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
|
|
1052
|
+
for (const tc of toolCalls) {
|
|
1053
|
+
if (tc.result === null) {
|
|
1054
|
+
const captured = toolResultCapture.get(tc.toolName);
|
|
1055
|
+
if (captured !== void 0) tc.result = captured;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
/** Wrap retry inner loop with shared error handling */
|
|
1060
|
+
async withRetryErrorHandling(signal, fn) {
|
|
1061
|
+
try {
|
|
1062
|
+
return await fn();
|
|
1063
|
+
} catch (retryError) {
|
|
1064
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
1065
|
+
if (signal.aborted) throw new AbortError();
|
|
1066
|
+
throw retryError;
|
|
1067
|
+
} finally {
|
|
1068
|
+
this.activeQuery = null;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
699
1071
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
700
|
-
async executeRun(messages,
|
|
1072
|
+
async executeRun(messages, options, signal) {
|
|
701
1073
|
this.checkAbort(signal);
|
|
702
1074
|
const sdk = await loadSDK();
|
|
703
1075
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
704
1076
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
705
|
-
let opts = this.buildQueryOptions(signal);
|
|
1077
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
706
1078
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
707
1079
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
708
1080
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -712,30 +1084,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
712
1084
|
let usage;
|
|
713
1085
|
try {
|
|
714
1086
|
for await (const msg of q) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
if (betaMessage?.content) {
|
|
718
|
-
for (const block of betaMessage.content) {
|
|
719
|
-
if (block.type === "tool_use") {
|
|
720
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
721
|
-
toolCalls.push({
|
|
722
|
-
toolName,
|
|
723
|
-
args: block.input ?? {},
|
|
724
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
725
|
-
approved: true
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
732
|
-
for (const tc of toolCalls) {
|
|
733
|
-
if (tc.result === null) {
|
|
734
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
735
|
-
if (captured !== void 0) tc.result = captured;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
1087
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1088
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
739
1089
|
if (msg.type === "result") {
|
|
740
1090
|
if (msg.subtype === "success") {
|
|
741
1091
|
const r = msg;
|
|
@@ -755,41 +1105,13 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
755
1105
|
} catch (e) {
|
|
756
1106
|
if (signal.aborted) throw new AbortError();
|
|
757
1107
|
if (isResuming && this.isPersistent) {
|
|
758
|
-
this.
|
|
759
|
-
const retryPrompt = buildContextualPrompt(messages);
|
|
760
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
761
|
-
toolResultCapture.clear();
|
|
762
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
763
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
764
|
-
this.activeQuery = retryQ;
|
|
1108
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
765
1109
|
toolCalls.length = 0;
|
|
766
1110
|
output = null;
|
|
767
|
-
|
|
1111
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
768
1112
|
for await (const msg of retryQ) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
if (betaMessage?.content) {
|
|
772
|
-
for (const block of betaMessage.content) {
|
|
773
|
-
if (block.type === "tool_use") {
|
|
774
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
775
|
-
toolCalls.push({
|
|
776
|
-
toolName,
|
|
777
|
-
args: block.input ?? {},
|
|
778
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
779
|
-
approved: true
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
786
|
-
for (const tc of toolCalls) {
|
|
787
|
-
if (tc.result === null) {
|
|
788
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
789
|
-
if (captured !== void 0) tc.result = captured;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
1113
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1114
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
793
1115
|
if (msg.type === "result") {
|
|
794
1116
|
if (msg.subtype === "success") {
|
|
795
1117
|
const r = msg;
|
|
@@ -806,23 +1128,17 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
806
1128
|
}
|
|
807
1129
|
}
|
|
808
1130
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
messages: [
|
|
821
|
-
...messages,
|
|
822
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
823
|
-
],
|
|
824
|
-
usage
|
|
825
|
-
};
|
|
1131
|
+
return {
|
|
1132
|
+
output,
|
|
1133
|
+
structuredOutput: void 0,
|
|
1134
|
+
toolCalls,
|
|
1135
|
+
messages: [
|
|
1136
|
+
...messages,
|
|
1137
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1138
|
+
],
|
|
1139
|
+
usage
|
|
1140
|
+
};
|
|
1141
|
+
});
|
|
826
1142
|
}
|
|
827
1143
|
if (this.isPersistent) this.clearPersistentSession();
|
|
828
1144
|
throw e;
|
|
@@ -841,12 +1157,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
841
1157
|
};
|
|
842
1158
|
}
|
|
843
1159
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
844
|
-
async executeRunStructured(messages, schema,
|
|
1160
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
845
1161
|
this.checkAbort(signal);
|
|
846
1162
|
const sdk = await loadSDK();
|
|
847
1163
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
848
1164
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
849
|
-
let opts = this.buildQueryOptions(signal);
|
|
1165
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
850
1166
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
851
1167
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
852
1168
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
@@ -862,30 +1178,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
862
1178
|
let usage;
|
|
863
1179
|
try {
|
|
864
1180
|
for await (const msg of q) {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (betaMessage?.content) {
|
|
868
|
-
for (const block of betaMessage.content) {
|
|
869
|
-
if (block.type === "tool_use") {
|
|
870
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
871
|
-
toolCalls.push({
|
|
872
|
-
toolName,
|
|
873
|
-
args: block.input ?? {},
|
|
874
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
875
|
-
approved: true
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
882
|
-
for (const tc of toolCalls) {
|
|
883
|
-
if (tc.result === null) {
|
|
884
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
885
|
-
if (captured !== void 0) tc.result = captured;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
}
|
|
1181
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1182
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
889
1183
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
890
1184
|
const r = msg;
|
|
891
1185
|
output = r.result;
|
|
@@ -920,46 +1214,23 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
920
1214
|
} catch (e) {
|
|
921
1215
|
if (signal.aborted) throw new AbortError();
|
|
922
1216
|
if (isResuming && this.isPersistent) {
|
|
923
|
-
this.
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
this.activeQuery = retryQ;
|
|
1217
|
+
const retryQ = await this.prepareRetryQuery(
|
|
1218
|
+
sdk,
|
|
1219
|
+
messages,
|
|
1220
|
+
signal,
|
|
1221
|
+
options,
|
|
1222
|
+
toolResultCapture,
|
|
1223
|
+
(opts2) => {
|
|
1224
|
+
opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
934
1227
|
toolCalls.length = 0;
|
|
935
1228
|
output = null;
|
|
936
1229
|
structuredOutput = void 0;
|
|
937
|
-
|
|
1230
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
938
1231
|
for await (const msg of retryQ) {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (betaMessage?.content) {
|
|
942
|
-
for (const block of betaMessage.content) {
|
|
943
|
-
if (block.type === "tool_use") {
|
|
944
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
945
|
-
toolCalls.push({
|
|
946
|
-
toolName,
|
|
947
|
-
args: block.input ?? {},
|
|
948
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
949
|
-
approved: true
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
956
|
-
for (const tc of toolCalls) {
|
|
957
|
-
if (tc.result === null) {
|
|
958
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
959
|
-
if (captured !== void 0) tc.result = captured;
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
1232
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1233
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
963
1234
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
964
1235
|
const r = msg;
|
|
965
1236
|
output = r.result;
|
|
@@ -991,23 +1262,17 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
991
1262
|
);
|
|
992
1263
|
}
|
|
993
1264
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
messages: [
|
|
1006
|
-
...messages,
|
|
1007
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1008
|
-
],
|
|
1009
|
-
usage
|
|
1010
|
-
};
|
|
1265
|
+
return {
|
|
1266
|
+
output,
|
|
1267
|
+
structuredOutput,
|
|
1268
|
+
toolCalls,
|
|
1269
|
+
messages: [
|
|
1270
|
+
...messages,
|
|
1271
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1272
|
+
],
|
|
1273
|
+
usage
|
|
1274
|
+
};
|
|
1275
|
+
});
|
|
1011
1276
|
}
|
|
1012
1277
|
if (this.isPersistent) this.clearPersistentSession();
|
|
1013
1278
|
throw e;
|
|
@@ -1026,12 +1291,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1026
1291
|
};
|
|
1027
1292
|
}
|
|
1028
1293
|
// ─── executeStream ──────────────────────────────────────────────
|
|
1029
|
-
async *executeStream(messages,
|
|
1294
|
+
async *executeStream(messages, options, signal) {
|
|
1030
1295
|
this.checkAbort(signal);
|
|
1031
1296
|
const sdk = await loadSDK();
|
|
1032
1297
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
1033
1298
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
1034
|
-
let opts = this.buildQueryOptions(signal);
|
|
1299
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1035
1300
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1036
1301
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
1037
1302
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -1039,6 +1304,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1039
1304
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1040
1305
|
const toolCallTracker = new ClaudeToolCallTracker();
|
|
1041
1306
|
const pendingStreamToolCalls = /* @__PURE__ */ new Map();
|
|
1307
|
+
let hasStreamedText = false;
|
|
1042
1308
|
try {
|
|
1043
1309
|
for await (const msg of q) {
|
|
1044
1310
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1056,6 +1322,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1056
1322
|
} else if (e.type === "tool_call_end") {
|
|
1057
1323
|
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1058
1324
|
}
|
|
1325
|
+
if (e.type === "text_delta") hasStreamedText = true;
|
|
1059
1326
|
yield e;
|
|
1060
1327
|
}
|
|
1061
1328
|
}
|
|
@@ -1079,22 +1346,21 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1079
1346
|
}
|
|
1080
1347
|
yield this.emitSessionInfo(r.session_id);
|
|
1081
1348
|
}
|
|
1082
|
-
yield {
|
|
1349
|
+
yield {
|
|
1350
|
+
type: "done",
|
|
1351
|
+
finalOutput: hasStreamedText ? null : r.result,
|
|
1352
|
+
...hasStreamedText ? { streamed: true } : {}
|
|
1353
|
+
};
|
|
1083
1354
|
}
|
|
1084
1355
|
}
|
|
1085
1356
|
} catch (e) {
|
|
1086
1357
|
if (signal.aborted) throw new AbortError();
|
|
1087
1358
|
if (isResuming && this.isPersistent) {
|
|
1088
|
-
this.
|
|
1089
|
-
const retryPrompt = buildContextualPrompt(messages);
|
|
1090
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
1091
|
-
toolResultCapture.clear();
|
|
1092
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1093
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1094
|
-
this.activeQuery = retryQ;
|
|
1359
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1095
1360
|
const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1096
1361
|
const retryToolCallTracker = new ClaudeToolCallTracker();
|
|
1097
1362
|
const retryPendingToolCalls = /* @__PURE__ */ new Map();
|
|
1363
|
+
let retryHasStreamedText = false;
|
|
1098
1364
|
try {
|
|
1099
1365
|
for await (const msg of retryQ) {
|
|
1100
1366
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1112,6 +1378,7 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1112
1378
|
} else if (ev.type === "tool_call_end") {
|
|
1113
1379
|
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1114
1380
|
}
|
|
1381
|
+
if (ev.type === "text_delta") retryHasStreamedText = true;
|
|
1115
1382
|
yield ev;
|
|
1116
1383
|
}
|
|
1117
1384
|
}
|
|
@@ -1135,7 +1402,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1135
1402
|
}
|
|
1136
1403
|
yield this.emitSessionInfo(r.session_id);
|
|
1137
1404
|
}
|
|
1138
|
-
yield {
|
|
1405
|
+
yield {
|
|
1406
|
+
type: "done",
|
|
1407
|
+
finalOutput: retryHasStreamedText ? null : r.result,
|
|
1408
|
+
...retryHasStreamedText ? { streamed: true } : {}
|
|
1409
|
+
};
|
|
1139
1410
|
}
|
|
1140
1411
|
}
|
|
1141
1412
|
} catch (retryError) {
|
|
@@ -1158,59 +1429,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
1158
1429
|
super.dispose();
|
|
1159
1430
|
}
|
|
1160
1431
|
};
|
|
1161
|
-
function extractLastUserPrompt(messages) {
|
|
1162
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1163
|
-
const msg = messages[i];
|
|
1164
|
-
if (msg.role === "user") {
|
|
1165
|
-
return getTextContent(msg.content);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
return "";
|
|
1169
|
-
}
|
|
1170
|
-
function serializeToolCall(tc) {
|
|
1171
|
-
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
1172
|
-
return ` Tool call: ${tc.name}(${args})`;
|
|
1173
|
-
}
|
|
1174
|
-
function serializeToolResult(tr) {
|
|
1175
|
-
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
1176
|
-
const prefix = tr.isError ? "[ERROR] " : "";
|
|
1177
|
-
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
1178
|
-
}
|
|
1179
|
-
function buildContextualPrompt(messages) {
|
|
1180
|
-
if (messages.length <= 1) {
|
|
1181
|
-
return extractLastUserPrompt(messages);
|
|
1182
|
-
}
|
|
1183
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
1184
|
-
if (msg.role === "user") {
|
|
1185
|
-
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
1186
|
-
}
|
|
1187
|
-
if (msg.role === "tool" && msg.toolResults) {
|
|
1188
|
-
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
1189
|
-
return `Tool results:
|
|
1190
|
-
${results}`;
|
|
1191
|
-
}
|
|
1192
|
-
if (msg.role === "assistant") {
|
|
1193
|
-
const parts = [];
|
|
1194
|
-
const thinking = msg.thinking;
|
|
1195
|
-
if (thinking) {
|
|
1196
|
-
parts.push(`[reasoning: ${thinking}]`);
|
|
1197
|
-
}
|
|
1198
|
-
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
1199
|
-
if (text2) parts.push(text2);
|
|
1200
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
1201
|
-
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
1202
|
-
}
|
|
1203
|
-
return `Assistant: ${parts.join("\n")}`;
|
|
1204
|
-
}
|
|
1205
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
1206
|
-
return `${msg.role}: ${text}`;
|
|
1207
|
-
}).join("\n");
|
|
1208
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
1209
|
-
return `Conversation history:
|
|
1210
|
-
${history}
|
|
1211
|
-
|
|
1212
|
-
User: ${lastPrompt}`;
|
|
1213
|
-
}
|
|
1214
1432
|
var ClaudeAgentService = class {
|
|
1215
1433
|
name = "claude";
|
|
1216
1434
|
disposed = false;
|
|
@@ -1250,7 +1468,8 @@ var ClaudeAgentService = class {
|
|
|
1250
1468
|
this.cachedModels = body.data.map((m) => ({
|
|
1251
1469
|
id: m.id,
|
|
1252
1470
|
name: m.display_name,
|
|
1253
|
-
provider: "claude"
|
|
1471
|
+
provider: "claude",
|
|
1472
|
+
...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
|
|
1254
1473
|
}));
|
|
1255
1474
|
return this.cachedModels;
|
|
1256
1475
|
}
|