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