@witqq/agent-sdk 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +539 -6
- package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
- package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
- package/dist/auth/index.cjs +260 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +21 -138
- package/dist/auth/index.d.ts +21 -138
- package/dist/auth/index.js +260 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/backends/claude.cjs +653 -140
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +4 -1
- package/dist/backends/claude.d.ts +4 -1
- package/dist/backends/claude.js +653 -140
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +428 -88
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +13 -4
- package/dist/backends/copilot.d.ts +13 -4
- package/dist/backends/copilot.js +428 -88
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +349 -77
- package/dist/backends/vercel-ai.cjs.map +1 -1
- package/dist/backends/vercel-ai.d.cts +3 -1
- package/dist/backends/vercel-ai.d.ts +3 -1
- package/dist/backends/vercel-ai.js +349 -77
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/backends-BSrsBYFn.d.cts +39 -0
- package/dist/backends-BSrsBYFn.d.ts +39 -0
- package/dist/chat/accumulator.cjs +147 -0
- package/dist/chat/accumulator.cjs.map +1 -0
- package/dist/chat/accumulator.d.cts +64 -0
- package/dist/chat/accumulator.d.ts +64 -0
- package/dist/chat/accumulator.js +145 -0
- package/dist/chat/accumulator.js.map +1 -0
- package/dist/chat/backends.cjs +3524 -0
- package/dist/chat/backends.cjs.map +1 -0
- package/dist/chat/backends.d.cts +66 -0
- package/dist/chat/backends.d.ts +66 -0
- package/dist/chat/backends.js +3512 -0
- package/dist/chat/backends.js.map +1 -0
- package/dist/chat/context.cjs +280 -0
- package/dist/chat/context.cjs.map +1 -0
- package/dist/chat/context.d.cts +191 -0
- package/dist/chat/context.d.ts +191 -0
- package/dist/chat/context.js +277 -0
- package/dist/chat/context.js.map +1 -0
- package/dist/chat/core.cjs +305 -0
- package/dist/chat/core.cjs.map +1 -0
- package/dist/chat/core.d.cts +84 -0
- package/dist/chat/core.d.ts +84 -0
- package/dist/chat/core.js +282 -0
- package/dist/chat/core.js.map +1 -0
- package/dist/chat/errors.cjs +273 -0
- package/dist/chat/errors.cjs.map +1 -0
- package/dist/chat/errors.d.cts +97 -0
- package/dist/chat/errors.d.ts +97 -0
- package/dist/chat/errors.js +266 -0
- package/dist/chat/errors.js.map +1 -0
- package/dist/chat/events.cjs +203 -0
- package/dist/chat/events.cjs.map +1 -0
- package/dist/chat/events.d.cts +245 -0
- package/dist/chat/events.d.ts +245 -0
- package/dist/chat/events.js +196 -0
- package/dist/chat/events.js.map +1 -0
- package/dist/chat/index.cjs +5550 -0
- package/dist/chat/index.cjs.map +1 -0
- package/dist/chat/index.d.cts +77 -0
- package/dist/chat/index.d.ts +77 -0
- package/dist/chat/index.js +5505 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +3589 -0
- package/dist/chat/react.cjs.map +1 -0
- package/dist/chat/react.d.cts +1088 -0
- package/dist/chat/react.d.ts +1088 -0
- package/dist/chat/react.js +3547 -0
- package/dist/chat/react.js.map +1 -0
- package/dist/chat/runtime.cjs +1245 -0
- package/dist/chat/runtime.cjs.map +1 -0
- package/dist/chat/runtime.d.cts +182 -0
- package/dist/chat/runtime.d.ts +182 -0
- package/dist/chat/runtime.js +1243 -0
- package/dist/chat/runtime.js.map +1 -0
- package/dist/chat/server.cjs +2668 -0
- package/dist/chat/server.cjs.map +1 -0
- package/dist/chat/server.d.cts +648 -0
- package/dist/chat/server.d.ts +648 -0
- package/dist/chat/server.js +2628 -0
- package/dist/chat/server.js.map +1 -0
- package/dist/chat/sessions.cjs +380 -0
- package/dist/chat/sessions.cjs.map +1 -0
- package/dist/chat/sessions.d.cts +158 -0
- package/dist/chat/sessions.d.ts +158 -0
- package/dist/chat/sessions.js +376 -0
- package/dist/chat/sessions.js.map +1 -0
- package/dist/chat/sqlite.cjs +441 -0
- package/dist/chat/sqlite.cjs.map +1 -0
- package/dist/chat/sqlite.d.cts +128 -0
- package/dist/chat/sqlite.d.ts +128 -0
- package/dist/chat/sqlite.js +435 -0
- package/dist/chat/sqlite.js.map +1 -0
- package/dist/chat/state.cjs +190 -0
- package/dist/chat/state.cjs.map +1 -0
- package/dist/chat/state.d.cts +95 -0
- package/dist/chat/state.d.ts +95 -0
- package/dist/chat/state.js +180 -0
- package/dist/chat/state.js.map +1 -0
- package/dist/chat/storage.cjs +249 -0
- package/dist/chat/storage.cjs.map +1 -0
- package/dist/chat/storage.d.cts +197 -0
- package/dist/chat/storage.d.ts +197 -0
- package/dist/chat/storage.js +245 -0
- package/dist/chat/storage.js.map +1 -0
- package/dist/errors-C-so0M4t.d.cts +33 -0
- package/dist/errors-C-so0M4t.d.ts +33 -0
- package/dist/errors-CmVvczxZ.d.cts +28 -0
- package/dist/errors-CmVvczxZ.d.ts +28 -0
- package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
- package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
- package/dist/index.cjs +365 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +322 -125
- package/dist/index.d.ts +322 -125
- package/dist/index.js +359 -60
- package/dist/index.js.map +1 -1
- package/dist/provider-types-PTSlRPNB.d.cts +39 -0
- package/dist/provider-types-PTSlRPNB.d.ts +39 -0
- package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
- package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
- package/dist/testing.cjs +383 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +132 -0
- package/dist/testing.d.ts +132 -0
- package/dist/testing.js +377 -0
- package/dist/testing.js.map +1 -0
- package/dist/token-store-CSUBgYwn.d.ts +48 -0
- package/dist/token-store-CuC4hB9Z.d.cts +48 -0
- package/dist/transport-Cdh3M0tS.d.cts +68 -0
- package/dist/transport-Ciap4PWK.d.ts +68 -0
- package/dist/types-4vbcmPTp.d.cts +143 -0
- package/dist/types-BxggH0Yh.d.ts +143 -0
- package/dist/types-DRgd_9R7.d.cts +363 -0
- package/dist/types-ajANVzf7.d.ts +363 -0
- package/package.json +178 -6
package/dist/backends/claude.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");
|
|
@@ -6,41 +44,71 @@ function getTextContent(content) {
|
|
|
6
44
|
|
|
7
45
|
// src/errors.ts
|
|
8
46
|
var AgentSDKError = class extends Error {
|
|
47
|
+
/** @internal Marker for cross-bundle identity checks */
|
|
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;
|
|
9
55
|
constructor(message, options) {
|
|
10
56
|
super(message, options);
|
|
11
57
|
this.name = "AgentSDKError";
|
|
58
|
+
this.code = options?.code;
|
|
59
|
+
this.retryable = options?.retryable ?? false;
|
|
60
|
+
this.httpStatus = options?.httpStatus;
|
|
61
|
+
}
|
|
62
|
+
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
63
|
+
static is(error) {
|
|
64
|
+
return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
|
|
12
65
|
}
|
|
13
66
|
};
|
|
14
67
|
var ReentrancyError = class extends AgentSDKError {
|
|
15
68
|
constructor() {
|
|
16
|
-
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
|
+
});
|
|
17
72
|
this.name = "ReentrancyError";
|
|
18
73
|
}
|
|
19
74
|
};
|
|
20
75
|
var DisposedError = class extends AgentSDKError {
|
|
21
76
|
constructor(entity) {
|
|
22
|
-
super(`${entity} has been disposed and cannot be used
|
|
77
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
78
|
+
code: "DISPOSED" /* DISPOSED */
|
|
79
|
+
});
|
|
23
80
|
this.name = "DisposedError";
|
|
24
81
|
}
|
|
25
82
|
};
|
|
26
83
|
var SubprocessError = class extends AgentSDKError {
|
|
27
84
|
constructor(message, options) {
|
|
28
|
-
super(message, options);
|
|
85
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
29
86
|
this.name = "SubprocessError";
|
|
30
87
|
}
|
|
31
88
|
};
|
|
32
89
|
var AbortError = class extends AgentSDKError {
|
|
33
90
|
constructor() {
|
|
34
|
-
super("Agent run was aborted.");
|
|
91
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
35
92
|
this.name = "AbortError";
|
|
36
93
|
}
|
|
37
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
|
+
};
|
|
38
104
|
|
|
39
105
|
// src/base-agent.ts
|
|
40
106
|
var BaseAgent = class {
|
|
41
107
|
state = "idle";
|
|
42
108
|
abortController = null;
|
|
43
109
|
config;
|
|
110
|
+
_cleanupExternalSignal = null;
|
|
111
|
+
_streamMiddleware = [];
|
|
44
112
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
45
113
|
get sessionId() {
|
|
46
114
|
return void 0;
|
|
@@ -56,12 +124,14 @@ var BaseAgent = class {
|
|
|
56
124
|
this.state = "running";
|
|
57
125
|
try {
|
|
58
126
|
const messages = [{ role: "user", content: prompt }];
|
|
59
|
-
const result = await this.
|
|
60
|
-
|
|
127
|
+
const result = await this.withRetry(
|
|
128
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
129
|
+
options
|
|
130
|
+
);
|
|
131
|
+
this.enrichAndNotifyUsage(result, options);
|
|
61
132
|
return result;
|
|
62
133
|
} finally {
|
|
63
|
-
this.
|
|
64
|
-
this.abortController = null;
|
|
134
|
+
this.cleanupRun();
|
|
65
135
|
}
|
|
66
136
|
}
|
|
67
137
|
async runWithContext(messages, options) {
|
|
@@ -70,12 +140,14 @@ var BaseAgent = class {
|
|
|
70
140
|
const ac = this.createAbortController(options?.signal);
|
|
71
141
|
this.state = "running";
|
|
72
142
|
try {
|
|
73
|
-
const result = await this.
|
|
74
|
-
|
|
143
|
+
const result = await this.withRetry(
|
|
144
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
145
|
+
options
|
|
146
|
+
);
|
|
147
|
+
this.enrichAndNotifyUsage(result, options);
|
|
75
148
|
return result;
|
|
76
149
|
} finally {
|
|
77
|
-
this.
|
|
78
|
-
this.abortController = null;
|
|
150
|
+
this.cleanupRun();
|
|
79
151
|
}
|
|
80
152
|
}
|
|
81
153
|
async runStructured(prompt, schema, options) {
|
|
@@ -85,17 +157,14 @@ var BaseAgent = class {
|
|
|
85
157
|
this.state = "running";
|
|
86
158
|
try {
|
|
87
159
|
const messages = [{ role: "user", content: prompt }];
|
|
88
|
-
const result = await this.
|
|
89
|
-
messages,
|
|
90
|
-
|
|
91
|
-
options,
|
|
92
|
-
ac.signal
|
|
160
|
+
const result = await this.withRetry(
|
|
161
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
162
|
+
options
|
|
93
163
|
);
|
|
94
|
-
this.enrichAndNotifyUsage(result);
|
|
164
|
+
this.enrichAndNotifyUsage(result, options);
|
|
95
165
|
return result;
|
|
96
166
|
} finally {
|
|
97
|
-
this.
|
|
98
|
-
this.abortController = null;
|
|
167
|
+
this.cleanupRun();
|
|
99
168
|
}
|
|
100
169
|
}
|
|
101
170
|
async *stream(prompt, options) {
|
|
@@ -105,11 +174,12 @@ var BaseAgent = class {
|
|
|
105
174
|
this.state = "streaming";
|
|
106
175
|
try {
|
|
107
176
|
const messages = [{ role: "user", content: prompt }];
|
|
108
|
-
|
|
109
|
-
|
|
177
|
+
yield* this.streamWithRetry(
|
|
178
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
179
|
+
options
|
|
180
|
+
);
|
|
110
181
|
} finally {
|
|
111
|
-
this.
|
|
112
|
-
this.abortController = null;
|
|
182
|
+
this.cleanupRun();
|
|
113
183
|
}
|
|
114
184
|
}
|
|
115
185
|
async *streamWithContext(messages, options) {
|
|
@@ -118,12 +188,36 @@ var BaseAgent = class {
|
|
|
118
188
|
const ac = this.createAbortController(options?.signal);
|
|
119
189
|
this.state = "streaming";
|
|
120
190
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
191
|
+
yield* this.streamWithRetry(
|
|
192
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
193
|
+
options
|
|
194
|
+
);
|
|
123
195
|
} finally {
|
|
124
|
-
this.
|
|
125
|
-
|
|
196
|
+
this.cleanupRun();
|
|
197
|
+
}
|
|
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
|
+
}
|
|
126
219
|
}
|
|
220
|
+
yield* stream;
|
|
127
221
|
}
|
|
128
222
|
abort() {
|
|
129
223
|
if (this.abortController) {
|
|
@@ -142,29 +236,114 @@ var BaseAgent = class {
|
|
|
142
236
|
}
|
|
143
237
|
/** Mark agent as disposed. Override to add cleanup. */
|
|
144
238
|
dispose() {
|
|
239
|
+
this._cleanupExternalSignal?.();
|
|
240
|
+
this._cleanupExternalSignal = null;
|
|
145
241
|
this.abort();
|
|
146
242
|
this.state = "disposed";
|
|
147
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
|
+
}
|
|
148
326
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
149
327
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
150
|
-
enrichAndNotifyUsage(result) {
|
|
328
|
+
enrichAndNotifyUsage(result, options) {
|
|
151
329
|
if (result.usage) {
|
|
152
330
|
result.usage = {
|
|
153
331
|
...result.usage,
|
|
154
|
-
model:
|
|
332
|
+
model: options.model,
|
|
155
333
|
backend: this.backendName
|
|
156
334
|
};
|
|
157
335
|
this.callOnUsage(result.usage);
|
|
158
336
|
}
|
|
159
337
|
}
|
|
160
338
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
161
|
-
async *enrichStream(source) {
|
|
339
|
+
async *enrichStream(source, options) {
|
|
340
|
+
const model = options.model;
|
|
162
341
|
for await (const event of source) {
|
|
163
342
|
if (event.type === "usage_update") {
|
|
164
343
|
const usage = {
|
|
165
344
|
promptTokens: event.promptTokens,
|
|
166
345
|
completionTokens: event.completionTokens,
|
|
167
|
-
model
|
|
346
|
+
model,
|
|
168
347
|
backend: this.backendName
|
|
169
348
|
};
|
|
170
349
|
this.callOnUsage(usage);
|
|
@@ -234,6 +413,35 @@ var BaseAgent = class {
|
|
|
234
413
|
heartbeatResolve = null;
|
|
235
414
|
}
|
|
236
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
|
+
}
|
|
237
445
|
// ─── Guards ───────────────────────────────────────────────────
|
|
238
446
|
guardReentrancy() {
|
|
239
447
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -252,16 +460,24 @@ var BaseAgent = class {
|
|
|
252
460
|
}
|
|
253
461
|
}
|
|
254
462
|
// ─── Internal Helpers ─────────────────────────────────────────
|
|
463
|
+
/** Clean up after a run completes (success, error, or abort). */
|
|
464
|
+
cleanupRun() {
|
|
465
|
+
this._cleanupExternalSignal?.();
|
|
466
|
+
this._cleanupExternalSignal = null;
|
|
467
|
+
this.state = "idle";
|
|
468
|
+
this.abortController = null;
|
|
469
|
+
}
|
|
255
470
|
createAbortController(externalSignal) {
|
|
256
471
|
const ac = new AbortController();
|
|
257
472
|
this.abortController = ac;
|
|
473
|
+
this._cleanupExternalSignal = null;
|
|
258
474
|
if (externalSignal) {
|
|
259
475
|
if (externalSignal.aborted) {
|
|
260
476
|
ac.abort();
|
|
261
477
|
} else {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
478
|
+
const listener = () => ac.abort();
|
|
479
|
+
externalSignal.addEventListener("abort", listener, { once: true });
|
|
480
|
+
this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
|
|
265
481
|
}
|
|
266
482
|
}
|
|
267
483
|
return ac;
|
|
@@ -324,6 +540,61 @@ function extractSchemaFromDef(schema) {
|
|
|
324
540
|
}
|
|
325
541
|
}
|
|
326
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
|
+
|
|
327
598
|
// src/backends/claude.ts
|
|
328
599
|
var MCP_SERVER_NAME = "agent-sdk-tools";
|
|
329
600
|
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
@@ -333,12 +604,12 @@ function mcpToolName(toolName) {
|
|
|
333
604
|
function stripMcpPrefix(name) {
|
|
334
605
|
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
335
606
|
}
|
|
336
|
-
var
|
|
607
|
+
var CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
608
|
+
var _sdkMock = null;
|
|
337
609
|
async function loadSDK() {
|
|
338
|
-
if (
|
|
610
|
+
if (_sdkMock) return _sdkMock;
|
|
339
611
|
try {
|
|
340
|
-
|
|
341
|
-
return sdkModule;
|
|
612
|
+
return await import('@anthropic-ai/claude-agent-sdk');
|
|
342
613
|
} catch {
|
|
343
614
|
throw new SubprocessError(
|
|
344
615
|
"@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
|
|
@@ -346,16 +617,35 @@ async function loadSDK() {
|
|
|
346
617
|
}
|
|
347
618
|
}
|
|
348
619
|
function _injectSDK(mock) {
|
|
349
|
-
|
|
620
|
+
_sdkMock = mock;
|
|
350
621
|
}
|
|
351
622
|
function _resetSDK() {
|
|
352
|
-
|
|
623
|
+
_sdkMock = null;
|
|
353
624
|
}
|
|
354
625
|
var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
|
|
355
626
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
356
627
|
var ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
357
|
-
function
|
|
358
|
-
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;
|
|
359
649
|
const mcpTools = tools.map((tool) => {
|
|
360
650
|
const zodSchema = tool.parameters;
|
|
361
651
|
const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
|
|
@@ -379,6 +669,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
|
379
669
|
}
|
|
380
670
|
);
|
|
381
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
|
+
}
|
|
382
705
|
return sdk.createSdkMcpServer({
|
|
383
706
|
name: MCP_SERVER_NAME,
|
|
384
707
|
version: "1.0.0",
|
|
@@ -428,6 +751,7 @@ function buildCanUseTool(config) {
|
|
|
428
751
|
const unifiedRequest = {
|
|
429
752
|
toolName,
|
|
430
753
|
toolArgs: input,
|
|
754
|
+
toolCallId: options.toolUseID,
|
|
431
755
|
suggestedScope: extractSuggestedScope(options.suggestions),
|
|
432
756
|
rawSDKRequest: { toolName, input, ...options }
|
|
433
757
|
};
|
|
@@ -504,6 +828,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
504
828
|
if (block.type === "tool_use") {
|
|
505
829
|
const toolCallId = String(block.id ?? "");
|
|
506
830
|
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
831
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
507
832
|
if (toolCallTracker) {
|
|
508
833
|
toolCallTracker.trackStart(toolCallId, toolName);
|
|
509
834
|
}
|
|
@@ -527,6 +852,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
527
852
|
case "tool_use_summary": {
|
|
528
853
|
const summary = msg.summary;
|
|
529
854
|
const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
|
|
855
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
|
|
530
856
|
const precedingIds = msg.preceding_tool_use_ids;
|
|
531
857
|
let toolCallId = "";
|
|
532
858
|
if (precedingIds && precedingIds.length > 0) {
|
|
@@ -580,10 +906,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
580
906
|
}
|
|
581
907
|
if (msg.is_error) {
|
|
582
908
|
const r = msg;
|
|
909
|
+
const errorMsg = r.errors?.join("; ") ?? "Unknown error";
|
|
910
|
+
const code = classifyAgentError(errorMsg);
|
|
583
911
|
return {
|
|
584
912
|
type: "error",
|
|
585
|
-
error:
|
|
586
|
-
recoverable:
|
|
913
|
+
error: errorMsg,
|
|
914
|
+
recoverable: isRecoverableErrorCode(code),
|
|
915
|
+
code
|
|
587
916
|
};
|
|
588
917
|
}
|
|
589
918
|
return null;
|
|
@@ -603,13 +932,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
603
932
|
constructor(config, options) {
|
|
604
933
|
super(config);
|
|
605
934
|
this.options = options;
|
|
606
|
-
this.tools = config.tools;
|
|
935
|
+
this.tools = config.tools ?? [];
|
|
607
936
|
this.canUseTool = buildCanUseTool(config);
|
|
608
937
|
this.isPersistent = config.sessionMode === "persistent";
|
|
609
|
-
if (
|
|
610
|
-
|
|
611
|
-
"[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
|
|
612
|
-
);
|
|
938
|
+
if (options.resumeSessionId) {
|
|
939
|
+
this._sessionId = options.resumeSessionId;
|
|
613
940
|
}
|
|
614
941
|
}
|
|
615
942
|
get sessionId() {
|
|
@@ -634,12 +961,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
634
961
|
const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
|
|
635
962
|
return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
|
|
636
963
|
}
|
|
637
|
-
buildQueryOptions(signal) {
|
|
964
|
+
buildQueryOptions(signal, options) {
|
|
638
965
|
const ac = new AbortController();
|
|
639
966
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
640
967
|
const opts = {
|
|
641
968
|
abortController: ac,
|
|
642
|
-
model:
|
|
969
|
+
model: options.model,
|
|
643
970
|
maxTurns: this.options.maxTurns,
|
|
644
971
|
cwd: this.options.workingDirectory,
|
|
645
972
|
pathToClaudeCodeExecutable: this.options.cliPath,
|
|
@@ -664,30 +991,90 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
664
991
|
opts.permissionMode = "default";
|
|
665
992
|
}
|
|
666
993
|
if (this.config.availableTools) {
|
|
667
|
-
opts.
|
|
994
|
+
opts.tools = [...this.config.availableTools];
|
|
668
995
|
}
|
|
669
996
|
return opts;
|
|
670
997
|
}
|
|
671
998
|
async buildMcpConfig(opts, toolResultCapture) {
|
|
672
|
-
|
|
999
|
+
const onAskUser = this.config.supervisor?.onAskUser;
|
|
1000
|
+
if (this.tools.length === 0 && !onAskUser) return opts;
|
|
673
1001
|
const sdk = await loadSDK();
|
|
674
|
-
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
|
|
1002
|
+
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
|
|
675
1003
|
if (mcpServer) {
|
|
676
1004
|
opts.mcpServers = {
|
|
677
1005
|
[MCP_SERVER_NAME]: mcpServer
|
|
678
1006
|
};
|
|
679
1007
|
const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
|
|
1008
|
+
if (onAskUser) {
|
|
1009
|
+
mcpToolNames.push(mcpToolName("ask_user"));
|
|
1010
|
+
}
|
|
680
1011
|
opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
|
|
681
1012
|
}
|
|
1013
|
+
if (onAskUser) {
|
|
1014
|
+
opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
|
|
1015
|
+
}
|
|
682
1016
|
return opts;
|
|
683
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
|
+
}
|
|
684
1071
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
685
|
-
async executeRun(messages,
|
|
1072
|
+
async executeRun(messages, options, signal) {
|
|
686
1073
|
this.checkAbort(signal);
|
|
687
1074
|
const sdk = await loadSDK();
|
|
688
1075
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
689
1076
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
690
|
-
let opts = this.buildQueryOptions(signal);
|
|
1077
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
691
1078
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
692
1079
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
693
1080
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -697,30 +1084,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
697
1084
|
let usage;
|
|
698
1085
|
try {
|
|
699
1086
|
for await (const msg of q) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (betaMessage?.content) {
|
|
703
|
-
for (const block of betaMessage.content) {
|
|
704
|
-
if (block.type === "tool_use") {
|
|
705
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
706
|
-
toolCalls.push({
|
|
707
|
-
toolName,
|
|
708
|
-
args: block.input ?? {},
|
|
709
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
710
|
-
approved: true
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
717
|
-
for (const tc of toolCalls) {
|
|
718
|
-
if (tc.result === null) {
|
|
719
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
720
|
-
if (captured !== void 0) tc.result = captured;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
1087
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1088
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
724
1089
|
if (msg.type === "result") {
|
|
725
1090
|
if (msg.subtype === "success") {
|
|
726
1091
|
const r = msg;
|
|
@@ -738,8 +1103,44 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
738
1103
|
}
|
|
739
1104
|
}
|
|
740
1105
|
} catch (e) {
|
|
741
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
742
1106
|
if (signal.aborted) throw new AbortError();
|
|
1107
|
+
if (isResuming && this.isPersistent) {
|
|
1108
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1109
|
+
toolCalls.length = 0;
|
|
1110
|
+
output = null;
|
|
1111
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1112
|
+
for await (const msg of retryQ) {
|
|
1113
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1114
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1115
|
+
if (msg.type === "result") {
|
|
1116
|
+
if (msg.subtype === "success") {
|
|
1117
|
+
const r = msg;
|
|
1118
|
+
output = r.result;
|
|
1119
|
+
usage = aggregateUsage(r.modelUsage);
|
|
1120
|
+
if (this.isPersistent && r.session_id) {
|
|
1121
|
+
this._sessionId = r.session_id;
|
|
1122
|
+
}
|
|
1123
|
+
} else if (msg.is_error) {
|
|
1124
|
+
const r = msg;
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
`Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
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
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
743
1144
|
throw e;
|
|
744
1145
|
} finally {
|
|
745
1146
|
this.activeQuery = null;
|
|
@@ -756,12 +1157,12 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
756
1157
|
};
|
|
757
1158
|
}
|
|
758
1159
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
759
|
-
async executeRunStructured(messages, schema,
|
|
1160
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
760
1161
|
this.checkAbort(signal);
|
|
761
1162
|
const sdk = await loadSDK();
|
|
762
1163
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
763
1164
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
764
|
-
let opts = this.buildQueryOptions(signal);
|
|
1165
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
765
1166
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
766
1167
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
767
1168
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
@@ -777,30 +1178,8 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
777
1178
|
let usage;
|
|
778
1179
|
try {
|
|
779
1180
|
for await (const msg of q) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (betaMessage?.content) {
|
|
783
|
-
for (const block of betaMessage.content) {
|
|
784
|
-
if (block.type === "tool_use") {
|
|
785
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
786
|
-
toolCalls.push({
|
|
787
|
-
toolName,
|
|
788
|
-
args: block.input ?? {},
|
|
789
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
790
|
-
approved: true
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
797
|
-
for (const tc of toolCalls) {
|
|
798
|
-
if (tc.result === null) {
|
|
799
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
800
|
-
if (captured !== void 0) tc.result = captured;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
1181
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1182
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
804
1183
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
805
1184
|
const r = msg;
|
|
806
1185
|
output = r.result;
|
|
@@ -833,8 +1212,69 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
833
1212
|
}
|
|
834
1213
|
}
|
|
835
1214
|
} catch (e) {
|
|
836
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
837
1215
|
if (signal.aborted) throw new AbortError();
|
|
1216
|
+
if (isResuming && this.isPersistent) {
|
|
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
|
+
);
|
|
1227
|
+
toolCalls.length = 0;
|
|
1228
|
+
output = null;
|
|
1229
|
+
structuredOutput = void 0;
|
|
1230
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1231
|
+
for await (const msg of retryQ) {
|
|
1232
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1233
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1234
|
+
if (msg.type === "result" && msg.subtype === "success") {
|
|
1235
|
+
const r = msg;
|
|
1236
|
+
output = r.result;
|
|
1237
|
+
if (r.structured_output !== void 0) {
|
|
1238
|
+
try {
|
|
1239
|
+
structuredOutput = schema.schema.parse(r.structured_output);
|
|
1240
|
+
} catch {
|
|
1241
|
+
try {
|
|
1242
|
+
structuredOutput = schema.schema.parse(JSON.parse(r.result));
|
|
1243
|
+
} catch {
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
} else if (r.result) {
|
|
1247
|
+
try {
|
|
1248
|
+
const jsonMatch = r.result.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1249
|
+
const raw = jsonMatch ? jsonMatch[1].trim() : r.result.trim();
|
|
1250
|
+
structuredOutput = schema.schema.parse(JSON.parse(raw));
|
|
1251
|
+
} catch {
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
usage = aggregateUsage(r.modelUsage);
|
|
1255
|
+
if (this.isPersistent && r.session_id) {
|
|
1256
|
+
this._sessionId = r.session_id;
|
|
1257
|
+
}
|
|
1258
|
+
} else if (msg.type === "result" && msg.is_error) {
|
|
1259
|
+
const r = msg;
|
|
1260
|
+
throw new Error(
|
|
1261
|
+
`Claude query failed: ${r.errors?.join("; ") ?? "unknown error"}`
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return {
|
|
1266
|
+
output,
|
|
1267
|
+
structuredOutput,
|
|
1268
|
+
toolCalls,
|
|
1269
|
+
messages: [
|
|
1270
|
+
...messages,
|
|
1271
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1272
|
+
],
|
|
1273
|
+
usage
|
|
1274
|
+
};
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
838
1278
|
throw e;
|
|
839
1279
|
} finally {
|
|
840
1280
|
this.activeQuery = null;
|
|
@@ -851,39 +1291,134 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
851
1291
|
};
|
|
852
1292
|
}
|
|
853
1293
|
// ─── executeStream ──────────────────────────────────────────────
|
|
854
|
-
async *executeStream(messages,
|
|
1294
|
+
async *executeStream(messages, options, signal) {
|
|
855
1295
|
this.checkAbort(signal);
|
|
856
1296
|
const sdk = await loadSDK();
|
|
857
1297
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
858
1298
|
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
859
|
-
let opts = this.buildQueryOptions(signal);
|
|
860
|
-
|
|
1299
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1300
|
+
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1301
|
+
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
861
1302
|
const q = sdk.query({ prompt, options: opts });
|
|
862
1303
|
this.activeQuery = q;
|
|
863
1304
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
864
1305
|
const toolCallTracker = new ClaudeToolCallTracker();
|
|
1306
|
+
const pendingStreamToolCalls = /* @__PURE__ */ new Map();
|
|
1307
|
+
let hasStreamedText = false;
|
|
865
1308
|
try {
|
|
866
1309
|
for await (const msg of q) {
|
|
867
1310
|
if (signal.aborted) throw new AbortError();
|
|
868
1311
|
const events = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
|
|
869
1312
|
if (events) {
|
|
870
1313
|
const mapped = Array.isArray(events) ? events : [events];
|
|
871
|
-
for (const e of mapped)
|
|
1314
|
+
for (const e of mapped) {
|
|
1315
|
+
if (e.type === "tool_call_start") {
|
|
1316
|
+
pendingStreamToolCalls.set(e.toolCallId, e.toolName);
|
|
1317
|
+
}
|
|
1318
|
+
if (e.type === "tool_call_end" && toolResultCapture.has(e.toolName)) {
|
|
1319
|
+
e.result = toolResultCapture.get(e.toolName);
|
|
1320
|
+
toolResultCapture.delete(e.toolName);
|
|
1321
|
+
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1322
|
+
} else if (e.type === "tool_call_end") {
|
|
1323
|
+
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1324
|
+
}
|
|
1325
|
+
if (e.type === "text_delta") hasStreamedText = true;
|
|
1326
|
+
yield e;
|
|
1327
|
+
}
|
|
872
1328
|
}
|
|
873
1329
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
874
1330
|
const r = msg;
|
|
1331
|
+
for (const [toolCallId, toolName] of pendingStreamToolCalls) {
|
|
1332
|
+
if (toolResultCapture.has(toolName)) {
|
|
1333
|
+
yield {
|
|
1334
|
+
type: "tool_call_end",
|
|
1335
|
+
toolCallId,
|
|
1336
|
+
toolName,
|
|
1337
|
+
result: toolResultCapture.get(toolName)
|
|
1338
|
+
};
|
|
1339
|
+
toolResultCapture.delete(toolName);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
pendingStreamToolCalls.clear();
|
|
875
1343
|
if (r.session_id) {
|
|
876
1344
|
if (this.isPersistent) {
|
|
877
1345
|
this._sessionId = r.session_id;
|
|
878
1346
|
}
|
|
879
1347
|
yield this.emitSessionInfo(r.session_id);
|
|
880
1348
|
}
|
|
881
|
-
yield {
|
|
1349
|
+
yield {
|
|
1350
|
+
type: "done",
|
|
1351
|
+
finalOutput: hasStreamedText ? null : r.result,
|
|
1352
|
+
...hasStreamedText ? { streamed: true } : {}
|
|
1353
|
+
};
|
|
882
1354
|
}
|
|
883
1355
|
}
|
|
884
1356
|
} catch (e) {
|
|
885
|
-
if (this.isPersistent) this.clearPersistentSession();
|
|
886
1357
|
if (signal.aborted) throw new AbortError();
|
|
1358
|
+
if (isResuming && this.isPersistent) {
|
|
1359
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1360
|
+
const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1361
|
+
const retryToolCallTracker = new ClaudeToolCallTracker();
|
|
1362
|
+
const retryPendingToolCalls = /* @__PURE__ */ new Map();
|
|
1363
|
+
let retryHasStreamedText = false;
|
|
1364
|
+
try {
|
|
1365
|
+
for await (const msg of retryQ) {
|
|
1366
|
+
if (signal.aborted) throw new AbortError();
|
|
1367
|
+
const retryEvents = mapSDKMessage(msg, retryThinkingBlockIndices, retryToolCallTracker);
|
|
1368
|
+
if (retryEvents) {
|
|
1369
|
+
const mapped = Array.isArray(retryEvents) ? retryEvents : [retryEvents];
|
|
1370
|
+
for (const ev of mapped) {
|
|
1371
|
+
if (ev.type === "tool_call_start") {
|
|
1372
|
+
retryPendingToolCalls.set(ev.toolCallId, ev.toolName);
|
|
1373
|
+
}
|
|
1374
|
+
if (ev.type === "tool_call_end" && toolResultCapture.has(ev.toolName)) {
|
|
1375
|
+
ev.result = toolResultCapture.get(ev.toolName);
|
|
1376
|
+
toolResultCapture.delete(ev.toolName);
|
|
1377
|
+
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1378
|
+
} else if (ev.type === "tool_call_end") {
|
|
1379
|
+
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1380
|
+
}
|
|
1381
|
+
if (ev.type === "text_delta") retryHasStreamedText = true;
|
|
1382
|
+
yield ev;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
if (msg.type === "result" && msg.subtype === "success") {
|
|
1386
|
+
const r = msg;
|
|
1387
|
+
for (const [toolCallId, toolName] of retryPendingToolCalls) {
|
|
1388
|
+
if (toolResultCapture.has(toolName)) {
|
|
1389
|
+
yield {
|
|
1390
|
+
type: "tool_call_end",
|
|
1391
|
+
toolCallId,
|
|
1392
|
+
toolName,
|
|
1393
|
+
result: toolResultCapture.get(toolName)
|
|
1394
|
+
};
|
|
1395
|
+
toolResultCapture.delete(toolName);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
retryPendingToolCalls.clear();
|
|
1399
|
+
if (r.session_id) {
|
|
1400
|
+
if (this.isPersistent) {
|
|
1401
|
+
this._sessionId = r.session_id;
|
|
1402
|
+
}
|
|
1403
|
+
yield this.emitSessionInfo(r.session_id);
|
|
1404
|
+
}
|
|
1405
|
+
yield {
|
|
1406
|
+
type: "done",
|
|
1407
|
+
finalOutput: retryHasStreamedText ? null : r.result,
|
|
1408
|
+
...retryHasStreamedText ? { streamed: true } : {}
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
} catch (retryError) {
|
|
1413
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
1414
|
+
if (signal.aborted) throw new AbortError();
|
|
1415
|
+
throw retryError;
|
|
1416
|
+
} finally {
|
|
1417
|
+
this.activeQuery = null;
|
|
1418
|
+
}
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
887
1422
|
throw e;
|
|
888
1423
|
} finally {
|
|
889
1424
|
this.activeQuery = null;
|
|
@@ -894,29 +1429,6 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
894
1429
|
super.dispose();
|
|
895
1430
|
}
|
|
896
1431
|
};
|
|
897
|
-
function extractLastUserPrompt(messages) {
|
|
898
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
899
|
-
const msg = messages[i];
|
|
900
|
-
if (msg.role === "user") {
|
|
901
|
-
return getTextContent(msg.content);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
return "";
|
|
905
|
-
}
|
|
906
|
-
function buildContextualPrompt(messages) {
|
|
907
|
-
if (messages.length <= 1) {
|
|
908
|
-
return extractLastUserPrompt(messages);
|
|
909
|
-
}
|
|
910
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
911
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
912
|
-
return msg.role === "user" ? `User: ${text}` : `Assistant: ${text}`;
|
|
913
|
-
}).join("\n");
|
|
914
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
915
|
-
return `Conversation history:
|
|
916
|
-
${history}
|
|
917
|
-
|
|
918
|
-
User: ${lastPrompt}`;
|
|
919
|
-
}
|
|
920
1432
|
var ClaudeAgentService = class {
|
|
921
1433
|
name = "claude";
|
|
922
1434
|
disposed = false;
|
|
@@ -956,7 +1468,8 @@ var ClaudeAgentService = class {
|
|
|
956
1468
|
this.cachedModels = body.data.map((m) => ({
|
|
957
1469
|
id: m.id,
|
|
958
1470
|
name: m.display_name,
|
|
959
|
-
provider: "claude"
|
|
1471
|
+
provider: "claude",
|
|
1472
|
+
...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
|
|
960
1473
|
}));
|
|
961
1474
|
return this.cachedModels;
|
|
962
1475
|
}
|