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