@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,28 +68,41 @@ 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 SubprocessError = class extends AgentSDKError {
|
|
35
86
|
constructor(message, options) {
|
|
36
|
-
super(message, options);
|
|
87
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
37
88
|
this.name = "SubprocessError";
|
|
38
89
|
}
|
|
39
90
|
};
|
|
40
91
|
var AbortError = class extends AgentSDKError {
|
|
41
92
|
constructor() {
|
|
42
|
-
super("Agent run was aborted.");
|
|
93
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
43
94
|
this.name = "AbortError";
|
|
44
95
|
}
|
|
45
96
|
};
|
|
97
|
+
var ActivityTimeoutError = class extends AgentSDKError {
|
|
98
|
+
constructor(timeoutMs) {
|
|
99
|
+
super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
|
|
100
|
+
code: "TIMEOUT" /* TIMEOUT */,
|
|
101
|
+
retryable: true
|
|
102
|
+
});
|
|
103
|
+
this.name = "ActivityTimeoutError";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
46
106
|
|
|
47
107
|
// src/base-agent.ts
|
|
48
108
|
var BaseAgent = class {
|
|
@@ -50,6 +110,7 @@ var BaseAgent = class {
|
|
|
50
110
|
abortController = null;
|
|
51
111
|
config;
|
|
52
112
|
_cleanupExternalSignal = null;
|
|
113
|
+
_streamMiddleware = [];
|
|
53
114
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
54
115
|
get sessionId() {
|
|
55
116
|
return void 0;
|
|
@@ -65,8 +126,11 @@ var BaseAgent = class {
|
|
|
65
126
|
this.state = "running";
|
|
66
127
|
try {
|
|
67
128
|
const messages = [{ role: "user", content: prompt }];
|
|
68
|
-
const result = await this.
|
|
69
|
-
|
|
129
|
+
const result = await this.withRetry(
|
|
130
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
131
|
+
options
|
|
132
|
+
);
|
|
133
|
+
this.enrichAndNotifyUsage(result, options);
|
|
70
134
|
return result;
|
|
71
135
|
} finally {
|
|
72
136
|
this.cleanupRun();
|
|
@@ -78,8 +142,11 @@ var BaseAgent = class {
|
|
|
78
142
|
const ac = this.createAbortController(options?.signal);
|
|
79
143
|
this.state = "running";
|
|
80
144
|
try {
|
|
81
|
-
const result = await this.
|
|
82
|
-
|
|
145
|
+
const result = await this.withRetry(
|
|
146
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
147
|
+
options
|
|
148
|
+
);
|
|
149
|
+
this.enrichAndNotifyUsage(result, options);
|
|
83
150
|
return result;
|
|
84
151
|
} finally {
|
|
85
152
|
this.cleanupRun();
|
|
@@ -92,13 +159,11 @@ var BaseAgent = class {
|
|
|
92
159
|
this.state = "running";
|
|
93
160
|
try {
|
|
94
161
|
const messages = [{ role: "user", content: prompt }];
|
|
95
|
-
const result = await this.
|
|
96
|
-
messages,
|
|
97
|
-
|
|
98
|
-
options,
|
|
99
|
-
ac.signal
|
|
162
|
+
const result = await this.withRetry(
|
|
163
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
164
|
+
options
|
|
100
165
|
);
|
|
101
|
-
this.enrichAndNotifyUsage(result);
|
|
166
|
+
this.enrichAndNotifyUsage(result, options);
|
|
102
167
|
return result;
|
|
103
168
|
} finally {
|
|
104
169
|
this.cleanupRun();
|
|
@@ -111,8 +176,10 @@ var BaseAgent = class {
|
|
|
111
176
|
this.state = "streaming";
|
|
112
177
|
try {
|
|
113
178
|
const messages = [{ role: "user", content: prompt }];
|
|
114
|
-
|
|
115
|
-
|
|
179
|
+
yield* this.streamWithRetry(
|
|
180
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
181
|
+
options
|
|
182
|
+
);
|
|
116
183
|
} finally {
|
|
117
184
|
this.cleanupRun();
|
|
118
185
|
}
|
|
@@ -123,12 +190,37 @@ var BaseAgent = class {
|
|
|
123
190
|
const ac = this.createAbortController(options?.signal);
|
|
124
191
|
this.state = "streaming";
|
|
125
192
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
193
|
+
yield* this.streamWithRetry(
|
|
194
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
195
|
+
options
|
|
196
|
+
);
|
|
128
197
|
} finally {
|
|
129
198
|
this.cleanupRun();
|
|
130
199
|
}
|
|
131
200
|
}
|
|
201
|
+
/** Register a stream middleware. Applied in registration order after built-in transforms. */
|
|
202
|
+
addStreamMiddleware(middleware) {
|
|
203
|
+
this.guardDisposed();
|
|
204
|
+
this._streamMiddleware.push(middleware);
|
|
205
|
+
}
|
|
206
|
+
/** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
|
|
207
|
+
async *applyStreamPipeline(source, options, ac) {
|
|
208
|
+
let stream = this.enrichStream(source, options);
|
|
209
|
+
stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
|
|
210
|
+
stream = this.heartbeatStream(stream);
|
|
211
|
+
if (this._streamMiddleware.length > 0) {
|
|
212
|
+
const ctx = {
|
|
213
|
+
model: options.model,
|
|
214
|
+
backend: this.backendName,
|
|
215
|
+
abortController: ac,
|
|
216
|
+
config: Object.freeze({ ...this.config })
|
|
217
|
+
};
|
|
218
|
+
for (const mw of this._streamMiddleware) {
|
|
219
|
+
stream = mw(stream, ctx);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
yield* stream;
|
|
223
|
+
}
|
|
132
224
|
abort() {
|
|
133
225
|
if (this.abortController) {
|
|
134
226
|
this.abortController.abort();
|
|
@@ -151,26 +243,109 @@ var BaseAgent = class {
|
|
|
151
243
|
this.abort();
|
|
152
244
|
this.state = "disposed";
|
|
153
245
|
}
|
|
246
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
247
|
+
/** Check if an error should be retried given the retry configuration. */
|
|
248
|
+
isRetryableError(error, retry) {
|
|
249
|
+
if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (AgentSDKError.is(error)) {
|
|
253
|
+
if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
|
|
254
|
+
return retry.retryableErrors.includes(error.code);
|
|
255
|
+
}
|
|
256
|
+
if (error.retryable) return true;
|
|
257
|
+
if (error.code) return isRecoverableErrorCode(error.code);
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
/** Execute a function with retry logic per RetryConfig. */
|
|
262
|
+
async withRetry(fn, options) {
|
|
263
|
+
const retry = options?.retry;
|
|
264
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
265
|
+
return fn();
|
|
266
|
+
}
|
|
267
|
+
const maxRetries = retry.maxRetries;
|
|
268
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
269
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
270
|
+
let lastError;
|
|
271
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
272
|
+
try {
|
|
273
|
+
return await fn();
|
|
274
|
+
} catch (err) {
|
|
275
|
+
lastError = err;
|
|
276
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
279
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
280
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
281
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
throw lastError;
|
|
287
|
+
}
|
|
288
|
+
/** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
|
|
289
|
+
async *streamWithRetry(factory, options) {
|
|
290
|
+
const retry = options?.retry;
|
|
291
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
292
|
+
yield* factory();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const maxRetries = retry.maxRetries;
|
|
296
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
297
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
298
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
299
|
+
try {
|
|
300
|
+
const stream = factory();
|
|
301
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
302
|
+
const first = await iterator.next();
|
|
303
|
+
if (first.done) return;
|
|
304
|
+
yield first.value;
|
|
305
|
+
while (true) {
|
|
306
|
+
const next = await iterator.next();
|
|
307
|
+
if (next.done) break;
|
|
308
|
+
yield next.value;
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
const delay = initialDelay * Math.pow(multiplier, attempt);
|
|
316
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
317
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ─── CallOptions Resolution ──────────────────────────────────
|
|
324
|
+
/** Resolve tools to use for this call (per-call override > config default) */
|
|
325
|
+
resolveTools(options) {
|
|
326
|
+
return options?.tools ?? this.config.tools ?? [];
|
|
327
|
+
}
|
|
154
328
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
155
329
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
156
|
-
enrichAndNotifyUsage(result) {
|
|
330
|
+
enrichAndNotifyUsage(result, options) {
|
|
157
331
|
if (result.usage) {
|
|
158
332
|
result.usage = {
|
|
159
333
|
...result.usage,
|
|
160
|
-
model:
|
|
334
|
+
model: options.model,
|
|
161
335
|
backend: this.backendName
|
|
162
336
|
};
|
|
163
337
|
this.callOnUsage(result.usage);
|
|
164
338
|
}
|
|
165
339
|
}
|
|
166
340
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
167
|
-
async *enrichStream(source) {
|
|
341
|
+
async *enrichStream(source, options) {
|
|
342
|
+
const model = options.model;
|
|
168
343
|
for await (const event of source) {
|
|
169
344
|
if (event.type === "usage_update") {
|
|
170
345
|
const usage = {
|
|
171
346
|
promptTokens: event.promptTokens,
|
|
172
347
|
completionTokens: event.completionTokens,
|
|
173
|
-
model
|
|
348
|
+
model,
|
|
174
349
|
backend: this.backendName
|
|
175
350
|
};
|
|
176
351
|
this.callOnUsage(usage);
|
|
@@ -240,6 +415,35 @@ var BaseAgent = class {
|
|
|
240
415
|
heartbeatResolve = null;
|
|
241
416
|
}
|
|
242
417
|
}
|
|
418
|
+
// ─── Activity Timeout ────────────────────────────────────────
|
|
419
|
+
/** Wrap a stream to abort on inactivity. Resets timer on every event.
|
|
420
|
+
* When timeoutMs is not set, passes through directly. */
|
|
421
|
+
async *activityTimeoutStream(source, timeoutMs, ac) {
|
|
422
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
423
|
+
yield* source;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
427
|
+
let timerId;
|
|
428
|
+
try {
|
|
429
|
+
while (true) {
|
|
430
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
431
|
+
timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
|
|
432
|
+
});
|
|
433
|
+
const result = await Promise.race([iterator.next(), timeoutPromise]);
|
|
434
|
+
clearTimeout(timerId);
|
|
435
|
+
if (result.done) break;
|
|
436
|
+
yield result.value;
|
|
437
|
+
}
|
|
438
|
+
} catch (err) {
|
|
439
|
+
if (err instanceof ActivityTimeoutError) {
|
|
440
|
+
ac.abort(err);
|
|
441
|
+
}
|
|
442
|
+
throw err;
|
|
443
|
+
} finally {
|
|
444
|
+
clearTimeout(timerId);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
243
447
|
// ─── Guards ───────────────────────────────────────────────────
|
|
244
448
|
guardReentrancy() {
|
|
245
449
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -338,13 +542,67 @@ function extractSchemaFromDef(schema) {
|
|
|
338
542
|
}
|
|
339
543
|
}
|
|
340
544
|
|
|
545
|
+
// src/backends/shared.ts
|
|
546
|
+
function extractLastUserPrompt(messages) {
|
|
547
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
548
|
+
const msg = messages[i];
|
|
549
|
+
if (msg.role === "user") {
|
|
550
|
+
return getTextContent(msg.content);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return "";
|
|
554
|
+
}
|
|
555
|
+
function serializeToolCall(tc) {
|
|
556
|
+
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
557
|
+
return ` Tool call: ${tc.name}(${args})`;
|
|
558
|
+
}
|
|
559
|
+
function serializeToolResult(tr) {
|
|
560
|
+
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
561
|
+
const prefix = tr.isError ? "[ERROR] " : "";
|
|
562
|
+
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
563
|
+
}
|
|
564
|
+
function buildContextualPrompt(messages) {
|
|
565
|
+
if (messages.length <= 1) {
|
|
566
|
+
return extractLastUserPrompt(messages);
|
|
567
|
+
}
|
|
568
|
+
const history = messages.slice(0, -1).map((msg) => {
|
|
569
|
+
if (msg.role === "user") {
|
|
570
|
+
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
571
|
+
}
|
|
572
|
+
if (msg.role === "tool" && msg.toolResults) {
|
|
573
|
+
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
574
|
+
return `Tool results:
|
|
575
|
+
${results}`;
|
|
576
|
+
}
|
|
577
|
+
if (msg.role === "assistant") {
|
|
578
|
+
const parts = [];
|
|
579
|
+
const thinking = msg.thinking;
|
|
580
|
+
if (thinking) {
|
|
581
|
+
parts.push(`[reasoning: ${thinking}]`);
|
|
582
|
+
}
|
|
583
|
+
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
584
|
+
if (text2) parts.push(text2);
|
|
585
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
586
|
+
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
587
|
+
}
|
|
588
|
+
return `Assistant: ${parts.join("\n")}`;
|
|
589
|
+
}
|
|
590
|
+
const text = msg.content ? getTextContent(msg.content) : "";
|
|
591
|
+
return `${msg.role}: ${text}`;
|
|
592
|
+
}).join("\n");
|
|
593
|
+
const lastPrompt = extractLastUserPrompt(messages);
|
|
594
|
+
return `Conversation history:
|
|
595
|
+
${history}
|
|
596
|
+
|
|
597
|
+
User: ${lastPrompt}`;
|
|
598
|
+
}
|
|
599
|
+
|
|
341
600
|
// src/backends/copilot.ts
|
|
342
|
-
var
|
|
601
|
+
var _sdkMock = null;
|
|
343
602
|
async function loadSDK() {
|
|
344
|
-
if (
|
|
603
|
+
if (_sdkMock) return _sdkMock;
|
|
345
604
|
try {
|
|
346
|
-
|
|
347
|
-
return sdkModule;
|
|
605
|
+
return await import('@github/copilot-sdk');
|
|
348
606
|
} catch {
|
|
349
607
|
throw new SubprocessError(
|
|
350
608
|
"@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
|
|
@@ -352,10 +610,10 @@ async function loadSDK() {
|
|
|
352
610
|
}
|
|
353
611
|
}
|
|
354
612
|
function _injectSDK(mock) {
|
|
355
|
-
|
|
613
|
+
_sdkMock = mock;
|
|
356
614
|
}
|
|
357
615
|
function _resetSDK() {
|
|
358
|
-
|
|
616
|
+
_sdkMock = null;
|
|
359
617
|
}
|
|
360
618
|
function mapToolsToSDK(tools) {
|
|
361
619
|
return tools.map((tool) => ({
|
|
@@ -400,6 +658,7 @@ function buildPermissionHandler(config) {
|
|
|
400
658
|
const unifiedRequest = {
|
|
401
659
|
toolName,
|
|
402
660
|
toolArgs: { ...request },
|
|
661
|
+
toolCallId: request.toolCallId,
|
|
403
662
|
rawSDKRequest: request
|
|
404
663
|
};
|
|
405
664
|
const ac = new AbortController();
|
|
@@ -542,15 +801,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
|
|
|
542
801
|
};
|
|
543
802
|
case "session.error":
|
|
544
803
|
console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
804
|
+
{
|
|
805
|
+
const errorMsg = String(data.message ?? "Unknown error");
|
|
806
|
+
const code = classifyAgentError(errorMsg);
|
|
807
|
+
return {
|
|
808
|
+
type: "error",
|
|
809
|
+
error: errorMsg,
|
|
810
|
+
recoverable: isRecoverableErrorCode(code),
|
|
811
|
+
code
|
|
812
|
+
};
|
|
813
|
+
}
|
|
550
814
|
case "assistant.message": {
|
|
551
815
|
const doneEvent = {
|
|
552
816
|
type: "done",
|
|
553
|
-
finalOutput:
|
|
817
|
+
finalOutput: null,
|
|
818
|
+
streamed: true
|
|
554
819
|
};
|
|
555
820
|
if (thinkingTracker.endThinking()) {
|
|
556
821
|
return [{ type: "thinking_end" }, doneEvent];
|
|
@@ -570,6 +835,7 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
570
835
|
isPersistent;
|
|
571
836
|
persistentSession = null;
|
|
572
837
|
_sessionId;
|
|
838
|
+
_persistentModel;
|
|
573
839
|
activeSession = null;
|
|
574
840
|
_resumeSessionId;
|
|
575
841
|
_toolsReady = null;
|
|
@@ -620,47 +886,63 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
620
886
|
});
|
|
621
887
|
this.persistentSession = null;
|
|
622
888
|
this._sessionId = void 0;
|
|
889
|
+
this._persistentModel = void 0;
|
|
623
890
|
}
|
|
624
891
|
}
|
|
625
|
-
async getOrCreateSession(streaming) {
|
|
892
|
+
async getOrCreateSession(streaming, options) {
|
|
626
893
|
if (this.isPersistent && this.persistentSession) {
|
|
627
|
-
|
|
894
|
+
if (options.model !== this._persistentModel) {
|
|
895
|
+
this.persistentSession.destroy().catch(() => {
|
|
896
|
+
});
|
|
897
|
+
this.persistentSession = null;
|
|
898
|
+
this._sessionId = void 0;
|
|
899
|
+
} else {
|
|
900
|
+
return { session: this.persistentSession, isNew: false };
|
|
901
|
+
}
|
|
628
902
|
}
|
|
629
903
|
if (this._toolsReady) {
|
|
630
904
|
await this._toolsReady;
|
|
631
905
|
this._toolsReady = null;
|
|
632
906
|
}
|
|
907
|
+
const sessionConfig = { ...this.sessionConfig };
|
|
908
|
+
sessionConfig.model = options.model;
|
|
909
|
+
const resolvedTools = this.resolveTools(options);
|
|
910
|
+
if (options?.tools) {
|
|
911
|
+
sessionConfig.tools = mapToolsToSDK(resolvedTools);
|
|
912
|
+
}
|
|
633
913
|
const client = await this.getClient();
|
|
634
914
|
if (this._resumeSessionId) {
|
|
635
915
|
const storedId = this._resumeSessionId;
|
|
636
916
|
this._resumeSessionId = void 0;
|
|
637
917
|
try {
|
|
638
918
|
const session2 = await client.resumeSession(storedId, {
|
|
639
|
-
...
|
|
919
|
+
...sessionConfig,
|
|
640
920
|
streaming: this.isPersistent ? true : streaming
|
|
641
921
|
});
|
|
642
922
|
if (this.isPersistent) {
|
|
643
923
|
this.persistentSession = session2;
|
|
644
924
|
this._sessionId = session2.sessionId;
|
|
925
|
+
this._persistentModel = options.model;
|
|
645
926
|
}
|
|
646
927
|
return { session: session2, isNew: false };
|
|
647
928
|
} catch {
|
|
648
929
|
}
|
|
649
930
|
}
|
|
650
931
|
const session = await client.createSession({
|
|
651
|
-
...
|
|
932
|
+
...sessionConfig,
|
|
652
933
|
streaming: this.isPersistent ? true : streaming
|
|
653
934
|
});
|
|
654
935
|
if (this.isPersistent) {
|
|
655
936
|
this.persistentSession = session;
|
|
656
937
|
this._sessionId = session.sessionId;
|
|
938
|
+
this._persistentModel = options.model;
|
|
657
939
|
}
|
|
658
940
|
return { session, isNew: true };
|
|
659
941
|
}
|
|
660
942
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
661
|
-
async executeRun(messages,
|
|
943
|
+
async executeRun(messages, options, signal) {
|
|
662
944
|
this.checkAbort(signal);
|
|
663
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
|
|
945
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
|
|
664
946
|
this.activeSession = session;
|
|
665
947
|
const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
666
948
|
const tracker = new ToolCallTracker();
|
|
@@ -757,9 +1039,9 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
757
1039
|
};
|
|
758
1040
|
}
|
|
759
1041
|
// ─── executeStream ──────────────────────────────────────────────
|
|
760
|
-
async *executeStream(messages,
|
|
1042
|
+
async *executeStream(messages, options, signal) {
|
|
761
1043
|
this.checkAbort(signal);
|
|
762
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
|
|
1044
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
|
|
763
1045
|
this.activeSession = session;
|
|
764
1046
|
if (isNewSession) {
|
|
765
1047
|
yield this.emitSessionInfo(session.sessionId);
|
|
@@ -845,59 +1127,6 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
845
1127
|
super.dispose();
|
|
846
1128
|
}
|
|
847
1129
|
};
|
|
848
|
-
function extractLastUserPrompt(messages) {
|
|
849
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
850
|
-
const msg = messages[i];
|
|
851
|
-
if (msg.role === "user") {
|
|
852
|
-
return getTextContent(msg.content);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
return "";
|
|
856
|
-
}
|
|
857
|
-
function serializeToolCall(tc) {
|
|
858
|
-
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
859
|
-
return ` Tool call: ${tc.name}(${args})`;
|
|
860
|
-
}
|
|
861
|
-
function serializeToolResult(tr) {
|
|
862
|
-
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
863
|
-
const prefix = tr.isError ? "[ERROR] " : "";
|
|
864
|
-
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
865
|
-
}
|
|
866
|
-
function buildContextualPrompt(messages) {
|
|
867
|
-
if (messages.length <= 1) {
|
|
868
|
-
return extractLastUserPrompt(messages);
|
|
869
|
-
}
|
|
870
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
871
|
-
if (msg.role === "user") {
|
|
872
|
-
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
873
|
-
}
|
|
874
|
-
if (msg.role === "tool" && msg.toolResults) {
|
|
875
|
-
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
876
|
-
return `Tool results:
|
|
877
|
-
${results}`;
|
|
878
|
-
}
|
|
879
|
-
if (msg.role === "assistant") {
|
|
880
|
-
const parts = [];
|
|
881
|
-
const thinking = msg.thinking;
|
|
882
|
-
if (thinking) {
|
|
883
|
-
parts.push(`[reasoning: ${thinking}]`);
|
|
884
|
-
}
|
|
885
|
-
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
886
|
-
if (text2) parts.push(text2);
|
|
887
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
888
|
-
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
889
|
-
}
|
|
890
|
-
return `Assistant: ${parts.join("\n")}`;
|
|
891
|
-
}
|
|
892
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
893
|
-
return `${msg.role}: ${text}`;
|
|
894
|
-
}).join("\n");
|
|
895
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
896
|
-
return `Conversation history:
|
|
897
|
-
${history}
|
|
898
|
-
|
|
899
|
-
User: ${lastPrompt}`;
|
|
900
|
-
}
|
|
901
1130
|
function withTimeout(promise, ms, message) {
|
|
902
1131
|
return new Promise((resolve, reject) => {
|
|
903
1132
|
const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
|
|
@@ -973,7 +1202,10 @@ var CopilotAgentService = class {
|
|
|
973
1202
|
return models.map((m) => ({
|
|
974
1203
|
id: m.id,
|
|
975
1204
|
name: m.name,
|
|
976
|
-
provider: "copilot"
|
|
1205
|
+
provider: "copilot",
|
|
1206
|
+
...m.capabilities?.limits?.max_context_window_tokens != null && {
|
|
1207
|
+
contextWindow: m.capabilities.limits.max_context_window_tokens
|
|
1208
|
+
}
|
|
977
1209
|
}));
|
|
978
1210
|
}
|
|
979
1211
|
async validate() {
|