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