@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
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) => ({
|
|
@@ -373,17 +631,6 @@ function convertParameters(params) {
|
|
|
373
631
|
}
|
|
374
632
|
return params;
|
|
375
633
|
}
|
|
376
|
-
async function mapToolsToSDKAsync(tools) {
|
|
377
|
-
return tools.map((tool) => ({
|
|
378
|
-
name: tool.name,
|
|
379
|
-
description: tool.description,
|
|
380
|
-
parameters: convertParameters(tool.parameters),
|
|
381
|
-
handler: async (args) => {
|
|
382
|
-
const result = await tool.execute(args);
|
|
383
|
-
return typeof result === "string" ? result : JSON.stringify(result);
|
|
384
|
-
}
|
|
385
|
-
}));
|
|
386
|
-
}
|
|
387
634
|
function buildPermissionHandler(config) {
|
|
388
635
|
const onPermission = config.supervisor?.onPermission;
|
|
389
636
|
if (!onPermission) {
|
|
@@ -398,6 +645,7 @@ function buildPermissionHandler(config) {
|
|
|
398
645
|
const unifiedRequest = {
|
|
399
646
|
toolName,
|
|
400
647
|
toolArgs: { ...request },
|
|
648
|
+
toolCallId: request.toolCallId,
|
|
401
649
|
rawSDKRequest: request
|
|
402
650
|
};
|
|
403
651
|
const ac = new AbortController();
|
|
@@ -540,15 +788,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
|
|
|
540
788
|
};
|
|
541
789
|
case "session.error":
|
|
542
790
|
console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
791
|
+
{
|
|
792
|
+
const errorMsg = String(data.message ?? "Unknown error");
|
|
793
|
+
const code = classifyAgentError(errorMsg);
|
|
794
|
+
return {
|
|
795
|
+
type: "error",
|
|
796
|
+
error: errorMsg,
|
|
797
|
+
recoverable: isRecoverableErrorCode(code),
|
|
798
|
+
code
|
|
799
|
+
};
|
|
800
|
+
}
|
|
548
801
|
case "assistant.message": {
|
|
549
802
|
const doneEvent = {
|
|
550
803
|
type: "done",
|
|
551
|
-
finalOutput:
|
|
804
|
+
finalOutput: null,
|
|
805
|
+
streamed: true
|
|
552
806
|
};
|
|
553
807
|
if (thinkingTracker.endThinking()) {
|
|
554
808
|
return [{ type: "thinking_end" }, doneEvent];
|
|
@@ -568,6 +822,7 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
568
822
|
isPersistent;
|
|
569
823
|
persistentSession = null;
|
|
570
824
|
_sessionId;
|
|
825
|
+
_persistentModel;
|
|
571
826
|
activeSession = null;
|
|
572
827
|
_resumeSessionId;
|
|
573
828
|
_toolsReady = null;
|
|
@@ -586,15 +841,15 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
586
841
|
},
|
|
587
842
|
onPermissionRequest: buildPermissionHandler(config),
|
|
588
843
|
onUserInputRequest: buildUserInputHandler(config),
|
|
589
|
-
...config.availableTools
|
|
844
|
+
...config.availableTools ? { availableTools: config.availableTools } : {}
|
|
590
845
|
};
|
|
591
846
|
this._toolsReady = this._initToolsAsync(config);
|
|
592
847
|
this._resumeSessionId = resumeSessionId;
|
|
593
848
|
}
|
|
594
|
-
/** Pre-convert Zod schemas to JSON Schema
|
|
849
|
+
/** Pre-convert Zod schemas to JSON Schema.
|
|
595
850
|
* Updates sdkTools and sessionConfig.tools before first session creation. */
|
|
596
851
|
async _initToolsAsync(config) {
|
|
597
|
-
this.sdkTools =
|
|
852
|
+
this.sdkTools = mapToolsToSDK(config.tools ?? []);
|
|
598
853
|
this.sessionConfig.tools = this.sdkTools;
|
|
599
854
|
}
|
|
600
855
|
get sessionId() {
|
|
@@ -618,47 +873,63 @@ var CopilotAgent = class extends BaseAgent {
|
|
|
618
873
|
});
|
|
619
874
|
this.persistentSession = null;
|
|
620
875
|
this._sessionId = void 0;
|
|
876
|
+
this._persistentModel = void 0;
|
|
621
877
|
}
|
|
622
878
|
}
|
|
623
|
-
async getOrCreateSession(streaming) {
|
|
879
|
+
async getOrCreateSession(streaming, options) {
|
|
624
880
|
if (this.isPersistent && this.persistentSession) {
|
|
625
|
-
|
|
881
|
+
if (options.model !== this._persistentModel) {
|
|
882
|
+
this.persistentSession.destroy().catch(() => {
|
|
883
|
+
});
|
|
884
|
+
this.persistentSession = null;
|
|
885
|
+
this._sessionId = void 0;
|
|
886
|
+
} else {
|
|
887
|
+
return { session: this.persistentSession, isNew: false };
|
|
888
|
+
}
|
|
626
889
|
}
|
|
627
890
|
if (this._toolsReady) {
|
|
628
891
|
await this._toolsReady;
|
|
629
892
|
this._toolsReady = null;
|
|
630
893
|
}
|
|
894
|
+
const sessionConfig = { ...this.sessionConfig };
|
|
895
|
+
sessionConfig.model = options.model;
|
|
896
|
+
const resolvedTools = this.resolveTools(options);
|
|
897
|
+
if (options?.tools) {
|
|
898
|
+
sessionConfig.tools = mapToolsToSDK(resolvedTools);
|
|
899
|
+
}
|
|
631
900
|
const client = await this.getClient();
|
|
632
901
|
if (this._resumeSessionId) {
|
|
633
902
|
const storedId = this._resumeSessionId;
|
|
634
903
|
this._resumeSessionId = void 0;
|
|
635
904
|
try {
|
|
636
905
|
const session2 = await client.resumeSession(storedId, {
|
|
637
|
-
...
|
|
906
|
+
...sessionConfig,
|
|
638
907
|
streaming: this.isPersistent ? true : streaming
|
|
639
908
|
});
|
|
640
909
|
if (this.isPersistent) {
|
|
641
910
|
this.persistentSession = session2;
|
|
642
911
|
this._sessionId = session2.sessionId;
|
|
912
|
+
this._persistentModel = options.model;
|
|
643
913
|
}
|
|
644
914
|
return { session: session2, isNew: false };
|
|
645
915
|
} catch {
|
|
646
916
|
}
|
|
647
917
|
}
|
|
648
918
|
const session = await client.createSession({
|
|
649
|
-
...
|
|
919
|
+
...sessionConfig,
|
|
650
920
|
streaming: this.isPersistent ? true : streaming
|
|
651
921
|
});
|
|
652
922
|
if (this.isPersistent) {
|
|
653
923
|
this.persistentSession = session;
|
|
654
924
|
this._sessionId = session.sessionId;
|
|
925
|
+
this._persistentModel = options.model;
|
|
655
926
|
}
|
|
656
927
|
return { session, isNew: true };
|
|
657
928
|
}
|
|
658
929
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
659
|
-
async executeRun(messages,
|
|
930
|
+
async executeRun(messages, options, signal) {
|
|
660
931
|
this.checkAbort(signal);
|
|
661
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
|
|
932
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
|
|
662
933
|
this.activeSession = session;
|
|
663
934
|
const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
664
935
|
const tracker = new ToolCallTracker();
|
|
@@ -755,9 +1026,9 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
755
1026
|
};
|
|
756
1027
|
}
|
|
757
1028
|
// ─── executeStream ──────────────────────────────────────────────
|
|
758
|
-
async *executeStream(messages,
|
|
1029
|
+
async *executeStream(messages, options, signal) {
|
|
759
1030
|
this.checkAbort(signal);
|
|
760
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
|
|
1031
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
|
|
761
1032
|
this.activeSession = session;
|
|
762
1033
|
if (isNewSession) {
|
|
763
1034
|
yield this.emitSessionInfo(session.sessionId);
|
|
@@ -843,59 +1114,6 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
843
1114
|
super.dispose();
|
|
844
1115
|
}
|
|
845
1116
|
};
|
|
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
1117
|
function withTimeout(promise, ms, message) {
|
|
900
1118
|
return new Promise((resolve, reject) => {
|
|
901
1119
|
const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
|
|
@@ -937,7 +1155,11 @@ var CopilotAgentService = class {
|
|
|
937
1155
|
githubToken: this.options.githubToken,
|
|
938
1156
|
useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
|
|
939
1157
|
...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
|
|
940
|
-
|
|
1158
|
+
env: {
|
|
1159
|
+
...process.env,
|
|
1160
|
+
...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
|
|
1161
|
+
...this.options.env
|
|
1162
|
+
}
|
|
941
1163
|
});
|
|
942
1164
|
const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
|
|
943
1165
|
await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
|
|
@@ -971,7 +1193,10 @@ var CopilotAgentService = class {
|
|
|
971
1193
|
return models.map((m) => ({
|
|
972
1194
|
id: m.id,
|
|
973
1195
|
name: m.name,
|
|
974
|
-
provider: "copilot"
|
|
1196
|
+
provider: "copilot",
|
|
1197
|
+
...m.capabilities?.limits?.max_context_window_tokens != null && {
|
|
1198
|
+
contextWindow: m.capabilities.limits.max_context_window_tokens
|
|
1199
|
+
}
|
|
975
1200
|
}));
|
|
976
1201
|
}
|
|
977
1202
|
async validate() {
|