@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
|
@@ -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");
|
|
@@ -8,51 +46,83 @@ function getTextContent(content) {
|
|
|
8
46
|
|
|
9
47
|
// src/errors.ts
|
|
10
48
|
var AgentSDKError = class extends Error {
|
|
49
|
+
/** @internal Marker for cross-bundle identity checks */
|
|
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;
|
|
11
57
|
constructor(message, options) {
|
|
12
58
|
super(message, options);
|
|
13
59
|
this.name = "AgentSDKError";
|
|
60
|
+
this.code = options?.code;
|
|
61
|
+
this.retryable = options?.retryable ?? false;
|
|
62
|
+
this.httpStatus = options?.httpStatus;
|
|
63
|
+
}
|
|
64
|
+
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
65
|
+
static is(error) {
|
|
66
|
+
return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
|
|
14
67
|
}
|
|
15
68
|
};
|
|
16
69
|
var ReentrancyError = class extends AgentSDKError {
|
|
17
70
|
constructor() {
|
|
18
|
-
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
|
+
});
|
|
19
74
|
this.name = "ReentrancyError";
|
|
20
75
|
}
|
|
21
76
|
};
|
|
22
77
|
var DisposedError = class extends AgentSDKError {
|
|
23
78
|
constructor(entity) {
|
|
24
|
-
super(`${entity} has been disposed and cannot be used
|
|
79
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
80
|
+
code: "DISPOSED" /* DISPOSED */
|
|
81
|
+
});
|
|
25
82
|
this.name = "DisposedError";
|
|
26
83
|
}
|
|
27
84
|
};
|
|
28
85
|
var DependencyError = class extends AgentSDKError {
|
|
29
86
|
packageName;
|
|
30
87
|
constructor(packageName) {
|
|
31
|
-
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
|
+
});
|
|
32
91
|
this.name = "DependencyError";
|
|
33
92
|
this.packageName = packageName;
|
|
34
93
|
}
|
|
35
94
|
};
|
|
36
95
|
var AbortError = class extends AgentSDKError {
|
|
37
96
|
constructor() {
|
|
38
|
-
super("Agent run was aborted.");
|
|
97
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
39
98
|
this.name = "AbortError";
|
|
40
99
|
}
|
|
41
100
|
};
|
|
42
101
|
var ToolExecutionError = class extends AgentSDKError {
|
|
43
102
|
toolName;
|
|
44
103
|
constructor(toolName, message, options) {
|
|
45
|
-
super(`Tool "${toolName}" failed: ${message}`, options);
|
|
104
|
+
super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
|
|
46
105
|
this.name = "ToolExecutionError";
|
|
47
106
|
this.toolName = toolName;
|
|
48
107
|
}
|
|
49
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
|
+
};
|
|
50
118
|
|
|
51
119
|
// src/base-agent.ts
|
|
52
120
|
var BaseAgent = class {
|
|
53
121
|
state = "idle";
|
|
54
122
|
abortController = null;
|
|
55
123
|
config;
|
|
124
|
+
_cleanupExternalSignal = null;
|
|
125
|
+
_streamMiddleware = [];
|
|
56
126
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
57
127
|
get sessionId() {
|
|
58
128
|
return void 0;
|
|
@@ -68,12 +138,14 @@ var BaseAgent = class {
|
|
|
68
138
|
this.state = "running";
|
|
69
139
|
try {
|
|
70
140
|
const messages = [{ role: "user", content: prompt }];
|
|
71
|
-
const result = await this.
|
|
72
|
-
|
|
141
|
+
const result = await this.withRetry(
|
|
142
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
143
|
+
options
|
|
144
|
+
);
|
|
145
|
+
this.enrichAndNotifyUsage(result, options);
|
|
73
146
|
return result;
|
|
74
147
|
} finally {
|
|
75
|
-
this.
|
|
76
|
-
this.abortController = null;
|
|
148
|
+
this.cleanupRun();
|
|
77
149
|
}
|
|
78
150
|
}
|
|
79
151
|
async runWithContext(messages, options) {
|
|
@@ -82,12 +154,14 @@ var BaseAgent = class {
|
|
|
82
154
|
const ac = this.createAbortController(options?.signal);
|
|
83
155
|
this.state = "running";
|
|
84
156
|
try {
|
|
85
|
-
const result = await this.
|
|
86
|
-
|
|
157
|
+
const result = await this.withRetry(
|
|
158
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
159
|
+
options
|
|
160
|
+
);
|
|
161
|
+
this.enrichAndNotifyUsage(result, options);
|
|
87
162
|
return result;
|
|
88
163
|
} finally {
|
|
89
|
-
this.
|
|
90
|
-
this.abortController = null;
|
|
164
|
+
this.cleanupRun();
|
|
91
165
|
}
|
|
92
166
|
}
|
|
93
167
|
async runStructured(prompt, schema, options) {
|
|
@@ -97,17 +171,14 @@ var BaseAgent = class {
|
|
|
97
171
|
this.state = "running";
|
|
98
172
|
try {
|
|
99
173
|
const messages = [{ role: "user", content: prompt }];
|
|
100
|
-
const result = await this.
|
|
101
|
-
messages,
|
|
102
|
-
|
|
103
|
-
options,
|
|
104
|
-
ac.signal
|
|
174
|
+
const result = await this.withRetry(
|
|
175
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
176
|
+
options
|
|
105
177
|
);
|
|
106
|
-
this.enrichAndNotifyUsage(result);
|
|
178
|
+
this.enrichAndNotifyUsage(result, options);
|
|
107
179
|
return result;
|
|
108
180
|
} finally {
|
|
109
|
-
this.
|
|
110
|
-
this.abortController = null;
|
|
181
|
+
this.cleanupRun();
|
|
111
182
|
}
|
|
112
183
|
}
|
|
113
184
|
async *stream(prompt, options) {
|
|
@@ -117,11 +188,12 @@ var BaseAgent = class {
|
|
|
117
188
|
this.state = "streaming";
|
|
118
189
|
try {
|
|
119
190
|
const messages = [{ role: "user", content: prompt }];
|
|
120
|
-
|
|
121
|
-
|
|
191
|
+
yield* this.streamWithRetry(
|
|
192
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
193
|
+
options
|
|
194
|
+
);
|
|
122
195
|
} finally {
|
|
123
|
-
this.
|
|
124
|
-
this.abortController = null;
|
|
196
|
+
this.cleanupRun();
|
|
125
197
|
}
|
|
126
198
|
}
|
|
127
199
|
async *streamWithContext(messages, options) {
|
|
@@ -130,12 +202,36 @@ var BaseAgent = class {
|
|
|
130
202
|
const ac = this.createAbortController(options?.signal);
|
|
131
203
|
this.state = "streaming";
|
|
132
204
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
205
|
+
yield* this.streamWithRetry(
|
|
206
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
207
|
+
options
|
|
208
|
+
);
|
|
135
209
|
} finally {
|
|
136
|
-
this.
|
|
137
|
-
|
|
210
|
+
this.cleanupRun();
|
|
211
|
+
}
|
|
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
|
+
}
|
|
138
233
|
}
|
|
234
|
+
yield* stream;
|
|
139
235
|
}
|
|
140
236
|
abort() {
|
|
141
237
|
if (this.abortController) {
|
|
@@ -154,29 +250,114 @@ var BaseAgent = class {
|
|
|
154
250
|
}
|
|
155
251
|
/** Mark agent as disposed. Override to add cleanup. */
|
|
156
252
|
dispose() {
|
|
253
|
+
this._cleanupExternalSignal?.();
|
|
254
|
+
this._cleanupExternalSignal = null;
|
|
157
255
|
this.abort();
|
|
158
256
|
this.state = "disposed";
|
|
159
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
|
+
}
|
|
160
340
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
161
341
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
162
|
-
enrichAndNotifyUsage(result) {
|
|
342
|
+
enrichAndNotifyUsage(result, options) {
|
|
163
343
|
if (result.usage) {
|
|
164
344
|
result.usage = {
|
|
165
345
|
...result.usage,
|
|
166
|
-
model:
|
|
346
|
+
model: options.model,
|
|
167
347
|
backend: this.backendName
|
|
168
348
|
};
|
|
169
349
|
this.callOnUsage(result.usage);
|
|
170
350
|
}
|
|
171
351
|
}
|
|
172
352
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
173
|
-
async *enrichStream(source) {
|
|
353
|
+
async *enrichStream(source, options) {
|
|
354
|
+
const model = options.model;
|
|
174
355
|
for await (const event of source) {
|
|
175
356
|
if (event.type === "usage_update") {
|
|
176
357
|
const usage = {
|
|
177
358
|
promptTokens: event.promptTokens,
|
|
178
359
|
completionTokens: event.completionTokens,
|
|
179
|
-
model
|
|
360
|
+
model,
|
|
180
361
|
backend: this.backendName
|
|
181
362
|
};
|
|
182
363
|
this.callOnUsage(usage);
|
|
@@ -246,6 +427,35 @@ var BaseAgent = class {
|
|
|
246
427
|
heartbeatResolve = null;
|
|
247
428
|
}
|
|
248
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
|
+
}
|
|
249
459
|
// ─── Guards ───────────────────────────────────────────────────
|
|
250
460
|
guardReentrancy() {
|
|
251
461
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -264,16 +474,24 @@ var BaseAgent = class {
|
|
|
264
474
|
}
|
|
265
475
|
}
|
|
266
476
|
// ─── Internal Helpers ─────────────────────────────────────────
|
|
477
|
+
/** Clean up after a run completes (success, error, or abort). */
|
|
478
|
+
cleanupRun() {
|
|
479
|
+
this._cleanupExternalSignal?.();
|
|
480
|
+
this._cleanupExternalSignal = null;
|
|
481
|
+
this.state = "idle";
|
|
482
|
+
this.abortController = null;
|
|
483
|
+
}
|
|
267
484
|
createAbortController(externalSignal) {
|
|
268
485
|
const ac = new AbortController();
|
|
269
486
|
this.abortController = ac;
|
|
487
|
+
this._cleanupExternalSignal = null;
|
|
270
488
|
if (externalSignal) {
|
|
271
489
|
if (externalSignal.aborted) {
|
|
272
490
|
ac.abort();
|
|
273
491
|
} else {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
492
|
+
const listener = () => ac.abort();
|
|
493
|
+
externalSignal.addEventListener("abort", listener, { once: true });
|
|
494
|
+
this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
|
|
277
495
|
}
|
|
278
496
|
}
|
|
279
497
|
return ac;
|
|
@@ -337,35 +555,33 @@ function extractSchemaFromDef(schema) {
|
|
|
337
555
|
}
|
|
338
556
|
|
|
339
557
|
// src/backends/vercel-ai.ts
|
|
340
|
-
var
|
|
341
|
-
var
|
|
558
|
+
var _sdkMock = null;
|
|
559
|
+
var _compatMock = null;
|
|
342
560
|
async function loadSDK() {
|
|
343
|
-
if (
|
|
561
|
+
if (_sdkMock) return _sdkMock;
|
|
344
562
|
try {
|
|
345
|
-
|
|
346
|
-
return sdkModule;
|
|
563
|
+
return await import('ai');
|
|
347
564
|
} catch {
|
|
348
565
|
throw new DependencyError("ai");
|
|
349
566
|
}
|
|
350
567
|
}
|
|
351
568
|
async function loadCompat() {
|
|
352
|
-
if (
|
|
569
|
+
if (_compatMock) return _compatMock;
|
|
353
570
|
try {
|
|
354
|
-
|
|
355
|
-
return compatModule;
|
|
571
|
+
return await import('@ai-sdk/openai-compatible');
|
|
356
572
|
} catch {
|
|
357
573
|
throw new DependencyError("@ai-sdk/openai-compatible");
|
|
358
574
|
}
|
|
359
575
|
}
|
|
360
576
|
function _injectSDK(mock) {
|
|
361
|
-
|
|
577
|
+
_sdkMock = mock;
|
|
362
578
|
}
|
|
363
579
|
function _injectCompat(mock) {
|
|
364
|
-
|
|
580
|
+
_compatMock = mock;
|
|
365
581
|
}
|
|
366
582
|
function _resetSDK() {
|
|
367
|
-
|
|
368
|
-
|
|
583
|
+
_sdkMock = null;
|
|
584
|
+
_compatMock = null;
|
|
369
585
|
}
|
|
370
586
|
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
371
587
|
var DEFAULT_PROVIDER = "openrouter";
|
|
@@ -411,13 +627,14 @@ function mapToolsToSDK(sdk, tools, config, sessionApprovals, permissionStore, si
|
|
|
411
627
|
return toolMap;
|
|
412
628
|
}
|
|
413
629
|
function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
|
|
414
|
-
return async (args) => {
|
|
630
|
+
return async (args, options) => {
|
|
415
631
|
if (ourTool.needsApproval && supervisor?.onPermission) {
|
|
416
632
|
const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
|
|
417
633
|
if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
|
|
418
634
|
const request = {
|
|
419
635
|
toolName: ourTool.name,
|
|
420
|
-
toolArgs: args ?? {}
|
|
636
|
+
toolArgs: args ?? {},
|
|
637
|
+
toolCallId: options?.toolCallId
|
|
421
638
|
};
|
|
422
639
|
const decision = await supervisor.onPermission(
|
|
423
640
|
request,
|
|
@@ -457,12 +674,39 @@ function messagesToSDK(messages) {
|
|
|
457
674
|
switch (msg.role) {
|
|
458
675
|
case "user":
|
|
459
676
|
return { role: "user", content: getTextContent(msg.content) };
|
|
460
|
-
case "assistant":
|
|
461
|
-
|
|
677
|
+
case "assistant": {
|
|
678
|
+
let content = getTextContent(msg.content);
|
|
679
|
+
const thinking = msg.thinking;
|
|
680
|
+
if (thinking) {
|
|
681
|
+
content = `[reasoning: ${thinking}]
|
|
682
|
+
${content}`;
|
|
683
|
+
}
|
|
684
|
+
const mapped = { role: "assistant", content };
|
|
685
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
686
|
+
mapped.toolCalls = msg.toolCalls.map((tc) => ({
|
|
687
|
+
id: tc.id,
|
|
688
|
+
name: tc.name,
|
|
689
|
+
args: tc.args
|
|
690
|
+
}));
|
|
691
|
+
}
|
|
692
|
+
return mapped;
|
|
693
|
+
}
|
|
462
694
|
case "system":
|
|
463
695
|
return { role: "system", content: msg.content };
|
|
464
|
-
case "tool":
|
|
696
|
+
case "tool": {
|
|
697
|
+
if (msg.toolResults && msg.toolResults.length > 0) {
|
|
698
|
+
return {
|
|
699
|
+
role: "tool",
|
|
700
|
+
toolResults: msg.toolResults.map((tr) => ({
|
|
701
|
+
toolCallId: tr.toolCallId,
|
|
702
|
+
name: tr.name,
|
|
703
|
+
result: tr.result,
|
|
704
|
+
isError: tr.isError ?? false
|
|
705
|
+
}))
|
|
706
|
+
};
|
|
707
|
+
}
|
|
465
708
|
return { role: "tool", content: msg.content ?? "" };
|
|
709
|
+
}
|
|
466
710
|
default:
|
|
467
711
|
return { role: "user", content: "" };
|
|
468
712
|
}
|
|
@@ -497,7 +741,8 @@ function mapStreamPart(part) {
|
|
|
497
741
|
return {
|
|
498
742
|
type: "error",
|
|
499
743
|
error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
|
|
500
|
-
recoverable: true
|
|
744
|
+
recoverable: true,
|
|
745
|
+
code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
|
|
501
746
|
};
|
|
502
747
|
}
|
|
503
748
|
case "reasoning-start":
|
|
@@ -518,10 +763,13 @@ function mapStreamPart(part) {
|
|
|
518
763
|
}
|
|
519
764
|
case "error": {
|
|
520
765
|
const p = part;
|
|
766
|
+
const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
|
|
767
|
+
const code = classifyAgentError(errorMsg);
|
|
521
768
|
return {
|
|
522
769
|
type: "error",
|
|
523
|
-
error:
|
|
524
|
-
recoverable:
|
|
770
|
+
error: errorMsg,
|
|
771
|
+
recoverable: isRecoverableErrorCode(code),
|
|
772
|
+
code
|
|
525
773
|
};
|
|
526
774
|
}
|
|
527
775
|
default:
|
|
@@ -537,28 +785,33 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
537
785
|
super(config);
|
|
538
786
|
this.backendOptions = backendOptions;
|
|
539
787
|
}
|
|
540
|
-
async getModel() {
|
|
541
|
-
|
|
788
|
+
async getModel(options) {
|
|
789
|
+
const requestedModel = options.model;
|
|
790
|
+
const defaultModel = this.config.model;
|
|
791
|
+
if (requestedModel === defaultModel && this.model) return this.model;
|
|
542
792
|
const compat = await loadCompat();
|
|
543
793
|
const provider = compat.createOpenAICompatible({
|
|
544
794
|
name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
|
|
545
795
|
baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
|
|
546
796
|
apiKey: this.backendOptions.apiKey
|
|
547
797
|
});
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
798
|
+
const model = provider.chatModel(requestedModel);
|
|
799
|
+
if (requestedModel === defaultModel) {
|
|
800
|
+
this.model = model;
|
|
801
|
+
}
|
|
802
|
+
return model;
|
|
551
803
|
}
|
|
552
|
-
async getSDKTools(signal) {
|
|
804
|
+
async getSDKTools(signal, options) {
|
|
553
805
|
const sdk = await loadSDK();
|
|
554
|
-
|
|
806
|
+
const tools = this.resolveTools(options);
|
|
807
|
+
return mapToolsToSDK(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
|
|
555
808
|
}
|
|
556
809
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
557
|
-
async executeRun(messages,
|
|
810
|
+
async executeRun(messages, options, signal) {
|
|
558
811
|
this.checkAbort(signal);
|
|
559
812
|
const sdk = await loadSDK();
|
|
560
|
-
const model = await this.getModel();
|
|
561
|
-
const tools = await this.getSDKTools(signal);
|
|
813
|
+
const model = await this.getModel(options);
|
|
814
|
+
const tools = await this.getSDKTools(signal, options);
|
|
562
815
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
563
816
|
const sdkMessages = messagesToSDK(messages);
|
|
564
817
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -614,10 +867,10 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
614
867
|
};
|
|
615
868
|
}
|
|
616
869
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
617
|
-
async executeRunStructured(messages, schema,
|
|
870
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
618
871
|
this.checkAbort(signal);
|
|
619
872
|
const sdk = await loadSDK();
|
|
620
|
-
const model = await this.getModel();
|
|
873
|
+
const model = await this.getModel(options);
|
|
621
874
|
const sdkMessages = messagesToSDK(messages);
|
|
622
875
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
623
876
|
const result = await sdk.generateObject({
|
|
@@ -659,11 +912,11 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
659
912
|
};
|
|
660
913
|
}
|
|
661
914
|
// ─── executeStream ──────────────────────────────────────────────
|
|
662
|
-
async *executeStream(messages,
|
|
915
|
+
async *executeStream(messages, options, signal) {
|
|
663
916
|
this.checkAbort(signal);
|
|
664
917
|
const sdk = await loadSDK();
|
|
665
|
-
const model = await this.getModel();
|
|
666
|
-
const tools = await this.getSDKTools(signal);
|
|
918
|
+
const model = await this.getModel(options);
|
|
919
|
+
const tools = await this.getSDKTools(signal, options);
|
|
667
920
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
668
921
|
const sdkMessages = messagesToSDK(messages);
|
|
669
922
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -709,9 +962,11 @@ var VercelAIAgent = class extends BaseAgent {
|
|
|
709
962
|
promptTokens: Number(totalUsage?.inputTokens ?? 0),
|
|
710
963
|
completionTokens: Number(totalUsage?.outputTokens ?? 0)
|
|
711
964
|
};
|
|
965
|
+
const hasStreamed = finalText.length > 0;
|
|
712
966
|
yield {
|
|
713
967
|
type: "done",
|
|
714
|
-
finalOutput: finalText || null
|
|
968
|
+
finalOutput: hasStreamed ? null : finalText || null,
|
|
969
|
+
...hasStreamed ? { streamed: true } : {}
|
|
715
970
|
};
|
|
716
971
|
} catch (e) {
|
|
717
972
|
if (signal.aborted) throw new AbortError();
|
|
@@ -740,16 +995,33 @@ var VercelAIAgentService = class {
|
|
|
740
995
|
const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
741
996
|
try {
|
|
742
997
|
const res = await globalThis.fetch(`${baseUrl}/models`, {
|
|
743
|
-
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
|
+
}
|
|
744
1003
|
});
|
|
745
1004
|
if (!res.ok) {
|
|
746
1005
|
return [];
|
|
747
1006
|
}
|
|
748
1007
|
const body = await res.json();
|
|
749
|
-
if (
|
|
750
|
-
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
|
+
}));
|
|
751
1015
|
}
|
|
752
|
-
|
|
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
|
+
}));
|
|
1023
|
+
}
|
|
1024
|
+
return [];
|
|
753
1025
|
} catch {
|
|
754
1026
|
return [];
|
|
755
1027
|
}
|