@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
|
@@ -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,38 +66,53 @@ 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 DependencyError = class extends AgentSDKError {
|
|
33
84
|
packageName;
|
|
34
85
|
constructor(packageName) {
|
|
35
|
-
super(`${packageName} is not installed. Install it: npm install ${packageName}
|
|
86
|
+
super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
|
|
87
|
+
code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
|
|
88
|
+
});
|
|
36
89
|
this.name = "DependencyError";
|
|
37
90
|
this.packageName = packageName;
|
|
38
91
|
}
|
|
39
92
|
};
|
|
40
93
|
var AbortError = class extends AgentSDKError {
|
|
41
94
|
constructor() {
|
|
42
|
-
super("Agent run was aborted.");
|
|
95
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
43
96
|
this.name = "AbortError";
|
|
44
97
|
}
|
|
45
98
|
};
|
|
46
99
|
var ToolExecutionError = class extends AgentSDKError {
|
|
47
100
|
toolName;
|
|
48
101
|
constructor(toolName, message, options) {
|
|
49
|
-
super(`Tool "${toolName}" failed: ${message}`, options);
|
|
102
|
+
super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
|
|
50
103
|
this.name = "ToolExecutionError";
|
|
51
104
|
this.toolName = toolName;
|
|
52
105
|
}
|
|
53
106
|
};
|
|
107
|
+
var ActivityTimeoutError = class extends AgentSDKError {
|
|
108
|
+
constructor(timeoutMs) {
|
|
109
|
+
super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
|
|
110
|
+
code: "TIMEOUT" /* TIMEOUT */,
|
|
111
|
+
retryable: true
|
|
112
|
+
});
|
|
113
|
+
this.name = "ActivityTimeoutError";
|
|
114
|
+
}
|
|
115
|
+
};
|
|
54
116
|
|
|
55
117
|
// src/base-agent.ts
|
|
56
118
|
var BaseAgent = class {
|
|
@@ -58,6 +120,7 @@ var BaseAgent = class {
|
|
|
58
120
|
abortController = null;
|
|
59
121
|
config;
|
|
60
122
|
_cleanupExternalSignal = null;
|
|
123
|
+
_streamMiddleware = [];
|
|
61
124
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
62
125
|
get sessionId() {
|
|
63
126
|
return void 0;
|
|
@@ -73,8 +136,11 @@ var BaseAgent = class {
|
|
|
73
136
|
this.state = "running";
|
|
74
137
|
try {
|
|
75
138
|
const messages = [{ role: "user", content: prompt }];
|
|
76
|
-
const result = await this.
|
|
77
|
-
|
|
139
|
+
const result = await this.withRetry(
|
|
140
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
141
|
+
options
|
|
142
|
+
);
|
|
143
|
+
this.enrichAndNotifyUsage(result, options);
|
|
78
144
|
return result;
|
|
79
145
|
} finally {
|
|
80
146
|
this.cleanupRun();
|
|
@@ -86,8 +152,11 @@ var BaseAgent = class {
|
|
|
86
152
|
const ac = this.createAbortController(options?.signal);
|
|
87
153
|
this.state = "running";
|
|
88
154
|
try {
|
|
89
|
-
const result = await this.
|
|
90
|
-
|
|
155
|
+
const result = await this.withRetry(
|
|
156
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
157
|
+
options
|
|
158
|
+
);
|
|
159
|
+
this.enrichAndNotifyUsage(result, options);
|
|
91
160
|
return result;
|
|
92
161
|
} finally {
|
|
93
162
|
this.cleanupRun();
|
|
@@ -100,13 +169,11 @@ var BaseAgent = class {
|
|
|
100
169
|
this.state = "running";
|
|
101
170
|
try {
|
|
102
171
|
const messages = [{ role: "user", content: prompt }];
|
|
103
|
-
const result = await this.
|
|
104
|
-
messages,
|
|
105
|
-
|
|
106
|
-
options,
|
|
107
|
-
ac.signal
|
|
172
|
+
const result = await this.withRetry(
|
|
173
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
174
|
+
options
|
|
108
175
|
);
|
|
109
|
-
this.enrichAndNotifyUsage(result);
|
|
176
|
+
this.enrichAndNotifyUsage(result, options);
|
|
110
177
|
return result;
|
|
111
178
|
} finally {
|
|
112
179
|
this.cleanupRun();
|
|
@@ -119,8 +186,10 @@ var BaseAgent = class {
|
|
|
119
186
|
this.state = "streaming";
|
|
120
187
|
try {
|
|
121
188
|
const messages = [{ role: "user", content: prompt }];
|
|
122
|
-
|
|
123
|
-
|
|
189
|
+
yield* this.streamWithRetry(
|
|
190
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
191
|
+
options
|
|
192
|
+
);
|
|
124
193
|
} finally {
|
|
125
194
|
this.cleanupRun();
|
|
126
195
|
}
|
|
@@ -131,12 +200,37 @@ var BaseAgent = class {
|
|
|
131
200
|
const ac = this.createAbortController(options?.signal);
|
|
132
201
|
this.state = "streaming";
|
|
133
202
|
try {
|
|
134
|
-
|
|
135
|
-
|
|
203
|
+
yield* this.streamWithRetry(
|
|
204
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
205
|
+
options
|
|
206
|
+
);
|
|
136
207
|
} finally {
|
|
137
208
|
this.cleanupRun();
|
|
138
209
|
}
|
|
139
210
|
}
|
|
211
|
+
/** Register a stream middleware. Applied in registration order after built-in transforms. */
|
|
212
|
+
addStreamMiddleware(middleware) {
|
|
213
|
+
this.guardDisposed();
|
|
214
|
+
this._streamMiddleware.push(middleware);
|
|
215
|
+
}
|
|
216
|
+
/** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
|
|
217
|
+
async *applyStreamPipeline(source, options, ac) {
|
|
218
|
+
let stream = this.enrichStream(source, options);
|
|
219
|
+
stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
|
|
220
|
+
stream = this.heartbeatStream(stream);
|
|
221
|
+
if (this._streamMiddleware.length > 0) {
|
|
222
|
+
const ctx = {
|
|
223
|
+
model: options.model,
|
|
224
|
+
backend: this.backendName,
|
|
225
|
+
abortController: ac,
|
|
226
|
+
config: Object.freeze({ ...this.config })
|
|
227
|
+
};
|
|
228
|
+
for (const mw of this._streamMiddleware) {
|
|
229
|
+
stream = mw(stream, ctx);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
yield* stream;
|
|
233
|
+
}
|
|
140
234
|
abort() {
|
|
141
235
|
if (this.abortController) {
|
|
142
236
|
this.abortController.abort();
|
|
@@ -159,26 +253,109 @@ var BaseAgent = class {
|
|
|
159
253
|
this.abort();
|
|
160
254
|
this.state = "disposed";
|
|
161
255
|
}
|
|
256
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
257
|
+
/** Check if an error should be retried given the retry configuration. */
|
|
258
|
+
isRetryableError(error, retry) {
|
|
259
|
+
if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
if (AgentSDKError.is(error)) {
|
|
263
|
+
if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
|
|
264
|
+
return retry.retryableErrors.includes(error.code);
|
|
265
|
+
}
|
|
266
|
+
if (error.retryable) return true;
|
|
267
|
+
if (error.code) return isRecoverableErrorCode(error.code);
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
/** Execute a function with retry logic per RetryConfig. */
|
|
272
|
+
async withRetry(fn, options) {
|
|
273
|
+
const retry = options?.retry;
|
|
274
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
275
|
+
return fn();
|
|
276
|
+
}
|
|
277
|
+
const maxRetries = retry.maxRetries;
|
|
278
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
279
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
280
|
+
let lastError;
|
|
281
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
282
|
+
try {
|
|
283
|
+
return await fn();
|
|
284
|
+
} catch (err) {
|
|
285
|
+
lastError = err;
|
|
286
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
290
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
291
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
throw lastError;
|
|
297
|
+
}
|
|
298
|
+
/** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
|
|
299
|
+
async *streamWithRetry(factory, options) {
|
|
300
|
+
const retry = options?.retry;
|
|
301
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
302
|
+
yield* factory();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const maxRetries = retry.maxRetries;
|
|
306
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
307
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
308
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
309
|
+
try {
|
|
310
|
+
const stream = factory();
|
|
311
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
312
|
+
const first = await iterator.next();
|
|
313
|
+
if (first.done) return;
|
|
314
|
+
yield first.value;
|
|
315
|
+
while (true) {
|
|
316
|
+
const next = await iterator.next();
|
|
317
|
+
if (next.done) break;
|
|
318
|
+
yield next.value;
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
} catch (err) {
|
|
322
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
323
|
+
throw err;
|
|
324
|
+
}
|
|
325
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
326
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
327
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// ─── CallOptions Resolution ──────────────────────────────────
|
|
334
|
+
/** Resolve tools to use for this call (per-call override > config default) */
|
|
335
|
+
resolveTools(options) {
|
|
336
|
+
return options?.tools ?? this.config.tools ?? [];
|
|
337
|
+
}
|
|
162
338
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
163
339
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
164
|
-
enrichAndNotifyUsage(result) {
|
|
340
|
+
enrichAndNotifyUsage(result, options) {
|
|
165
341
|
if (result.usage) {
|
|
166
342
|
result.usage = {
|
|
167
343
|
...result.usage,
|
|
168
|
-
model:
|
|
344
|
+
model: options.model,
|
|
169
345
|
backend: this.backendName
|
|
170
346
|
};
|
|
171
347
|
this.callOnUsage(result.usage);
|
|
172
348
|
}
|
|
173
349
|
}
|
|
174
350
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
175
|
-
async *enrichStream(source) {
|
|
351
|
+
async *enrichStream(source, options) {
|
|
352
|
+
const model = options.model;
|
|
176
353
|
for await (const event of source) {
|
|
177
354
|
if (event.type === "usage_update") {
|
|
178
355
|
const usage = {
|
|
179
356
|
promptTokens: event.promptTokens,
|
|
180
357
|
completionTokens: event.completionTokens,
|
|
181
|
-
model
|
|
358
|
+
model,
|
|
182
359
|
backend: this.backendName
|
|
183
360
|
};
|
|
184
361
|
this.callOnUsage(usage);
|
|
@@ -248,6 +425,35 @@ var BaseAgent = class {
|
|
|
248
425
|
heartbeatResolve = null;
|
|
249
426
|
}
|
|
250
427
|
}
|
|
428
|
+
// ─── Activity Timeout ────────────────────────────────────────
|
|
429
|
+
/** Wrap a stream to abort on inactivity. Resets timer on every event.
|
|
430
|
+
* When timeoutMs is not set, passes through directly. */
|
|
431
|
+
async *activityTimeoutStream(source, timeoutMs, ac) {
|
|
432
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
433
|
+
yield* source;
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
437
|
+
let timerId;
|
|
438
|
+
try {
|
|
439
|
+
while (true) {
|
|
440
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
441
|
+
timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
|
|
442
|
+
});
|
|
443
|
+
const result = await Promise.race([iterator.next(), timeoutPromise]);
|
|
444
|
+
clearTimeout(timerId);
|
|
445
|
+
if (result.done) break;
|
|
446
|
+
yield result.value;
|
|
447
|
+
}
|
|
448
|
+
} catch (err) {
|
|
449
|
+
if (err instanceof ActivityTimeoutError) {
|
|
450
|
+
ac.abort(err);
|
|
451
|
+
}
|
|
452
|
+
throw err;
|
|
453
|
+
} finally {
|
|
454
|
+
clearTimeout(timerId);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
251
457
|
// ─── Guards ───────────────────────────────────────────────────
|
|
252
458
|
guardReentrancy() {
|
|
253
459
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -347,35 +553,33 @@ function extractSchemaFromDef(schema) {
|
|
|
347
553
|
}
|
|
348
554
|
|
|
349
555
|
// src/backends/vercel-ai.ts
|
|
350
|
-
var
|
|
351
|
-
var
|
|
556
|
+
var _sdkMock = null;
|
|
557
|
+
var _compatMock = null;
|
|
352
558
|
async function loadSDK() {
|
|
353
|
-
if (
|
|
559
|
+
if (_sdkMock) return _sdkMock;
|
|
354
560
|
try {
|
|
355
|
-
|
|
356
|
-
return sdkModule;
|
|
561
|
+
return await import('ai');
|
|
357
562
|
} catch {
|
|
358
563
|
throw new DependencyError("ai");
|
|
359
564
|
}
|
|
360
565
|
}
|
|
361
566
|
async function loadCompat() {
|
|
362
|
-
if (
|
|
567
|
+
if (_compatMock) return _compatMock;
|
|
363
568
|
try {
|
|
364
|
-
|
|
365
|
-
return compatModule;
|
|
569
|
+
return await import('@ai-sdk/openai-compatible');
|
|
366
570
|
} catch {
|
|
367
571
|
throw new DependencyError("@ai-sdk/openai-compatible");
|
|
368
572
|
}
|
|
369
573
|
}
|
|
370
574
|
function _injectSDK(mock) {
|
|
371
|
-
|
|
575
|
+
_sdkMock = mock;
|
|
372
576
|
}
|
|
373
577
|
function _injectCompat(mock) {
|
|
374
|
-
|
|
578
|
+
_compatMock = mock;
|
|
375
579
|
}
|
|
376
580
|
function _resetSDK() {
|
|
377
|
-
|
|
378
|
-
|
|
581
|
+
_sdkMock = null;
|
|
582
|
+
_compatMock = null;
|
|
379
583
|
}
|
|
380
584
|
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
381
585
|
var DEFAULT_PROVIDER = "openrouter";
|
|
@@ -421,13 +625,14 @@ function mapToolsToSDK(sdk, tools, config, sessionApprovals, permissionStore, si
|
|
|
421
625
|
return toolMap;
|
|
422
626
|
}
|
|
423
627
|
function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
|
|
424
|
-
return async (args) => {
|
|
628
|
+
return async (args, options) => {
|
|
425
629
|
if (ourTool.needsApproval && supervisor?.onPermission) {
|
|
426
630
|
const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
|
|
427
631
|
if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
|
|
428
632
|
const request = {
|
|
429
633
|
toolName: ourTool.name,
|
|
430
|
-
toolArgs: args ?? {}
|
|
634
|
+
toolArgs: args ?? {},
|
|
635
|
+
toolCallId: options?.toolCallId
|
|
431
636
|
};
|
|
432
637
|
const decision = await supervisor.onPermission(
|
|
433
638
|
request,
|
|
@@ -534,7 +739,8 @@ function mapStreamPart(part) {
|
|
|
534
739
|
return {
|
|
535
740
|
type: "error",
|
|
536
741
|
error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
|
|
537
|
-
recoverable: true
|
|
742
|
+
recoverable: true,
|
|
743
|
+
code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
|
|
538
744
|
};
|
|
539
745
|
}
|
|
540
746
|
case "reasoning-start":
|
|
@@ -555,10 +761,13 @@ function mapStreamPart(part) {
|
|
|
555
761
|
}
|
|
556
762
|
case "error": {
|
|
557
763
|
const p = part;
|
|
764
|
+
const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
|
|
765
|
+
const code = classifyAgentError(errorMsg);
|
|
558
766
|
return {
|
|
559
767
|
type: "error",
|
|
560
|
-
error:
|
|
561
|
-
recoverable:
|
|
768
|
+
error: errorMsg,
|
|
769
|
+
recoverable: isRecoverableErrorCode(code),
|
|
770
|
+
code
|
|
562
771
|
};
|
|
563
772
|
}
|
|
564
773
|
default:
|
|
@@ -574,28 +783,33 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
574
783
|
super(config);
|
|
575
784
|
this.backendOptions = backendOptions;
|
|
576
785
|
}
|
|
577
|
-
async getModel() {
|
|
578
|
-
|
|
786
|
+
async getModel(options) {
|
|
787
|
+
const requestedModel = options.model;
|
|
788
|
+
const defaultModel = this.config.model;
|
|
789
|
+
if (requestedModel === defaultModel && this.model) return this.model;
|
|
579
790
|
const compat = await loadCompat();
|
|
580
791
|
const provider = compat.createOpenAICompatible({
|
|
581
792
|
name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
|
|
582
793
|
baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
|
|
583
794
|
apiKey: this.backendOptions.apiKey
|
|
584
795
|
});
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
796
|
+
const model = provider.chatModel(requestedModel);
|
|
797
|
+
if (requestedModel === defaultModel) {
|
|
798
|
+
this.model = model;
|
|
799
|
+
}
|
|
800
|
+
return model;
|
|
588
801
|
}
|
|
589
|
-
async getSDKTools(signal) {
|
|
802
|
+
async getSDKTools(signal, options) {
|
|
590
803
|
const sdk = await loadSDK();
|
|
591
|
-
|
|
804
|
+
const tools = this.resolveTools(options);
|
|
805
|
+
return mapToolsToSDK(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
|
|
592
806
|
}
|
|
593
807
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
594
|
-
async executeRun(messages,
|
|
808
|
+
async executeRun(messages, options, signal) {
|
|
595
809
|
this.checkAbort(signal);
|
|
596
810
|
const sdk = await loadSDK();
|
|
597
|
-
const model = await this.getModel();
|
|
598
|
-
const tools = await this.getSDKTools(signal);
|
|
811
|
+
const model = await this.getModel(options);
|
|
812
|
+
const tools = await this.getSDKTools(signal, options);
|
|
599
813
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
600
814
|
const sdkMessages = messagesToSDK(messages);
|
|
601
815
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -651,10 +865,10 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
651
865
|
};
|
|
652
866
|
}
|
|
653
867
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
654
|
-
async executeRunStructured(messages, schema,
|
|
868
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
655
869
|
this.checkAbort(signal);
|
|
656
870
|
const sdk = await loadSDK();
|
|
657
|
-
const model = await this.getModel();
|
|
871
|
+
const model = await this.getModel(options);
|
|
658
872
|
const sdkMessages = messagesToSDK(messages);
|
|
659
873
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
660
874
|
const result = await sdk.generateObject({
|
|
@@ -696,11 +910,11 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
696
910
|
};
|
|
697
911
|
}
|
|
698
912
|
// ─── executeStream ──────────────────────────────────────────────
|
|
699
|
-
async *executeStream(messages,
|
|
913
|
+
async *executeStream(messages, options, signal) {
|
|
700
914
|
this.checkAbort(signal);
|
|
701
915
|
const sdk = await loadSDK();
|
|
702
|
-
const model = await this.getModel();
|
|
703
|
-
const tools = await this.getSDKTools(signal);
|
|
916
|
+
const model = await this.getModel(options);
|
|
917
|
+
const tools = await this.getSDKTools(signal, options);
|
|
704
918
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
705
919
|
const sdkMessages = messagesToSDK(messages);
|
|
706
920
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -746,9 +960,11 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
746
960
|
promptTokens: Number(totalUsage?.inputTokens ?? 0),
|
|
747
961
|
completionTokens: Number(totalUsage?.outputTokens ?? 0)
|
|
748
962
|
};
|
|
963
|
+
const hasStreamed = finalText.length > 0;
|
|
749
964
|
yield {
|
|
750
965
|
type: "done",
|
|
751
|
-
finalOutput: finalText || null
|
|
966
|
+
finalOutput: hasStreamed ? null : finalText || null,
|
|
967
|
+
...hasStreamed ? { streamed: true } : {}
|
|
752
968
|
};
|
|
753
969
|
} catch (e) {
|
|
754
970
|
if (signal.aborted) throw new AbortError();
|
|
@@ -777,16 +993,33 @@ var VercelAIAgentService = class {
|
|
|
777
993
|
const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
778
994
|
try {
|
|
779
995
|
const res = await globalThis.fetch(`${baseUrl}/models`, {
|
|
780
|
-
headers: {
|
|
996
|
+
headers: {
|
|
997
|
+
Authorization: `Bearer ${this.options.apiKey}`,
|
|
998
|
+
// OpenRouter requires HTTP-Referer for API access
|
|
999
|
+
"HTTP-Referer": "https://github.com/nicepkg/agent-sdk"
|
|
1000
|
+
}
|
|
781
1001
|
});
|
|
782
1002
|
if (!res.ok) {
|
|
783
1003
|
return [];
|
|
784
1004
|
}
|
|
785
1005
|
const body = await res.json();
|
|
786
|
-
if (
|
|
787
|
-
return
|
|
1006
|
+
if (body.data && Array.isArray(body.data)) {
|
|
1007
|
+
return body.data.filter((m) => typeof m.id === "string").map((m) => ({
|
|
1008
|
+
id: m.id,
|
|
1009
|
+
...typeof m.name === "string" && { name: m.name },
|
|
1010
|
+
...typeof m.description === "string" && { description: m.description },
|
|
1011
|
+
...typeof m.context_length === "number" && { contextWindow: m.context_length }
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
1014
|
+
if (Array.isArray(body)) {
|
|
1015
|
+
return body.filter((m) => typeof m.id === "string").map((m) => ({
|
|
1016
|
+
id: m.id,
|
|
1017
|
+
...typeof m.name === "string" && { name: m.name },
|
|
1018
|
+
...typeof m.description === "string" && { description: m.description },
|
|
1019
|
+
...typeof m.context_length === "number" && { contextWindow: m.context_length }
|
|
1020
|
+
}));
|
|
788
1021
|
}
|
|
789
|
-
return
|
|
1022
|
+
return [];
|
|
790
1023
|
} catch {
|
|
791
1024
|
return [];
|
|
792
1025
|
}
|