@witqq/agent-sdk 0.7.0 → 0.9.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/dist/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
- package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -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 +337 -112
- 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 +337 -112
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/mock-llm.cjs +719 -0
- package/dist/backends/mock-llm.cjs.map +1 -0
- package/dist/backends/mock-llm.d.cts +37 -0
- package/dist/backends/mock-llm.d.ts +37 -0
- package/dist/backends/mock-llm.js +717 -0
- package/dist/backends/mock-llm.js.map +1 -0
- package/dist/backends/vercel-ai.cjs +301 -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 +301 -61
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/backends-Cno0gZjy.d.cts +114 -0
- package/dist/backends-Cno0gZjy.d.ts +114 -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 +1084 -821
- 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 +1082 -800
- 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 +60 -27
- package/dist/chat/core.cjs.map +1 -1
- package/dist/chat/core.d.cts +41 -382
- package/dist/chat/core.d.ts +41 -382
- package/dist/chat/core.js +58 -28
- 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 +1612 -1125
- 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 +1600 -1097
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +2212 -1158
- package/dist/chat/react.cjs.map +1 -1
- package/dist/chat/react.d.cts +665 -122
- package/dist/chat/react.d.ts +665 -122
- package/dist/chat/react.js +2191 -1156
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/runtime.cjs +405 -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 +405 -186
- package/dist/chat/runtime.js.map +1 -1
- package/dist/chat/server.cjs +2247 -212
- 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 +2234 -213
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/sessions.cjs +64 -66
- 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 +65 -67
- package/dist/chat/sessions.js.map +1 -1
- package/dist/chat/sqlite.cjs +536 -0
- package/dist/chat/sqlite.cjs.map +1 -0
- package/dist/chat/sqlite.d.cts +164 -0
- package/dist/chat/sqlite.d.ts +164 -0
- package/dist/chat/sqlite.js +527 -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 +58 -33
- package/dist/chat/storage.cjs.map +1 -1
- package/dist/chat/storage.d.cts +18 -8
- package/dist/chat/storage.d.ts +18 -8
- package/dist/chat/storage.js +59 -34
- 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-7EIit9Xk.d.ts} +72 -33
- package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
- package/dist/index.cjs +354 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +294 -123
- package/dist/index.d.ts +294 -123
- package/dist/index.js +347 -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 +1107 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +144 -0
- package/dist/testing.d.ts +144 -0
- package/dist/testing.js +1101 -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-DLWCN18G.d.cts} +5 -4
- package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.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-DgtI1hzh.d.ts +364 -0
- package/dist/types-DkSXALKg.d.cts +364 -0
- package/package.json +41 -5
- package/LICENSE +0 -21
- package/README.md +0 -948
- 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) => ({
|
|
@@ -375,17 +633,6 @@ function convertParameters(params) {
|
|
|
375
633
|
}
|
|
376
634
|
return params;
|
|
377
635
|
}
|
|
378
|
-
async function mapToolsToSDKAsync(tools) {
|
|
379
|
-
return tools.map((tool) => ({
|
|
380
|
-
name: tool.name,
|
|
381
|
-
description: tool.description,
|
|
382
|
-
parameters: convertParameters(tool.parameters),
|
|
383
|
-
handler: async (args) => {
|
|
384
|
-
const result = await tool.execute(args);
|
|
385
|
-
return typeof result === "string" ? result : JSON.stringify(result);
|
|
386
|
-
}
|
|
387
|
-
}));
|
|
388
|
-
}
|
|
389
636
|
function buildPermissionHandler(config) {
|
|
390
637
|
const onPermission = config.supervisor?.onPermission;
|
|
391
638
|
if (!onPermission) {
|
|
@@ -400,6 +647,7 @@ function buildPermissionHandler(config) {
|
|
|
400
647
|
const unifiedRequest = {
|
|
401
648
|
toolName,
|
|
402
649
|
toolArgs: { ...request },
|
|
650
|
+
toolCallId: request.toolCallId,
|
|
403
651
|
rawSDKRequest: request
|
|
404
652
|
};
|
|
405
653
|
const ac = new AbortController();
|
|
@@ -542,15 +790,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
|
|
|
542
790
|
};
|
|
543
791
|
case "session.error":
|
|
544
792
|
console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
793
|
+
{
|
|
794
|
+
const errorMsg = String(data.message ?? "Unknown error");
|
|
795
|
+
const code = classifyAgentError(errorMsg);
|
|
796
|
+
return {
|
|
797
|
+
type: "error",
|
|
798
|
+
error: errorMsg,
|
|
799
|
+
recoverable: isRecoverableErrorCode(code),
|
|
800
|
+
code
|
|
801
|
+
};
|
|
802
|
+
}
|
|
550
803
|
case "assistant.message": {
|
|
551
804
|
const doneEvent = {
|
|
552
805
|
type: "done",
|
|
553
|
-
finalOutput:
|
|
806
|
+
finalOutput: null,
|
|
807
|
+
streamed: true
|
|
554
808
|
};
|
|
555
809
|
if (thinkingTracker.endThinking()) {
|
|
556
810
|
return [{ type: "thinking_end" }, doneEvent];
|
|
@@ -570,6 +824,7 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
570
824
|
isPersistent;
|
|
571
825
|
persistentSession = null;
|
|
572
826
|
_sessionId;
|
|
827
|
+
_persistentModel;
|
|
573
828
|
activeSession = null;
|
|
574
829
|
_resumeSessionId;
|
|
575
830
|
_toolsReady = null;
|
|
@@ -588,15 +843,15 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
588
843
|
},
|
|
589
844
|
onPermissionRequest: buildPermissionHandler(config),
|
|
590
845
|
onUserInputRequest: buildUserInputHandler(config),
|
|
591
|
-
...config.availableTools
|
|
846
|
+
...config.availableTools ? { availableTools: config.availableTools } : {}
|
|
592
847
|
};
|
|
593
848
|
this._toolsReady = this._initToolsAsync(config);
|
|
594
849
|
this._resumeSessionId = resumeSessionId;
|
|
595
850
|
}
|
|
596
|
-
/** Pre-convert Zod schemas to JSON Schema
|
|
851
|
+
/** Pre-convert Zod schemas to JSON Schema.
|
|
597
852
|
* Updates sdkTools and sessionConfig.tools before first session creation. */
|
|
598
853
|
async _initToolsAsync(config) {
|
|
599
|
-
this.sdkTools =
|
|
854
|
+
this.sdkTools = mapToolsToSDK(config.tools ?? []);
|
|
600
855
|
this.sessionConfig.tools = this.sdkTools;
|
|
601
856
|
}
|
|
602
857
|
get sessionId() {
|
|
@@ -620,47 +875,63 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
620
875
|
});
|
|
621
876
|
this.persistentSession = null;
|
|
622
877
|
this._sessionId = void 0;
|
|
878
|
+
this._persistentModel = void 0;
|
|
623
879
|
}
|
|
624
880
|
}
|
|
625
|
-
async getOrCreateSession(streaming) {
|
|
881
|
+
async getOrCreateSession(streaming, options) {
|
|
626
882
|
if (this.isPersistent && this.persistentSession) {
|
|
627
|
-
|
|
883
|
+
if (options.model !== this._persistentModel) {
|
|
884
|
+
this.persistentSession.destroy().catch(() => {
|
|
885
|
+
});
|
|
886
|
+
this.persistentSession = null;
|
|
887
|
+
this._sessionId = void 0;
|
|
888
|
+
} else {
|
|
889
|
+
return { session: this.persistentSession, isNew: false };
|
|
890
|
+
}
|
|
628
891
|
}
|
|
629
892
|
if (this._toolsReady) {
|
|
630
893
|
await this._toolsReady;
|
|
631
894
|
this._toolsReady = null;
|
|
632
895
|
}
|
|
896
|
+
const sessionConfig = { ...this.sessionConfig };
|
|
897
|
+
sessionConfig.model = options.model;
|
|
898
|
+
const resolvedTools = this.resolveTools(options);
|
|
899
|
+
if (options?.tools) {
|
|
900
|
+
sessionConfig.tools = mapToolsToSDK(resolvedTools);
|
|
901
|
+
}
|
|
633
902
|
const client = await this.getClient();
|
|
634
903
|
if (this._resumeSessionId) {
|
|
635
904
|
const storedId = this._resumeSessionId;
|
|
636
905
|
this._resumeSessionId = void 0;
|
|
637
906
|
try {
|
|
638
907
|
const session2 = await client.resumeSession(storedId, {
|
|
639
|
-
...
|
|
908
|
+
...sessionConfig,
|
|
640
909
|
streaming: this.isPersistent ? true : streaming
|
|
641
910
|
});
|
|
642
911
|
if (this.isPersistent) {
|
|
643
912
|
this.persistentSession = session2;
|
|
644
913
|
this._sessionId = session2.sessionId;
|
|
914
|
+
this._persistentModel = options.model;
|
|
645
915
|
}
|
|
646
916
|
return { session: session2, isNew: false };
|
|
647
917
|
} catch {
|
|
648
918
|
}
|
|
649
919
|
}
|
|
650
920
|
const session = await client.createSession({
|
|
651
|
-
...
|
|
921
|
+
...sessionConfig,
|
|
652
922
|
streaming: this.isPersistent ? true : streaming
|
|
653
923
|
});
|
|
654
924
|
if (this.isPersistent) {
|
|
655
925
|
this.persistentSession = session;
|
|
656
926
|
this._sessionId = session.sessionId;
|
|
927
|
+
this._persistentModel = options.model;
|
|
657
928
|
}
|
|
658
929
|
return { session, isNew: true };
|
|
659
930
|
}
|
|
660
931
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
661
|
-
async executeRun(messages,
|
|
932
|
+
async executeRun(messages, options, signal) {
|
|
662
933
|
this.checkAbort(signal);
|
|
663
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
|
|
934
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
|
|
664
935
|
this.activeSession = session;
|
|
665
936
|
const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
666
937
|
const tracker = new ToolCallTracker();
|
|
@@ -757,9 +1028,9 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
757
1028
|
};
|
|
758
1029
|
}
|
|
759
1030
|
// ─── executeStream ──────────────────────────────────────────────
|
|
760
|
-
async *executeStream(messages,
|
|
1031
|
+
async *executeStream(messages, options, signal) {
|
|
761
1032
|
this.checkAbort(signal);
|
|
762
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
|
|
1033
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
|
|
763
1034
|
this.activeSession = session;
|
|
764
1035
|
if (isNewSession) {
|
|
765
1036
|
yield this.emitSessionInfo(session.sessionId);
|
|
@@ -845,59 +1116,6 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
845
1116
|
super.dispose();
|
|
846
1117
|
}
|
|
847
1118
|
};
|
|
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
1119
|
function withTimeout(promise, ms, message) {
|
|
902
1120
|
return new Promise((resolve, reject) => {
|
|
903
1121
|
const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
|
|
@@ -939,7 +1157,11 @@ var CopilotAgentService = class {
|
|
|
939
1157
|
githubToken: this.options.githubToken,
|
|
940
1158
|
useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
|
|
941
1159
|
...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
|
|
942
|
-
|
|
1160
|
+
env: {
|
|
1161
|
+
...process.env,
|
|
1162
|
+
...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
|
|
1163
|
+
...this.options.env
|
|
1164
|
+
}
|
|
943
1165
|
});
|
|
944
1166
|
const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
|
|
945
1167
|
await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
|
|
@@ -973,7 +1195,10 @@ var CopilotAgentService = class {
|
|
|
973
1195
|
return models.map((m) => ({
|
|
974
1196
|
id: m.id,
|
|
975
1197
|
name: m.name,
|
|
976
|
-
provider: "copilot"
|
|
1198
|
+
provider: "copilot",
|
|
1199
|
+
...m.capabilities?.limits?.max_context_window_tokens != null && {
|
|
1200
|
+
contextWindow: m.capabilities.limits.max_context_window_tokens
|
|
1201
|
+
}
|
|
977
1202
|
}));
|
|
978
1203
|
}
|
|
979
1204
|
async validate() {
|