@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/chat/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import * as path from 'path';
|
|
1
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { readdir, unlink, mkdir, access, readFile, writeFile } from 'fs/promises';
|
|
4
3
|
import { join } from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
4
|
|
|
7
5
|
var __defProp = Object.defineProperty;
|
|
8
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -25,16 +23,100 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
23
|
};
|
|
26
24
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
25
|
|
|
28
|
-
// src/errors.ts
|
|
29
|
-
|
|
26
|
+
// src/types/errors.ts
|
|
27
|
+
function isRecoverableErrorCode(code) {
|
|
28
|
+
return RECOVERABLE_CODES.has(code);
|
|
29
|
+
}
|
|
30
|
+
function classifyAgentError(error) {
|
|
31
|
+
const msg = (error instanceof Error ? error.message : error).toLowerCase();
|
|
32
|
+
if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("timedout") || msg.includes("etimedout")) {
|
|
33
|
+
return "TIMEOUT" /* TIMEOUT */;
|
|
34
|
+
}
|
|
35
|
+
if (msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("429") || msg.includes("too many requests")) {
|
|
36
|
+
return "RATE_LIMIT" /* RATE_LIMIT */;
|
|
37
|
+
}
|
|
38
|
+
if (msg.includes("unauthorized") || msg.includes("401") || msg.includes("auth") && (msg.includes("expired") || msg.includes("invalid") || msg.includes("denied") || msg.includes("failed"))) {
|
|
39
|
+
return "AUTH_EXPIRED" /* AUTH_EXPIRED */;
|
|
40
|
+
}
|
|
41
|
+
if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("enotfound") || msg.includes("network") || msg.includes("fetch failed") || msg.includes("socket hang up")) {
|
|
42
|
+
return "NETWORK" /* NETWORK */;
|
|
43
|
+
}
|
|
44
|
+
if (msg.includes("subprocess") || msg.includes("process exited") || msg.includes("spawn") || msg.includes("enoent") || msg.includes("killed")) {
|
|
45
|
+
return "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */;
|
|
46
|
+
}
|
|
47
|
+
if (msg.includes("abort") || msg.includes("cancel")) {
|
|
48
|
+
return "ABORTED" /* ABORTED */;
|
|
49
|
+
}
|
|
50
|
+
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")) {
|
|
51
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
52
|
+
}
|
|
53
|
+
return "PROVIDER_ERROR" /* PROVIDER_ERROR */;
|
|
54
|
+
}
|
|
55
|
+
var ErrorCode, RECOVERABLE_CODES;
|
|
30
56
|
var init_errors = __esm({
|
|
57
|
+
"src/types/errors.ts"() {
|
|
58
|
+
ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
59
|
+
ErrorCode2["AUTH_EXPIRED"] = "AUTH_EXPIRED";
|
|
60
|
+
ErrorCode2["AUTH_INVALID"] = "AUTH_INVALID";
|
|
61
|
+
ErrorCode2["RATE_LIMIT"] = "RATE_LIMIT";
|
|
62
|
+
ErrorCode2["NETWORK"] = "NETWORK";
|
|
63
|
+
ErrorCode2["TIMEOUT"] = "TIMEOUT";
|
|
64
|
+
ErrorCode2["PROVIDER_ERROR"] = "PROVIDER_ERROR";
|
|
65
|
+
ErrorCode2["MODEL_NOT_FOUND"] = "MODEL_NOT_FOUND";
|
|
66
|
+
ErrorCode2["MODEL_OVERLOADED"] = "MODEL_OVERLOADED";
|
|
67
|
+
ErrorCode2["CONTEXT_OVERFLOW"] = "CONTEXT_OVERFLOW";
|
|
68
|
+
ErrorCode2["INVALID_INPUT"] = "INVALID_INPUT";
|
|
69
|
+
ErrorCode2["INVALID_RESPONSE"] = "INVALID_RESPONSE";
|
|
70
|
+
ErrorCode2["REENTRANCY"] = "REENTRANCY";
|
|
71
|
+
ErrorCode2["DISPOSED"] = "DISPOSED";
|
|
72
|
+
ErrorCode2["ABORTED"] = "ABORTED";
|
|
73
|
+
ErrorCode2["INVALID_TRANSITION"] = "INVALID_TRANSITION";
|
|
74
|
+
ErrorCode2["DEPENDENCY_MISSING"] = "DEPENDENCY_MISSING";
|
|
75
|
+
ErrorCode2["BACKEND_NOT_INSTALLED"] = "BACKEND_NOT_INSTALLED";
|
|
76
|
+
ErrorCode2["TOOL_EXECUTION"] = "TOOL_EXECUTION";
|
|
77
|
+
ErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
78
|
+
ErrorCode2["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
|
|
79
|
+
ErrorCode2["SESSION_EXPIRED"] = "SESSION_EXPIRED";
|
|
80
|
+
ErrorCode2["PROVIDER_NOT_FOUND"] = "PROVIDER_NOT_FOUND";
|
|
81
|
+
ErrorCode2["AUTH_REQUIRED"] = "AUTH_REQUIRED";
|
|
82
|
+
ErrorCode2["STORAGE_ERROR"] = "STORAGE_ERROR";
|
|
83
|
+
ErrorCode2["STORAGE_NOT_FOUND"] = "STORAGE_NOT_FOUND";
|
|
84
|
+
ErrorCode2["STORAGE_DUPLICATE_KEY"] = "STORAGE_DUPLICATE_KEY";
|
|
85
|
+
ErrorCode2["STORAGE_IO_ERROR"] = "STORAGE_IO_ERROR";
|
|
86
|
+
ErrorCode2["STORAGE_SERIALIZATION_ERROR"] = "STORAGE_SERIALIZATION_ERROR";
|
|
87
|
+
return ErrorCode2;
|
|
88
|
+
})(ErrorCode || {});
|
|
89
|
+
RECOVERABLE_CODES = /* @__PURE__ */ new Set([
|
|
90
|
+
"TIMEOUT" /* TIMEOUT */,
|
|
91
|
+
"RATE_LIMIT" /* RATE_LIMIT */,
|
|
92
|
+
"NETWORK" /* NETWORK */,
|
|
93
|
+
"TOOL_EXECUTION" /* TOOL_EXECUTION */,
|
|
94
|
+
"MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
|
|
95
|
+
"PROVIDER_ERROR" /* PROVIDER_ERROR */
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// src/errors.ts
|
|
101
|
+
var AgentSDKError, ReentrancyError, DisposedError, SubprocessError, DependencyError, AbortError, ToolExecutionError, ActivityTimeoutError;
|
|
102
|
+
var init_errors2 = __esm({
|
|
31
103
|
"src/errors.ts"() {
|
|
104
|
+
init_errors();
|
|
32
105
|
AgentSDKError = class extends Error {
|
|
33
106
|
/** @internal Marker for cross-bundle identity checks */
|
|
34
107
|
_agentSDKError = true;
|
|
108
|
+
/** Machine-readable error code. Prefer values from the ErrorCode enum. */
|
|
109
|
+
code;
|
|
110
|
+
/** Whether this error is safe to retry */
|
|
111
|
+
retryable;
|
|
112
|
+
/** HTTP status code hint for error classification */
|
|
113
|
+
httpStatus;
|
|
35
114
|
constructor(message, options) {
|
|
36
115
|
super(message, options);
|
|
37
116
|
this.name = "AgentSDKError";
|
|
117
|
+
this.code = options?.code;
|
|
118
|
+
this.retryable = options?.retryable ?? false;
|
|
119
|
+
this.httpStatus = options?.httpStatus;
|
|
38
120
|
}
|
|
39
121
|
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
40
122
|
static is(error) {
|
|
@@ -43,83 +125,84 @@ var init_errors = __esm({
|
|
|
43
125
|
};
|
|
44
126
|
ReentrancyError = class extends AgentSDKError {
|
|
45
127
|
constructor() {
|
|
46
|
-
super("Agent is already running. Await the current run before starting another."
|
|
128
|
+
super("Agent is already running. Await the current run before starting another.", {
|
|
129
|
+
code: "REENTRANCY" /* REENTRANCY */
|
|
130
|
+
});
|
|
47
131
|
this.name = "ReentrancyError";
|
|
48
132
|
}
|
|
49
133
|
};
|
|
50
134
|
DisposedError = class extends AgentSDKError {
|
|
51
135
|
constructor(entity) {
|
|
52
|
-
super(`${entity} has been disposed and cannot be used
|
|
136
|
+
super(`${entity} has been disposed and cannot be used.`, {
|
|
137
|
+
code: "DISPOSED" /* DISPOSED */
|
|
138
|
+
});
|
|
53
139
|
this.name = "DisposedError";
|
|
54
140
|
}
|
|
55
141
|
};
|
|
56
|
-
BackendNotFoundError = class extends AgentSDKError {
|
|
57
|
-
constructor(backend) {
|
|
58
|
-
super(
|
|
59
|
-
`Unknown backend: "${backend}". Built-in: copilot, claude, vercel-ai. Custom: use registerBackend() first.`
|
|
60
|
-
);
|
|
61
|
-
this.name = "BackendNotFoundError";
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
BackendAlreadyRegisteredError = class extends AgentSDKError {
|
|
65
|
-
constructor(backend) {
|
|
66
|
-
super(`Backend "${backend}" is already registered. Use a different name or unregister first.`);
|
|
67
|
-
this.name = "BackendAlreadyRegisteredError";
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
142
|
SubprocessError = class extends AgentSDKError {
|
|
71
143
|
constructor(message, options) {
|
|
72
|
-
super(message, options);
|
|
144
|
+
super(message, { ...options, code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */ });
|
|
73
145
|
this.name = "SubprocessError";
|
|
74
146
|
}
|
|
75
147
|
};
|
|
76
148
|
DependencyError = class extends AgentSDKError {
|
|
77
149
|
packageName;
|
|
78
150
|
constructor(packageName) {
|
|
79
|
-
super(`${packageName} is not installed. Install it: npm install ${packageName}
|
|
151
|
+
super(`${packageName} is not installed. Install it: npm install ${packageName}`, {
|
|
152
|
+
code: "DEPENDENCY_MISSING" /* DEPENDENCY_MISSING */
|
|
153
|
+
});
|
|
80
154
|
this.name = "DependencyError";
|
|
81
155
|
this.packageName = packageName;
|
|
82
156
|
}
|
|
83
157
|
};
|
|
84
158
|
AbortError = class extends AgentSDKError {
|
|
85
159
|
constructor() {
|
|
86
|
-
super("Agent run was aborted.");
|
|
160
|
+
super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
|
|
87
161
|
this.name = "AbortError";
|
|
88
162
|
}
|
|
89
163
|
};
|
|
90
164
|
ToolExecutionError = class extends AgentSDKError {
|
|
91
165
|
toolName;
|
|
92
166
|
constructor(toolName, message, options) {
|
|
93
|
-
super(`Tool "${toolName}" failed: ${message}`, options);
|
|
167
|
+
super(`Tool "${toolName}" failed: ${message}`, { ...options, code: "TOOL_EXECUTION" /* TOOL_EXECUTION */ });
|
|
94
168
|
this.name = "ToolExecutionError";
|
|
95
169
|
this.toolName = toolName;
|
|
96
170
|
}
|
|
97
171
|
};
|
|
98
|
-
|
|
99
|
-
constructor(
|
|
100
|
-
super(`
|
|
101
|
-
|
|
172
|
+
ActivityTimeoutError = class extends AgentSDKError {
|
|
173
|
+
constructor(timeoutMs) {
|
|
174
|
+
super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
|
|
175
|
+
code: "TIMEOUT" /* TIMEOUT */,
|
|
176
|
+
retryable: true
|
|
177
|
+
});
|
|
178
|
+
this.name = "ActivityTimeoutError";
|
|
102
179
|
}
|
|
103
180
|
};
|
|
104
181
|
}
|
|
105
182
|
});
|
|
106
183
|
|
|
107
|
-
// src/types.ts
|
|
108
|
-
function isToolDefinition(tool) {
|
|
109
|
-
return "execute" in tool && typeof tool.execute === "function";
|
|
110
|
-
}
|
|
111
|
-
function isTextContent(content) {
|
|
112
|
-
return typeof content === "string";
|
|
113
|
-
}
|
|
114
|
-
function isMultiPartContent(content) {
|
|
115
|
-
return Array.isArray(content);
|
|
116
|
-
}
|
|
184
|
+
// src/types/guards.ts
|
|
117
185
|
function getTextContent(content) {
|
|
118
186
|
if (typeof content === "string") return content;
|
|
119
187
|
return content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
120
188
|
}
|
|
189
|
+
var init_guards = __esm({
|
|
190
|
+
"src/types/guards.ts"() {
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// src/types/index.ts
|
|
121
195
|
var init_types = __esm({
|
|
196
|
+
"src/types/index.ts"() {
|
|
197
|
+
init_errors();
|
|
198
|
+
init_guards();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// src/types.ts
|
|
203
|
+
var init_types2 = __esm({
|
|
122
204
|
"src/types.ts"() {
|
|
205
|
+
init_types();
|
|
123
206
|
}
|
|
124
207
|
});
|
|
125
208
|
|
|
@@ -127,12 +210,15 @@ var init_types = __esm({
|
|
|
127
210
|
var BaseAgent;
|
|
128
211
|
var init_base_agent = __esm({
|
|
129
212
|
"src/base-agent.ts"() {
|
|
213
|
+
init_errors2();
|
|
214
|
+
init_errors2();
|
|
130
215
|
init_errors();
|
|
131
216
|
BaseAgent = class {
|
|
132
217
|
state = "idle";
|
|
133
218
|
abortController = null;
|
|
134
219
|
config;
|
|
135
220
|
_cleanupExternalSignal = null;
|
|
221
|
+
_streamMiddleware = [];
|
|
136
222
|
/** CLI session ID for persistent mode. Override in backends that support it. */
|
|
137
223
|
get sessionId() {
|
|
138
224
|
return void 0;
|
|
@@ -148,8 +234,11 @@ var init_base_agent = __esm({
|
|
|
148
234
|
this.state = "running";
|
|
149
235
|
try {
|
|
150
236
|
const messages = [{ role: "user", content: prompt }];
|
|
151
|
-
const result = await this.
|
|
152
|
-
|
|
237
|
+
const result = await this.withRetry(
|
|
238
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
239
|
+
options
|
|
240
|
+
);
|
|
241
|
+
this.enrichAndNotifyUsage(result, options);
|
|
153
242
|
return result;
|
|
154
243
|
} finally {
|
|
155
244
|
this.cleanupRun();
|
|
@@ -161,8 +250,11 @@ var init_base_agent = __esm({
|
|
|
161
250
|
const ac = this.createAbortController(options?.signal);
|
|
162
251
|
this.state = "running";
|
|
163
252
|
try {
|
|
164
|
-
const result = await this.
|
|
165
|
-
|
|
253
|
+
const result = await this.withRetry(
|
|
254
|
+
() => this.executeRun(messages, options, ac.signal),
|
|
255
|
+
options
|
|
256
|
+
);
|
|
257
|
+
this.enrichAndNotifyUsage(result, options);
|
|
166
258
|
return result;
|
|
167
259
|
} finally {
|
|
168
260
|
this.cleanupRun();
|
|
@@ -175,13 +267,11 @@ var init_base_agent = __esm({
|
|
|
175
267
|
this.state = "running";
|
|
176
268
|
try {
|
|
177
269
|
const messages = [{ role: "user", content: prompt }];
|
|
178
|
-
const result = await this.
|
|
179
|
-
messages,
|
|
180
|
-
|
|
181
|
-
options,
|
|
182
|
-
ac.signal
|
|
270
|
+
const result = await this.withRetry(
|
|
271
|
+
() => this.executeRunStructured(messages, schema, options, ac.signal),
|
|
272
|
+
options
|
|
183
273
|
);
|
|
184
|
-
this.enrichAndNotifyUsage(result);
|
|
274
|
+
this.enrichAndNotifyUsage(result, options);
|
|
185
275
|
return result;
|
|
186
276
|
} finally {
|
|
187
277
|
this.cleanupRun();
|
|
@@ -194,8 +284,10 @@ var init_base_agent = __esm({
|
|
|
194
284
|
this.state = "streaming";
|
|
195
285
|
try {
|
|
196
286
|
const messages = [{ role: "user", content: prompt }];
|
|
197
|
-
|
|
198
|
-
|
|
287
|
+
yield* this.streamWithRetry(
|
|
288
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
289
|
+
options
|
|
290
|
+
);
|
|
199
291
|
} finally {
|
|
200
292
|
this.cleanupRun();
|
|
201
293
|
}
|
|
@@ -206,12 +298,37 @@ var init_base_agent = __esm({
|
|
|
206
298
|
const ac = this.createAbortController(options?.signal);
|
|
207
299
|
this.state = "streaming";
|
|
208
300
|
try {
|
|
209
|
-
|
|
210
|
-
|
|
301
|
+
yield* this.streamWithRetry(
|
|
302
|
+
() => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
|
|
303
|
+
options
|
|
304
|
+
);
|
|
211
305
|
} finally {
|
|
212
306
|
this.cleanupRun();
|
|
213
307
|
}
|
|
214
308
|
}
|
|
309
|
+
/** Register a stream middleware. Applied in registration order after built-in transforms. */
|
|
310
|
+
addStreamMiddleware(middleware) {
|
|
311
|
+
this.guardDisposed();
|
|
312
|
+
this._streamMiddleware.push(middleware);
|
|
313
|
+
}
|
|
314
|
+
/** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
|
|
315
|
+
async *applyStreamPipeline(source, options, ac) {
|
|
316
|
+
let stream = this.enrichStream(source, options);
|
|
317
|
+
stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
|
|
318
|
+
stream = this.heartbeatStream(stream);
|
|
319
|
+
if (this._streamMiddleware.length > 0) {
|
|
320
|
+
const ctx = {
|
|
321
|
+
model: options.model,
|
|
322
|
+
backend: this.backendName,
|
|
323
|
+
abortController: ac,
|
|
324
|
+
config: Object.freeze({ ...this.config })
|
|
325
|
+
};
|
|
326
|
+
for (const mw of this._streamMiddleware) {
|
|
327
|
+
stream = mw(stream, ctx);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
yield* stream;
|
|
331
|
+
}
|
|
215
332
|
abort() {
|
|
216
333
|
if (this.abortController) {
|
|
217
334
|
this.abortController.abort();
|
|
@@ -234,26 +351,109 @@ var init_base_agent = __esm({
|
|
|
234
351
|
this.abort();
|
|
235
352
|
this.state = "disposed";
|
|
236
353
|
}
|
|
354
|
+
// ─── Retry Logic ─────────────────────────────────────────────
|
|
355
|
+
/** Check if an error should be retried given the retry configuration. */
|
|
356
|
+
isRetryableError(error, retry) {
|
|
357
|
+
if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
if (AgentSDKError.is(error)) {
|
|
361
|
+
if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
|
|
362
|
+
return retry.retryableErrors.includes(error.code);
|
|
363
|
+
}
|
|
364
|
+
if (error.retryable) return true;
|
|
365
|
+
if (error.code) return isRecoverableErrorCode(error.code);
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
/** Execute a function with retry logic per RetryConfig. */
|
|
370
|
+
async withRetry(fn, options) {
|
|
371
|
+
const retry = options?.retry;
|
|
372
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
373
|
+
return fn();
|
|
374
|
+
}
|
|
375
|
+
const maxRetries = retry.maxRetries;
|
|
376
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
377
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
378
|
+
let lastError;
|
|
379
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
380
|
+
try {
|
|
381
|
+
return await fn();
|
|
382
|
+
} catch (err) {
|
|
383
|
+
lastError = err;
|
|
384
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
385
|
+
throw err;
|
|
386
|
+
}
|
|
387
|
+
const delay2 = initialDelay * Math.pow(multiplier, attempt);
|
|
388
|
+
await new Promise((resolve) => setTimeout(resolve, delay2));
|
|
389
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
390
|
+
throw err;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
throw lastError;
|
|
395
|
+
}
|
|
396
|
+
/** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
|
|
397
|
+
async *streamWithRetry(factory, options) {
|
|
398
|
+
const retry = options?.retry;
|
|
399
|
+
if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
|
|
400
|
+
yield* factory();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const maxRetries = retry.maxRetries;
|
|
404
|
+
const initialDelay = retry.initialDelayMs ?? 1e3;
|
|
405
|
+
const multiplier = retry.backoffMultiplier ?? 2;
|
|
406
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
407
|
+
try {
|
|
408
|
+
const stream = factory();
|
|
409
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
410
|
+
const first = await iterator.next();
|
|
411
|
+
if (first.done) return;
|
|
412
|
+
yield first.value;
|
|
413
|
+
while (true) {
|
|
414
|
+
const next = await iterator.next();
|
|
415
|
+
if (next.done) break;
|
|
416
|
+
yield next.value;
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
} catch (err) {
|
|
420
|
+
if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
|
|
421
|
+
throw err;
|
|
422
|
+
}
|
|
423
|
+
const delay2 = initialDelay * Math.pow(multiplier, attempt);
|
|
424
|
+
await new Promise((resolve) => setTimeout(resolve, delay2));
|
|
425
|
+
if (options?.signal?.aborted || this.abortController?.signal.aborted) {
|
|
426
|
+
throw err;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// ─── CallOptions Resolution ──────────────────────────────────
|
|
432
|
+
/** Resolve tools to use for this call (per-call override > config default) */
|
|
433
|
+
resolveTools(options) {
|
|
434
|
+
return options?.tools ?? this.config.tools ?? [];
|
|
435
|
+
}
|
|
237
436
|
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
238
437
|
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
239
|
-
enrichAndNotifyUsage(result) {
|
|
438
|
+
enrichAndNotifyUsage(result, options) {
|
|
240
439
|
if (result.usage) {
|
|
241
440
|
result.usage = {
|
|
242
441
|
...result.usage,
|
|
243
|
-
model:
|
|
442
|
+
model: options.model,
|
|
244
443
|
backend: this.backendName
|
|
245
444
|
};
|
|
246
445
|
this.callOnUsage(result.usage);
|
|
247
446
|
}
|
|
248
447
|
}
|
|
249
448
|
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
250
|
-
async *enrichStream(source) {
|
|
449
|
+
async *enrichStream(source, options) {
|
|
450
|
+
const model = options.model;
|
|
251
451
|
for await (const event of source) {
|
|
252
452
|
if (event.type === "usage_update") {
|
|
253
453
|
const usage = {
|
|
254
454
|
promptTokens: event.promptTokens,
|
|
255
455
|
completionTokens: event.completionTokens,
|
|
256
|
-
model
|
|
456
|
+
model,
|
|
257
457
|
backend: this.backendName
|
|
258
458
|
};
|
|
259
459
|
this.callOnUsage(usage);
|
|
@@ -289,9 +489,9 @@ var init_base_agent = __esm({
|
|
|
289
489
|
let heartbeatResolve = null;
|
|
290
490
|
const timer = setInterval(() => {
|
|
291
491
|
if (heartbeatResolve) {
|
|
292
|
-
const
|
|
492
|
+
const resolve = heartbeatResolve;
|
|
293
493
|
heartbeatResolve = null;
|
|
294
|
-
|
|
494
|
+
resolve();
|
|
295
495
|
}
|
|
296
496
|
}, interval);
|
|
297
497
|
try {
|
|
@@ -299,8 +499,8 @@ var init_base_agent = __esm({
|
|
|
299
499
|
if (!pendingEvent) {
|
|
300
500
|
pendingEvent = iterator.next();
|
|
301
501
|
}
|
|
302
|
-
const heartbeatPromise = new Promise((
|
|
303
|
-
heartbeatResolve =
|
|
502
|
+
const heartbeatPromise = new Promise((resolve) => {
|
|
503
|
+
heartbeatResolve = resolve;
|
|
304
504
|
});
|
|
305
505
|
const eventDone = pendingEvent.then(
|
|
306
506
|
(r) => ({ kind: "event", result: r })
|
|
@@ -323,6 +523,35 @@ var init_base_agent = __esm({
|
|
|
323
523
|
heartbeatResolve = null;
|
|
324
524
|
}
|
|
325
525
|
}
|
|
526
|
+
// ─── Activity Timeout ────────────────────────────────────────
|
|
527
|
+
/** Wrap a stream to abort on inactivity. Resets timer on every event.
|
|
528
|
+
* When timeoutMs is not set, passes through directly. */
|
|
529
|
+
async *activityTimeoutStream(source, timeoutMs, ac) {
|
|
530
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
531
|
+
yield* source;
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
535
|
+
let timerId;
|
|
536
|
+
try {
|
|
537
|
+
while (true) {
|
|
538
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
539
|
+
timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
|
|
540
|
+
});
|
|
541
|
+
const result = await Promise.race([iterator.next(), timeoutPromise]);
|
|
542
|
+
clearTimeout(timerId);
|
|
543
|
+
if (result.done) break;
|
|
544
|
+
yield result.value;
|
|
545
|
+
}
|
|
546
|
+
} catch (err) {
|
|
547
|
+
if (err instanceof ActivityTimeoutError) {
|
|
548
|
+
ac.abort(err);
|
|
549
|
+
}
|
|
550
|
+
throw err;
|
|
551
|
+
} finally {
|
|
552
|
+
clearTimeout(timerId);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
326
555
|
// ─── Guards ───────────────────────────────────────────────────
|
|
327
556
|
guardReentrancy() {
|
|
328
557
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -427,6 +656,66 @@ var init_schema = __esm({
|
|
|
427
656
|
}
|
|
428
657
|
});
|
|
429
658
|
|
|
659
|
+
// src/backends/shared.ts
|
|
660
|
+
function extractLastUserPrompt(messages) {
|
|
661
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
662
|
+
const msg = messages[i];
|
|
663
|
+
if (msg.role === "user") {
|
|
664
|
+
return getTextContent(msg.content);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return "";
|
|
668
|
+
}
|
|
669
|
+
function serializeToolCall(tc) {
|
|
670
|
+
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
671
|
+
return ` Tool call: ${tc.name}(${args})`;
|
|
672
|
+
}
|
|
673
|
+
function serializeToolResult(tr) {
|
|
674
|
+
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
675
|
+
const prefix = tr.isError ? "[ERROR] " : "";
|
|
676
|
+
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
677
|
+
}
|
|
678
|
+
function buildContextualPrompt(messages) {
|
|
679
|
+
if (messages.length <= 1) {
|
|
680
|
+
return extractLastUserPrompt(messages);
|
|
681
|
+
}
|
|
682
|
+
const history = messages.slice(0, -1).map((msg) => {
|
|
683
|
+
if (msg.role === "user") {
|
|
684
|
+
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
685
|
+
}
|
|
686
|
+
if (msg.role === "tool" && msg.toolResults) {
|
|
687
|
+
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
688
|
+
return `Tool results:
|
|
689
|
+
${results}`;
|
|
690
|
+
}
|
|
691
|
+
if (msg.role === "assistant") {
|
|
692
|
+
const parts = [];
|
|
693
|
+
const thinking = msg.thinking;
|
|
694
|
+
if (thinking) {
|
|
695
|
+
parts.push(`[reasoning: ${thinking}]`);
|
|
696
|
+
}
|
|
697
|
+
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
698
|
+
if (text2) parts.push(text2);
|
|
699
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
700
|
+
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
701
|
+
}
|
|
702
|
+
return `Assistant: ${parts.join("\n")}`;
|
|
703
|
+
}
|
|
704
|
+
const text = msg.content ? getTextContent(msg.content) : "";
|
|
705
|
+
return `${msg.role}: ${text}`;
|
|
706
|
+
}).join("\n");
|
|
707
|
+
const lastPrompt = extractLastUserPrompt(messages);
|
|
708
|
+
return `Conversation history:
|
|
709
|
+
${history}
|
|
710
|
+
|
|
711
|
+
User: ${lastPrompt}`;
|
|
712
|
+
}
|
|
713
|
+
var init_shared = __esm({
|
|
714
|
+
"src/backends/shared.ts"() {
|
|
715
|
+
init_types2();
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
|
|
430
719
|
// src/backends/copilot.ts
|
|
431
720
|
var copilot_exports = {};
|
|
432
721
|
__export(copilot_exports, {
|
|
@@ -435,10 +724,9 @@ __export(copilot_exports, {
|
|
|
435
724
|
createCopilotService: () => createCopilotService
|
|
436
725
|
});
|
|
437
726
|
async function loadSDK() {
|
|
438
|
-
if (
|
|
727
|
+
if (_sdkMock) return _sdkMock;
|
|
439
728
|
try {
|
|
440
|
-
|
|
441
|
-
return sdkModule;
|
|
729
|
+
return await import('@github/copilot-sdk');
|
|
442
730
|
} catch {
|
|
443
731
|
throw new SubprocessError(
|
|
444
732
|
"@github/copilot-sdk is not installed. Install it: npm install @github/copilot-sdk"
|
|
@@ -446,10 +734,10 @@ async function loadSDK() {
|
|
|
446
734
|
}
|
|
447
735
|
}
|
|
448
736
|
function _injectSDK(mock) {
|
|
449
|
-
|
|
737
|
+
_sdkMock = mock;
|
|
450
738
|
}
|
|
451
739
|
function _resetSDK() {
|
|
452
|
-
|
|
740
|
+
_sdkMock = null;
|
|
453
741
|
}
|
|
454
742
|
function mapToolsToSDK(tools) {
|
|
455
743
|
return tools.map((tool) => ({
|
|
@@ -469,17 +757,6 @@ function convertParameters(params) {
|
|
|
469
757
|
}
|
|
470
758
|
return params;
|
|
471
759
|
}
|
|
472
|
-
async function mapToolsToSDKAsync(tools) {
|
|
473
|
-
return tools.map((tool) => ({
|
|
474
|
-
name: tool.name,
|
|
475
|
-
description: tool.description,
|
|
476
|
-
parameters: convertParameters(tool.parameters),
|
|
477
|
-
handler: async (args) => {
|
|
478
|
-
const result = await tool.execute(args);
|
|
479
|
-
return typeof result === "string" ? result : JSON.stringify(result);
|
|
480
|
-
}
|
|
481
|
-
}));
|
|
482
|
-
}
|
|
483
760
|
function buildPermissionHandler(config) {
|
|
484
761
|
const onPermission = config.supervisor?.onPermission;
|
|
485
762
|
if (!onPermission) {
|
|
@@ -494,6 +771,7 @@ function buildPermissionHandler(config) {
|
|
|
494
771
|
const unifiedRequest = {
|
|
495
772
|
toolName,
|
|
496
773
|
toolArgs: { ...request },
|
|
774
|
+
toolCallId: request.toolCallId,
|
|
497
775
|
rawSDKRequest: request
|
|
498
776
|
};
|
|
499
777
|
const ac = new AbortController();
|
|
@@ -598,15 +876,21 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
|
|
|
598
876
|
};
|
|
599
877
|
case "session.error":
|
|
600
878
|
console.error("[copilot] mapSessionEvent error:", JSON.stringify(data));
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
879
|
+
{
|
|
880
|
+
const errorMsg = String(data.message ?? "Unknown error");
|
|
881
|
+
const code = classifyAgentError(errorMsg);
|
|
882
|
+
return {
|
|
883
|
+
type: "error",
|
|
884
|
+
error: errorMsg,
|
|
885
|
+
recoverable: isRecoverableErrorCode(code),
|
|
886
|
+
code
|
|
887
|
+
};
|
|
888
|
+
}
|
|
606
889
|
case "assistant.message": {
|
|
607
890
|
const doneEvent = {
|
|
608
891
|
type: "done",
|
|
609
|
-
finalOutput:
|
|
892
|
+
finalOutput: null,
|
|
893
|
+
streamed: true
|
|
610
894
|
};
|
|
611
895
|
if (thinkingTracker.endThinking()) {
|
|
612
896
|
return [{ type: "thinking_end" }, doneEvent];
|
|
@@ -617,66 +901,13 @@ function mapSessionEvent(event, tracker, thinkingTracker) {
|
|
|
617
901
|
return null;
|
|
618
902
|
}
|
|
619
903
|
}
|
|
620
|
-
function extractLastUserPrompt(messages) {
|
|
621
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
622
|
-
const msg = messages[i];
|
|
623
|
-
if (msg.role === "user") {
|
|
624
|
-
return getTextContent(msg.content);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
return "";
|
|
628
|
-
}
|
|
629
|
-
function serializeToolCall(tc) {
|
|
630
|
-
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
631
|
-
return ` Tool call: ${tc.name}(${args})`;
|
|
632
|
-
}
|
|
633
|
-
function serializeToolResult(tr) {
|
|
634
|
-
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
635
|
-
const prefix = tr.isError ? "[ERROR] " : "";
|
|
636
|
-
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
637
|
-
}
|
|
638
|
-
function buildContextualPrompt(messages) {
|
|
639
|
-
if (messages.length <= 1) {
|
|
640
|
-
return extractLastUserPrompt(messages);
|
|
641
|
-
}
|
|
642
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
643
|
-
if (msg.role === "user") {
|
|
644
|
-
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
645
|
-
}
|
|
646
|
-
if (msg.role === "tool" && msg.toolResults) {
|
|
647
|
-
const results = msg.toolResults.map(serializeToolResult).join("\n");
|
|
648
|
-
return `Tool results:
|
|
649
|
-
${results}`;
|
|
650
|
-
}
|
|
651
|
-
if (msg.role === "assistant") {
|
|
652
|
-
const parts = [];
|
|
653
|
-
const thinking = msg.thinking;
|
|
654
|
-
if (thinking) {
|
|
655
|
-
parts.push(`[reasoning: ${thinking}]`);
|
|
656
|
-
}
|
|
657
|
-
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
658
|
-
if (text2) parts.push(text2);
|
|
659
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
660
|
-
parts.push(msg.toolCalls.map(serializeToolCall).join("\n"));
|
|
661
|
-
}
|
|
662
|
-
return `Assistant: ${parts.join("\n")}`;
|
|
663
|
-
}
|
|
664
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
665
|
-
return `${msg.role}: ${text}`;
|
|
666
|
-
}).join("\n");
|
|
667
|
-
const lastPrompt = extractLastUserPrompt(messages);
|
|
668
|
-
return `Conversation history:
|
|
669
|
-
${history}
|
|
670
|
-
|
|
671
|
-
User: ${lastPrompt}`;
|
|
672
|
-
}
|
|
673
904
|
function withTimeout(promise, ms, message) {
|
|
674
|
-
return new Promise((
|
|
905
|
+
return new Promise((resolve, reject) => {
|
|
675
906
|
const timer = setTimeout(() => reject(new SubprocessError(message)), ms);
|
|
676
907
|
promise.then(
|
|
677
908
|
(val) => {
|
|
678
909
|
clearTimeout(timer);
|
|
679
|
-
|
|
910
|
+
resolve(val);
|
|
680
911
|
},
|
|
681
912
|
(err) => {
|
|
682
913
|
clearTimeout(timer);
|
|
@@ -688,14 +919,15 @@ function withTimeout(promise, ms, message) {
|
|
|
688
919
|
function createCopilotService(options) {
|
|
689
920
|
return new CopilotAgentService(options);
|
|
690
921
|
}
|
|
691
|
-
var
|
|
922
|
+
var _sdkMock, ToolCallTracker, ThinkingTracker, CopilotAgent, CopilotAgentService;
|
|
692
923
|
var init_copilot = __esm({
|
|
693
924
|
"src/backends/copilot.ts"() {
|
|
694
|
-
|
|
925
|
+
init_types2();
|
|
695
926
|
init_base_agent();
|
|
696
|
-
|
|
927
|
+
init_errors2();
|
|
697
928
|
init_schema();
|
|
698
|
-
|
|
929
|
+
init_shared();
|
|
930
|
+
_sdkMock = null;
|
|
699
931
|
ToolCallTracker = class {
|
|
700
932
|
map = /* @__PURE__ */ new Map();
|
|
701
933
|
trackStart(toolCallId, toolName, args) {
|
|
@@ -743,6 +975,7 @@ var init_copilot = __esm({
|
|
|
743
975
|
isPersistent;
|
|
744
976
|
persistentSession = null;
|
|
745
977
|
_sessionId;
|
|
978
|
+
_persistentModel;
|
|
746
979
|
activeSession = null;
|
|
747
980
|
_resumeSessionId;
|
|
748
981
|
_toolsReady = null;
|
|
@@ -761,15 +994,15 @@ var init_copilot = __esm({
|
|
|
761
994
|
},
|
|
762
995
|
onPermissionRequest: buildPermissionHandler(config),
|
|
763
996
|
onUserInputRequest: buildUserInputHandler(config),
|
|
764
|
-
...config.availableTools
|
|
997
|
+
...config.availableTools ? { availableTools: config.availableTools } : {}
|
|
765
998
|
};
|
|
766
999
|
this._toolsReady = this._initToolsAsync(config);
|
|
767
1000
|
this._resumeSessionId = resumeSessionId;
|
|
768
1001
|
}
|
|
769
|
-
/** Pre-convert Zod schemas to JSON Schema
|
|
1002
|
+
/** Pre-convert Zod schemas to JSON Schema.
|
|
770
1003
|
* Updates sdkTools and sessionConfig.tools before first session creation. */
|
|
771
1004
|
async _initToolsAsync(config) {
|
|
772
|
-
this.sdkTools =
|
|
1005
|
+
this.sdkTools = mapToolsToSDK(config.tools ?? []);
|
|
773
1006
|
this.sessionConfig.tools = this.sdkTools;
|
|
774
1007
|
}
|
|
775
1008
|
get sessionId() {
|
|
@@ -793,47 +1026,63 @@ var init_copilot = __esm({
|
|
|
793
1026
|
});
|
|
794
1027
|
this.persistentSession = null;
|
|
795
1028
|
this._sessionId = void 0;
|
|
1029
|
+
this._persistentModel = void 0;
|
|
796
1030
|
}
|
|
797
1031
|
}
|
|
798
|
-
async getOrCreateSession(streaming) {
|
|
1032
|
+
async getOrCreateSession(streaming, options) {
|
|
799
1033
|
if (this.isPersistent && this.persistentSession) {
|
|
800
|
-
|
|
1034
|
+
if (options.model !== this._persistentModel) {
|
|
1035
|
+
this.persistentSession.destroy().catch(() => {
|
|
1036
|
+
});
|
|
1037
|
+
this.persistentSession = null;
|
|
1038
|
+
this._sessionId = void 0;
|
|
1039
|
+
} else {
|
|
1040
|
+
return { session: this.persistentSession, isNew: false };
|
|
1041
|
+
}
|
|
801
1042
|
}
|
|
802
1043
|
if (this._toolsReady) {
|
|
803
1044
|
await this._toolsReady;
|
|
804
1045
|
this._toolsReady = null;
|
|
805
1046
|
}
|
|
1047
|
+
const sessionConfig = { ...this.sessionConfig };
|
|
1048
|
+
sessionConfig.model = options.model;
|
|
1049
|
+
const resolvedTools = this.resolveTools(options);
|
|
1050
|
+
if (options?.tools) {
|
|
1051
|
+
sessionConfig.tools = mapToolsToSDK(resolvedTools);
|
|
1052
|
+
}
|
|
806
1053
|
const client = await this.getClient();
|
|
807
1054
|
if (this._resumeSessionId) {
|
|
808
1055
|
const storedId = this._resumeSessionId;
|
|
809
1056
|
this._resumeSessionId = void 0;
|
|
810
1057
|
try {
|
|
811
1058
|
const session2 = await client.resumeSession(storedId, {
|
|
812
|
-
...
|
|
1059
|
+
...sessionConfig,
|
|
813
1060
|
streaming: this.isPersistent ? true : streaming
|
|
814
1061
|
});
|
|
815
1062
|
if (this.isPersistent) {
|
|
816
1063
|
this.persistentSession = session2;
|
|
817
1064
|
this._sessionId = session2.sessionId;
|
|
1065
|
+
this._persistentModel = options.model;
|
|
818
1066
|
}
|
|
819
1067
|
return { session: session2, isNew: false };
|
|
820
1068
|
} catch {
|
|
821
1069
|
}
|
|
822
1070
|
}
|
|
823
1071
|
const session = await client.createSession({
|
|
824
|
-
...
|
|
1072
|
+
...sessionConfig,
|
|
825
1073
|
streaming: this.isPersistent ? true : streaming
|
|
826
1074
|
});
|
|
827
1075
|
if (this.isPersistent) {
|
|
828
1076
|
this.persistentSession = session;
|
|
829
1077
|
this._sessionId = session.sessionId;
|
|
1078
|
+
this._persistentModel = options.model;
|
|
830
1079
|
}
|
|
831
1080
|
return { session, isNew: true };
|
|
832
1081
|
}
|
|
833
1082
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
834
|
-
async executeRun(messages,
|
|
1083
|
+
async executeRun(messages, options, signal) {
|
|
835
1084
|
this.checkAbort(signal);
|
|
836
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(false);
|
|
1085
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(false, options);
|
|
837
1086
|
this.activeSession = session;
|
|
838
1087
|
const prompt = this.isPersistent && !isNewSession ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
839
1088
|
const tracker = new ToolCallTracker();
|
|
@@ -930,9 +1179,9 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
930
1179
|
};
|
|
931
1180
|
}
|
|
932
1181
|
// ─── executeStream ──────────────────────────────────────────────
|
|
933
|
-
async *executeStream(messages,
|
|
1182
|
+
async *executeStream(messages, options, signal) {
|
|
934
1183
|
this.checkAbort(signal);
|
|
935
|
-
const { session, isNew: isNewSession } = await this.getOrCreateSession(true);
|
|
1184
|
+
const { session, isNew: isNewSession } = await this.getOrCreateSession(true, options);
|
|
936
1185
|
this.activeSession = session;
|
|
937
1186
|
if (isNewSession) {
|
|
938
1187
|
yield this.emitSessionInfo(session.sessionId);
|
|
@@ -949,8 +1198,8 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
949
1198
|
notify = null;
|
|
950
1199
|
}
|
|
951
1200
|
};
|
|
952
|
-
const waitForItem = () => new Promise((
|
|
953
|
-
notify =
|
|
1201
|
+
const waitForItem = () => new Promise((resolve) => {
|
|
1202
|
+
notify = resolve;
|
|
954
1203
|
});
|
|
955
1204
|
const unsubscribe = session.on((event) => {
|
|
956
1205
|
const mapped = mapSessionEvent(event, tracker, thinkingTracker);
|
|
@@ -1044,7 +1293,11 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
1044
1293
|
githubToken: this.options.githubToken,
|
|
1045
1294
|
useLoggedInUser: this.options.useLoggedInUser ?? !this.options.githubToken,
|
|
1046
1295
|
...this.options.cliArgs ? { cliArgs: this.options.cliArgs } : {},
|
|
1047
|
-
|
|
1296
|
+
env: {
|
|
1297
|
+
...process.env,
|
|
1298
|
+
...this.options.githubToken ? { GITHUB_TOKEN: this.options.githubToken } : {},
|
|
1299
|
+
...this.options.env
|
|
1300
|
+
}
|
|
1048
1301
|
});
|
|
1049
1302
|
const startupTimeout = this.options.startupTimeoutMs ?? 3e4;
|
|
1050
1303
|
await withTimeout(client.start(), startupTimeout, "CLI startup timed out");
|
|
@@ -1078,7 +1331,10 @@ You MUST respond with ONLY valid JSON matching this schema:
|
|
|
1078
1331
|
return models.map((m) => ({
|
|
1079
1332
|
id: m.id,
|
|
1080
1333
|
name: m.name,
|
|
1081
|
-
provider: "copilot"
|
|
1334
|
+
provider: "copilot",
|
|
1335
|
+
...m.capabilities?.limits?.max_context_window_tokens != null && {
|
|
1336
|
+
contextWindow: m.capabilities.limits.max_context_window_tokens
|
|
1337
|
+
}
|
|
1082
1338
|
}));
|
|
1083
1339
|
}
|
|
1084
1340
|
async validate() {
|
|
@@ -1131,10 +1387,9 @@ function stripMcpPrefix(name) {
|
|
|
1131
1387
|
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
1132
1388
|
}
|
|
1133
1389
|
async function loadSDK2() {
|
|
1134
|
-
if (
|
|
1390
|
+
if (_sdkMock2) return _sdkMock2;
|
|
1135
1391
|
try {
|
|
1136
|
-
|
|
1137
|
-
return sdkModule2;
|
|
1392
|
+
return await import('@anthropic-ai/claude-agent-sdk');
|
|
1138
1393
|
} catch {
|
|
1139
1394
|
throw new SubprocessError(
|
|
1140
1395
|
"@anthropic-ai/claude-agent-sdk is not installed. Install it: npm install @anthropic-ai/claude-agent-sdk"
|
|
@@ -1142,13 +1397,32 @@ async function loadSDK2() {
|
|
|
1142
1397
|
}
|
|
1143
1398
|
}
|
|
1144
1399
|
function _injectSDK2(mock) {
|
|
1145
|
-
|
|
1400
|
+
_sdkMock2 = mock;
|
|
1146
1401
|
}
|
|
1147
1402
|
function _resetSDK2() {
|
|
1148
|
-
|
|
1403
|
+
_sdkMock2 = null;
|
|
1404
|
+
}
|
|
1405
|
+
function normalizeAskUserInput(args) {
|
|
1406
|
+
if (typeof args.question === "string") {
|
|
1407
|
+
return {
|
|
1408
|
+
question: args.question,
|
|
1409
|
+
choices: Array.isArray(args.choices) ? args.choices : void 0,
|
|
1410
|
+
allowFreeform: args.allowFreeform !== false
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
const questions = args.questions;
|
|
1414
|
+
if (questions && questions.length > 0) {
|
|
1415
|
+
const first = questions[0];
|
|
1416
|
+
return {
|
|
1417
|
+
question: first.question,
|
|
1418
|
+
choices: first.options?.map((o) => o.label),
|
|
1419
|
+
allowFreeform: true
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
return { question: JSON.stringify(args), allowFreeform: true };
|
|
1149
1423
|
}
|
|
1150
|
-
function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
1151
|
-
if (tools.length === 0) return void 0;
|
|
1424
|
+
function buildMcpServer(sdk, tools, toolResultCapture, onAskUser) {
|
|
1425
|
+
if (tools.length === 0 && !onAskUser) return void 0;
|
|
1152
1426
|
const mcpTools = tools.map((tool) => {
|
|
1153
1427
|
const zodSchema = tool.parameters;
|
|
1154
1428
|
const inputSchema = zodSchema.shape ?? zodToJsonSchema(tool.parameters);
|
|
@@ -1172,6 +1446,39 @@ function buildMcpServer(sdk, tools, toolResultCapture) {
|
|
|
1172
1446
|
}
|
|
1173
1447
|
);
|
|
1174
1448
|
});
|
|
1449
|
+
if (onAskUser) {
|
|
1450
|
+
const askUserTool = sdk.tool(
|
|
1451
|
+
"ask_user",
|
|
1452
|
+
"Ask the user a question and wait for their response",
|
|
1453
|
+
{
|
|
1454
|
+
question: { type: "string", description: "The question to ask the user" },
|
|
1455
|
+
choices: {
|
|
1456
|
+
type: "array",
|
|
1457
|
+
items: { type: "string" },
|
|
1458
|
+
description: "Optional list of choices for multiple choice"
|
|
1459
|
+
},
|
|
1460
|
+
questions: {
|
|
1461
|
+
type: "array",
|
|
1462
|
+
items: {
|
|
1463
|
+
type: "object",
|
|
1464
|
+
properties: {
|
|
1465
|
+
question: { type: "string" },
|
|
1466
|
+
options: { type: "array", items: { type: "object", properties: { label: { type: "string" } } } }
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
description: "Alternative nested question format"
|
|
1470
|
+
}
|
|
1471
|
+
},
|
|
1472
|
+
async (args) => {
|
|
1473
|
+
const normalized = normalizeAskUserInput(args);
|
|
1474
|
+
const response = await onAskUser(normalized, AbortSignal.timeout(3e5));
|
|
1475
|
+
return {
|
|
1476
|
+
content: [{ type: "text", text: response.answer }]
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
);
|
|
1480
|
+
mcpTools.push(askUserTool);
|
|
1481
|
+
}
|
|
1175
1482
|
return sdk.createSdkMcpServer({
|
|
1176
1483
|
name: MCP_SERVER_NAME,
|
|
1177
1484
|
version: "1.0.0",
|
|
@@ -1221,6 +1528,7 @@ function buildCanUseTool(config) {
|
|
|
1221
1528
|
const unifiedRequest = {
|
|
1222
1529
|
toolName,
|
|
1223
1530
|
toolArgs: input,
|
|
1531
|
+
toolCallId: options.toolUseID,
|
|
1224
1532
|
suggestedScope: extractSuggestedScope(options.suggestions),
|
|
1225
1533
|
rawSDKRequest: { toolName, input, ...options }
|
|
1226
1534
|
};
|
|
@@ -1273,6 +1581,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
1273
1581
|
if (block.type === "tool_use") {
|
|
1274
1582
|
const toolCallId = String(block.id ?? "");
|
|
1275
1583
|
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1584
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
1276
1585
|
if (toolCallTracker) {
|
|
1277
1586
|
toolCallTracker.trackStart(toolCallId, toolName);
|
|
1278
1587
|
}
|
|
@@ -1296,6 +1605,7 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
1296
1605
|
case "tool_use_summary": {
|
|
1297
1606
|
const summary = msg.summary;
|
|
1298
1607
|
const toolName = stripMcpPrefix(msg.tool_name ?? "unknown");
|
|
1608
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) return null;
|
|
1299
1609
|
const precedingIds = msg.preceding_tool_use_ids;
|
|
1300
1610
|
let toolCallId = "";
|
|
1301
1611
|
if (precedingIds && precedingIds.length > 0) {
|
|
@@ -1349,10 +1659,13 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
1349
1659
|
}
|
|
1350
1660
|
if (msg.is_error) {
|
|
1351
1661
|
const r = msg;
|
|
1662
|
+
const errorMsg = r.errors?.join("; ") ?? "Unknown error";
|
|
1663
|
+
const code = classifyAgentError(errorMsg);
|
|
1352
1664
|
return {
|
|
1353
1665
|
type: "error",
|
|
1354
|
-
error:
|
|
1355
|
-
recoverable:
|
|
1666
|
+
error: errorMsg,
|
|
1667
|
+
recoverable: isRecoverableErrorCode(code),
|
|
1668
|
+
code
|
|
1356
1669
|
};
|
|
1357
1670
|
}
|
|
1358
1671
|
return null;
|
|
@@ -1361,72 +1674,21 @@ function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
|
1361
1674
|
return null;
|
|
1362
1675
|
}
|
|
1363
1676
|
}
|
|
1364
|
-
function
|
|
1365
|
-
|
|
1366
|
-
const msg = messages[i];
|
|
1367
|
-
if (msg.role === "user") {
|
|
1368
|
-
return getTextContent(msg.content);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
return "";
|
|
1372
|
-
}
|
|
1373
|
-
function serializeToolCall2(tc) {
|
|
1374
|
-
const args = typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args);
|
|
1375
|
-
return ` Tool call: ${tc.name}(${args})`;
|
|
1376
|
-
}
|
|
1377
|
-
function serializeToolResult2(tr) {
|
|
1378
|
-
const result = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
1379
|
-
const prefix = tr.isError ? "[ERROR] " : "";
|
|
1380
|
-
return ` ${tr.name} \u2192 ${prefix}${result}`;
|
|
1677
|
+
function createClaudeService(options) {
|
|
1678
|
+
return new ClaudeAgentService(options);
|
|
1381
1679
|
}
|
|
1382
|
-
|
|
1383
|
-
if (messages.length <= 1) {
|
|
1384
|
-
return extractLastUserPrompt2(messages);
|
|
1385
|
-
}
|
|
1386
|
-
const history = messages.slice(0, -1).map((msg) => {
|
|
1387
|
-
if (msg.role === "user") {
|
|
1388
|
-
return `User: ${msg.content ? getTextContent(msg.content) : ""}`;
|
|
1389
|
-
}
|
|
1390
|
-
if (msg.role === "tool" && msg.toolResults) {
|
|
1391
|
-
const results = msg.toolResults.map(serializeToolResult2).join("\n");
|
|
1392
|
-
return `Tool results:
|
|
1393
|
-
${results}`;
|
|
1394
|
-
}
|
|
1395
|
-
if (msg.role === "assistant") {
|
|
1396
|
-
const parts = [];
|
|
1397
|
-
const thinking = msg.thinking;
|
|
1398
|
-
if (thinking) {
|
|
1399
|
-
parts.push(`[reasoning: ${thinking}]`);
|
|
1400
|
-
}
|
|
1401
|
-
const text2 = msg.content ? getTextContent(msg.content) : "";
|
|
1402
|
-
if (text2) parts.push(text2);
|
|
1403
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
1404
|
-
parts.push(msg.toolCalls.map(serializeToolCall2).join("\n"));
|
|
1405
|
-
}
|
|
1406
|
-
return `Assistant: ${parts.join("\n")}`;
|
|
1407
|
-
}
|
|
1408
|
-
const text = msg.content ? getTextContent(msg.content) : "";
|
|
1409
|
-
return `${msg.role}: ${text}`;
|
|
1410
|
-
}).join("\n");
|
|
1411
|
-
const lastPrompt = extractLastUserPrompt2(messages);
|
|
1412
|
-
return `Conversation history:
|
|
1413
|
-
${history}
|
|
1414
|
-
|
|
1415
|
-
User: ${lastPrompt}`;
|
|
1416
|
-
}
|
|
1417
|
-
function createClaudeService(options) {
|
|
1418
|
-
return new ClaudeAgentService(options);
|
|
1419
|
-
}
|
|
1420
|
-
var MCP_SERVER_NAME, MCP_TOOL_PREFIX, sdkModule2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
|
|
1680
|
+
var MCP_SERVER_NAME, MCP_TOOL_PREFIX, CLAUDE_INTERNAL_TOOL_NAMES, _sdkMock2, ANTHROPIC_MODELS_URL, ANTHROPIC_API_VERSION, ANTHROPIC_OAUTH_BETA, ClaudeToolCallTracker, ClaudeAgent, ClaudeAgentService;
|
|
1421
1681
|
var init_claude = __esm({
|
|
1422
1682
|
"src/backends/claude.ts"() {
|
|
1423
|
-
|
|
1683
|
+
init_types2();
|
|
1424
1684
|
init_base_agent();
|
|
1425
|
-
|
|
1685
|
+
init_errors2();
|
|
1426
1686
|
init_schema();
|
|
1687
|
+
init_shared();
|
|
1427
1688
|
MCP_SERVER_NAME = "agent-sdk-tools";
|
|
1428
1689
|
MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
1429
|
-
|
|
1690
|
+
CLAUDE_INTERNAL_TOOL_NAMES = /* @__PURE__ */ new Set(["AskUserQuestion"]);
|
|
1691
|
+
_sdkMock2 = null;
|
|
1430
1692
|
ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
|
|
1431
1693
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
1432
1694
|
ANTHROPIC_OAUTH_BETA = "oauth-2025-04-20";
|
|
@@ -1471,11 +1733,6 @@ var init_claude = __esm({
|
|
|
1471
1733
|
if (options.resumeSessionId) {
|
|
1472
1734
|
this._sessionId = options.resumeSessionId;
|
|
1473
1735
|
}
|
|
1474
|
-
if (config.supervisor?.onAskUser) {
|
|
1475
|
-
console.warn(
|
|
1476
|
-
"[agent-sdk/claude] supervisor.onAskUser is not supported by the Claude CLI backend. User interaction requests from the model will not be forwarded."
|
|
1477
|
-
);
|
|
1478
|
-
}
|
|
1479
1736
|
}
|
|
1480
1737
|
get sessionId() {
|
|
1481
1738
|
return this._sessionId;
|
|
@@ -1499,12 +1756,12 @@ var init_claude = __esm({
|
|
|
1499
1756
|
const transcriptPath = home ? `${home}/.claude/projects/.session/sessions/${sessionId}/conversation.jsonl` : void 0;
|
|
1500
1757
|
return { type: "session_info", sessionId, transcriptPath, backend: "claude" };
|
|
1501
1758
|
}
|
|
1502
|
-
buildQueryOptions(signal) {
|
|
1759
|
+
buildQueryOptions(signal, options) {
|
|
1503
1760
|
const ac = new AbortController();
|
|
1504
1761
|
signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
1505
1762
|
const opts = {
|
|
1506
1763
|
abortController: ac,
|
|
1507
|
-
model:
|
|
1764
|
+
model: options.model,
|
|
1508
1765
|
maxTurns: this.options.maxTurns,
|
|
1509
1766
|
cwd: this.options.workingDirectory,
|
|
1510
1767
|
pathToClaudeCodeExecutable: this.options.cliPath,
|
|
@@ -1534,25 +1791,85 @@ var init_claude = __esm({
|
|
|
1534
1791
|
return opts;
|
|
1535
1792
|
}
|
|
1536
1793
|
async buildMcpConfig(opts, toolResultCapture) {
|
|
1537
|
-
|
|
1794
|
+
const onAskUser = this.config.supervisor?.onAskUser;
|
|
1795
|
+
if (this.tools.length === 0 && !onAskUser) return opts;
|
|
1538
1796
|
const sdk = await loadSDK2();
|
|
1539
|
-
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture);
|
|
1797
|
+
const mcpServer = buildMcpServer(sdk, this.tools, toolResultCapture, onAskUser);
|
|
1540
1798
|
if (mcpServer) {
|
|
1541
1799
|
opts.mcpServers = {
|
|
1542
1800
|
[MCP_SERVER_NAME]: mcpServer
|
|
1543
1801
|
};
|
|
1544
1802
|
const mcpToolNames = this.tools.map((t) => mcpToolName(t.name));
|
|
1803
|
+
if (onAskUser) {
|
|
1804
|
+
mcpToolNames.push(mcpToolName("ask_user"));
|
|
1805
|
+
}
|
|
1545
1806
|
opts.allowedTools = [...opts.allowedTools ?? [], ...mcpToolNames];
|
|
1546
1807
|
}
|
|
1808
|
+
if (onAskUser) {
|
|
1809
|
+
opts.disallowedTools = [...opts.disallowedTools ?? [], "AskUserQuestion"];
|
|
1810
|
+
}
|
|
1547
1811
|
return opts;
|
|
1548
1812
|
}
|
|
1813
|
+
// ─── Retry Helpers (shared across executeRun/RunStructured/Stream) ──
|
|
1814
|
+
/** Setup a retry query: clear session, rebuild with full history */
|
|
1815
|
+
async prepareRetryQuery(sdk, messages, signal, options, toolResultCapture, modifyOpts) {
|
|
1816
|
+
this.clearPersistentSession();
|
|
1817
|
+
const retryPrompt = buildContextualPrompt(messages);
|
|
1818
|
+
let retryOpts = this.buildQueryOptions(signal, options);
|
|
1819
|
+
toolResultCapture.clear();
|
|
1820
|
+
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1821
|
+
modifyOpts?.(retryOpts);
|
|
1822
|
+
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1823
|
+
this.activeQuery = retryQ;
|
|
1824
|
+
return retryQ;
|
|
1825
|
+
}
|
|
1826
|
+
/** Extract tool_use blocks from an assistant SDK message into toolCalls array */
|
|
1827
|
+
collectToolCallsFromMessage(msg, toolCalls, toolResultCapture) {
|
|
1828
|
+
if (msg.type !== "assistant") return;
|
|
1829
|
+
const betaMessage = msg.message;
|
|
1830
|
+
if (!betaMessage?.content) return;
|
|
1831
|
+
for (const block of betaMessage.content) {
|
|
1832
|
+
if (block.type === "tool_use") {
|
|
1833
|
+
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1834
|
+
if (CLAUDE_INTERNAL_TOOL_NAMES.has(toolName)) continue;
|
|
1835
|
+
toolCalls.push({
|
|
1836
|
+
toolName,
|
|
1837
|
+
args: block.input ?? {},
|
|
1838
|
+
result: toolResultCapture.get(toolName) ?? null,
|
|
1839
|
+
approved: true
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
/** Back-fill tool results from capture map on summary/result messages */
|
|
1845
|
+
backfillToolResults(msg, toolCalls, toolResultCapture) {
|
|
1846
|
+
if (msg.type !== "tool_use_summary" && msg.type !== "result") return;
|
|
1847
|
+
for (const tc of toolCalls) {
|
|
1848
|
+
if (tc.result === null) {
|
|
1849
|
+
const captured = toolResultCapture.get(tc.toolName);
|
|
1850
|
+
if (captured !== void 0) tc.result = captured;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
/** Wrap retry inner loop with shared error handling */
|
|
1855
|
+
async withRetryErrorHandling(signal, fn) {
|
|
1856
|
+
try {
|
|
1857
|
+
return await fn();
|
|
1858
|
+
} catch (retryError) {
|
|
1859
|
+
if (this.isPersistent) this.clearPersistentSession();
|
|
1860
|
+
if (signal.aborted) throw new AbortError();
|
|
1861
|
+
throw retryError;
|
|
1862
|
+
} finally {
|
|
1863
|
+
this.activeQuery = null;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1549
1866
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
1550
|
-
async executeRun(messages,
|
|
1867
|
+
async executeRun(messages, options, signal) {
|
|
1551
1868
|
this.checkAbort(signal);
|
|
1552
1869
|
const sdk = await loadSDK2();
|
|
1553
1870
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
1554
|
-
const prompt = isResuming ?
|
|
1555
|
-
let opts = this.buildQueryOptions(signal);
|
|
1871
|
+
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
1872
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1556
1873
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1557
1874
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
1558
1875
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -1562,30 +1879,8 @@ var init_claude = __esm({
|
|
|
1562
1879
|
let usage;
|
|
1563
1880
|
try {
|
|
1564
1881
|
for await (const msg of q) {
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
if (betaMessage?.content) {
|
|
1568
|
-
for (const block of betaMessage.content) {
|
|
1569
|
-
if (block.type === "tool_use") {
|
|
1570
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1571
|
-
toolCalls.push({
|
|
1572
|
-
toolName,
|
|
1573
|
-
args: block.input ?? {},
|
|
1574
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
1575
|
-
approved: true
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
1582
|
-
for (const tc of toolCalls) {
|
|
1583
|
-
if (tc.result === null) {
|
|
1584
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
1585
|
-
if (captured !== void 0) tc.result = captured;
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1882
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1883
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1589
1884
|
if (msg.type === "result") {
|
|
1590
1885
|
if (msg.subtype === "success") {
|
|
1591
1886
|
const r = msg;
|
|
@@ -1605,41 +1900,13 @@ var init_claude = __esm({
|
|
|
1605
1900
|
} catch (e) {
|
|
1606
1901
|
if (signal.aborted) throw new AbortError();
|
|
1607
1902
|
if (isResuming && this.isPersistent) {
|
|
1608
|
-
this.
|
|
1609
|
-
const retryPrompt = buildContextualPrompt2(messages);
|
|
1610
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
1611
|
-
toolResultCapture.clear();
|
|
1612
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1613
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1614
|
-
this.activeQuery = retryQ;
|
|
1903
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1615
1904
|
toolCalls.length = 0;
|
|
1616
1905
|
output = null;
|
|
1617
|
-
|
|
1906
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1618
1907
|
for await (const msg of retryQ) {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
if (betaMessage?.content) {
|
|
1622
|
-
for (const block of betaMessage.content) {
|
|
1623
|
-
if (block.type === "tool_use") {
|
|
1624
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1625
|
-
toolCalls.push({
|
|
1626
|
-
toolName,
|
|
1627
|
-
args: block.input ?? {},
|
|
1628
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
1629
|
-
approved: true
|
|
1630
|
-
});
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
1636
|
-
for (const tc of toolCalls) {
|
|
1637
|
-
if (tc.result === null) {
|
|
1638
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
1639
|
-
if (captured !== void 0) tc.result = captured;
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1908
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1909
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1643
1910
|
if (msg.type === "result") {
|
|
1644
1911
|
if (msg.subtype === "success") {
|
|
1645
1912
|
const r = msg;
|
|
@@ -1656,23 +1923,17 @@ var init_claude = __esm({
|
|
|
1656
1923
|
}
|
|
1657
1924
|
}
|
|
1658
1925
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
messages: [
|
|
1671
|
-
...messages,
|
|
1672
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1673
|
-
],
|
|
1674
|
-
usage
|
|
1675
|
-
};
|
|
1926
|
+
return {
|
|
1927
|
+
output,
|
|
1928
|
+
structuredOutput: void 0,
|
|
1929
|
+
toolCalls,
|
|
1930
|
+
messages: [
|
|
1931
|
+
...messages,
|
|
1932
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1933
|
+
],
|
|
1934
|
+
usage
|
|
1935
|
+
};
|
|
1936
|
+
});
|
|
1676
1937
|
}
|
|
1677
1938
|
if (this.isPersistent) this.clearPersistentSession();
|
|
1678
1939
|
throw e;
|
|
@@ -1691,12 +1952,12 @@ var init_claude = __esm({
|
|
|
1691
1952
|
};
|
|
1692
1953
|
}
|
|
1693
1954
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
1694
|
-
async executeRunStructured(messages, schema,
|
|
1955
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
1695
1956
|
this.checkAbort(signal);
|
|
1696
1957
|
const sdk = await loadSDK2();
|
|
1697
1958
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
1698
|
-
const prompt = isResuming ?
|
|
1699
|
-
let opts = this.buildQueryOptions(signal);
|
|
1959
|
+
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
1960
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1700
1961
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1701
1962
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
1702
1963
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
@@ -1712,30 +1973,8 @@ var init_claude = __esm({
|
|
|
1712
1973
|
let usage;
|
|
1713
1974
|
try {
|
|
1714
1975
|
for await (const msg of q) {
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
if (betaMessage?.content) {
|
|
1718
|
-
for (const block of betaMessage.content) {
|
|
1719
|
-
if (block.type === "tool_use") {
|
|
1720
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1721
|
-
toolCalls.push({
|
|
1722
|
-
toolName,
|
|
1723
|
-
args: block.input ?? {},
|
|
1724
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
1725
|
-
approved: true
|
|
1726
|
-
});
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
1732
|
-
for (const tc of toolCalls) {
|
|
1733
|
-
if (tc.result === null) {
|
|
1734
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
1735
|
-
if (captured !== void 0) tc.result = captured;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1976
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
1977
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1739
1978
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
1740
1979
|
const r = msg;
|
|
1741
1980
|
output = r.result;
|
|
@@ -1770,46 +2009,23 @@ var init_claude = __esm({
|
|
|
1770
2009
|
} catch (e) {
|
|
1771
2010
|
if (signal.aborted) throw new AbortError();
|
|
1772
2011
|
if (isResuming && this.isPersistent) {
|
|
1773
|
-
this.
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
this.activeQuery = retryQ;
|
|
2012
|
+
const retryQ = await this.prepareRetryQuery(
|
|
2013
|
+
sdk,
|
|
2014
|
+
messages,
|
|
2015
|
+
signal,
|
|
2016
|
+
options,
|
|
2017
|
+
toolResultCapture,
|
|
2018
|
+
(opts2) => {
|
|
2019
|
+
opts2.outputFormat = { type: "json_schema", schema: jsonSchema };
|
|
2020
|
+
}
|
|
2021
|
+
);
|
|
1784
2022
|
toolCalls.length = 0;
|
|
1785
2023
|
output = null;
|
|
1786
2024
|
structuredOutput = void 0;
|
|
1787
|
-
|
|
2025
|
+
return this.withRetryErrorHandling(signal, async () => {
|
|
1788
2026
|
for await (const msg of retryQ) {
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
if (betaMessage?.content) {
|
|
1792
|
-
for (const block of betaMessage.content) {
|
|
1793
|
-
if (block.type === "tool_use") {
|
|
1794
|
-
const toolName = stripMcpPrefix(block.name ?? "unknown");
|
|
1795
|
-
toolCalls.push({
|
|
1796
|
-
toolName,
|
|
1797
|
-
args: block.input ?? {},
|
|
1798
|
-
result: toolResultCapture.get(toolName) ?? null,
|
|
1799
|
-
approved: true
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
if (msg.type === "tool_use_summary" || msg.type === "result") {
|
|
1806
|
-
for (const tc of toolCalls) {
|
|
1807
|
-
if (tc.result === null) {
|
|
1808
|
-
const captured = toolResultCapture.get(tc.toolName);
|
|
1809
|
-
if (captured !== void 0) tc.result = captured;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
2027
|
+
this.collectToolCallsFromMessage(msg, toolCalls, toolResultCapture);
|
|
2028
|
+
this.backfillToolResults(msg, toolCalls, toolResultCapture);
|
|
1813
2029
|
if (msg.type === "result" && msg.subtype === "success") {
|
|
1814
2030
|
const r = msg;
|
|
1815
2031
|
output = r.result;
|
|
@@ -1841,23 +2057,17 @@ var init_claude = __esm({
|
|
|
1841
2057
|
);
|
|
1842
2058
|
}
|
|
1843
2059
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
messages: [
|
|
1856
|
-
...messages,
|
|
1857
|
-
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
1858
|
-
],
|
|
1859
|
-
usage
|
|
1860
|
-
};
|
|
2060
|
+
return {
|
|
2061
|
+
output,
|
|
2062
|
+
structuredOutput,
|
|
2063
|
+
toolCalls,
|
|
2064
|
+
messages: [
|
|
2065
|
+
...messages,
|
|
2066
|
+
...output !== null ? [{ role: "assistant", content: output }] : []
|
|
2067
|
+
],
|
|
2068
|
+
usage
|
|
2069
|
+
};
|
|
2070
|
+
});
|
|
1861
2071
|
}
|
|
1862
2072
|
if (this.isPersistent) this.clearPersistentSession();
|
|
1863
2073
|
throw e;
|
|
@@ -1876,12 +2086,12 @@ var init_claude = __esm({
|
|
|
1876
2086
|
};
|
|
1877
2087
|
}
|
|
1878
2088
|
// ─── executeStream ──────────────────────────────────────────────
|
|
1879
|
-
async *executeStream(messages,
|
|
2089
|
+
async *executeStream(messages, options, signal) {
|
|
1880
2090
|
this.checkAbort(signal);
|
|
1881
2091
|
const sdk = await loadSDK2();
|
|
1882
2092
|
const isResuming = this.isPersistent && this._sessionId !== void 0;
|
|
1883
|
-
const prompt = isResuming ?
|
|
1884
|
-
let opts = this.buildQueryOptions(signal);
|
|
2093
|
+
const prompt = isResuming ? extractLastUserPrompt(messages) : buildContextualPrompt(messages);
|
|
2094
|
+
let opts = this.buildQueryOptions(signal, options);
|
|
1885
2095
|
const toolResultCapture = /* @__PURE__ */ new Map();
|
|
1886
2096
|
opts = await this.buildMcpConfig(opts, toolResultCapture);
|
|
1887
2097
|
const q = sdk.query({ prompt, options: opts });
|
|
@@ -1889,6 +2099,7 @@ var init_claude = __esm({
|
|
|
1889
2099
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1890
2100
|
const toolCallTracker = new ClaudeToolCallTracker();
|
|
1891
2101
|
const pendingStreamToolCalls = /* @__PURE__ */ new Map();
|
|
2102
|
+
let hasStreamedText = false;
|
|
1892
2103
|
try {
|
|
1893
2104
|
for await (const msg of q) {
|
|
1894
2105
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1906,6 +2117,7 @@ var init_claude = __esm({
|
|
|
1906
2117
|
} else if (e.type === "tool_call_end") {
|
|
1907
2118
|
pendingStreamToolCalls.delete(e.toolCallId);
|
|
1908
2119
|
}
|
|
2120
|
+
if (e.type === "text_delta") hasStreamedText = true;
|
|
1909
2121
|
yield e;
|
|
1910
2122
|
}
|
|
1911
2123
|
}
|
|
@@ -1929,22 +2141,21 @@ var init_claude = __esm({
|
|
|
1929
2141
|
}
|
|
1930
2142
|
yield this.emitSessionInfo(r.session_id);
|
|
1931
2143
|
}
|
|
1932
|
-
yield {
|
|
2144
|
+
yield {
|
|
2145
|
+
type: "done",
|
|
2146
|
+
finalOutput: hasStreamedText ? null : r.result,
|
|
2147
|
+
...hasStreamedText ? { streamed: true } : {}
|
|
2148
|
+
};
|
|
1933
2149
|
}
|
|
1934
2150
|
}
|
|
1935
2151
|
} catch (e) {
|
|
1936
2152
|
if (signal.aborted) throw new AbortError();
|
|
1937
2153
|
if (isResuming && this.isPersistent) {
|
|
1938
|
-
this.
|
|
1939
|
-
const retryPrompt = buildContextualPrompt2(messages);
|
|
1940
|
-
let retryOpts = this.buildQueryOptions(signal);
|
|
1941
|
-
toolResultCapture.clear();
|
|
1942
|
-
retryOpts = await this.buildMcpConfig(retryOpts, toolResultCapture);
|
|
1943
|
-
const retryQ = sdk.query({ prompt: retryPrompt, options: retryOpts });
|
|
1944
|
-
this.activeQuery = retryQ;
|
|
2154
|
+
const retryQ = await this.prepareRetryQuery(sdk, messages, signal, options, toolResultCapture);
|
|
1945
2155
|
const retryThinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
1946
2156
|
const retryToolCallTracker = new ClaudeToolCallTracker();
|
|
1947
2157
|
const retryPendingToolCalls = /* @__PURE__ */ new Map();
|
|
2158
|
+
let retryHasStreamedText = false;
|
|
1948
2159
|
try {
|
|
1949
2160
|
for await (const msg of retryQ) {
|
|
1950
2161
|
if (signal.aborted) throw new AbortError();
|
|
@@ -1962,6 +2173,7 @@ var init_claude = __esm({
|
|
|
1962
2173
|
} else if (ev.type === "tool_call_end") {
|
|
1963
2174
|
retryPendingToolCalls.delete(ev.toolCallId);
|
|
1964
2175
|
}
|
|
2176
|
+
if (ev.type === "text_delta") retryHasStreamedText = true;
|
|
1965
2177
|
yield ev;
|
|
1966
2178
|
}
|
|
1967
2179
|
}
|
|
@@ -1985,7 +2197,11 @@ var init_claude = __esm({
|
|
|
1985
2197
|
}
|
|
1986
2198
|
yield this.emitSessionInfo(r.session_id);
|
|
1987
2199
|
}
|
|
1988
|
-
yield {
|
|
2200
|
+
yield {
|
|
2201
|
+
type: "done",
|
|
2202
|
+
finalOutput: retryHasStreamedText ? null : r.result,
|
|
2203
|
+
...retryHasStreamedText ? { streamed: true } : {}
|
|
2204
|
+
};
|
|
1989
2205
|
}
|
|
1990
2206
|
}
|
|
1991
2207
|
} catch (retryError) {
|
|
@@ -2047,7 +2263,8 @@ var init_claude = __esm({
|
|
|
2047
2263
|
this.cachedModels = body.data.map((m) => ({
|
|
2048
2264
|
id: m.id,
|
|
2049
2265
|
name: m.display_name,
|
|
2050
|
-
provider: "claude"
|
|
2266
|
+
provider: "claude",
|
|
2267
|
+
...m.max_input_tokens != null && { contextWindow: m.max_input_tokens }
|
|
2051
2268
|
}));
|
|
2052
2269
|
return this.cachedModels;
|
|
2053
2270
|
}
|
|
@@ -2105,32 +2322,30 @@ __export(vercel_ai_exports, {
|
|
|
2105
2322
|
createVercelAIService: () => createVercelAIService
|
|
2106
2323
|
});
|
|
2107
2324
|
async function loadSDK3() {
|
|
2108
|
-
if (
|
|
2325
|
+
if (_sdkMock3) return _sdkMock3;
|
|
2109
2326
|
try {
|
|
2110
|
-
|
|
2111
|
-
return sdkModule3;
|
|
2327
|
+
return await import('ai');
|
|
2112
2328
|
} catch {
|
|
2113
2329
|
throw new DependencyError("ai");
|
|
2114
2330
|
}
|
|
2115
2331
|
}
|
|
2116
2332
|
async function loadCompat() {
|
|
2117
|
-
if (
|
|
2333
|
+
if (_compatMock) return _compatMock;
|
|
2118
2334
|
try {
|
|
2119
|
-
|
|
2120
|
-
return compatModule;
|
|
2335
|
+
return await import('@ai-sdk/openai-compatible');
|
|
2121
2336
|
} catch {
|
|
2122
2337
|
throw new DependencyError("@ai-sdk/openai-compatible");
|
|
2123
2338
|
}
|
|
2124
2339
|
}
|
|
2125
2340
|
function _injectSDK3(mock) {
|
|
2126
|
-
|
|
2341
|
+
_sdkMock3 = mock;
|
|
2127
2342
|
}
|
|
2128
2343
|
function _injectCompat(mock) {
|
|
2129
|
-
|
|
2344
|
+
_compatMock = mock;
|
|
2130
2345
|
}
|
|
2131
2346
|
function _resetSDK3() {
|
|
2132
|
-
|
|
2133
|
-
|
|
2347
|
+
_sdkMock3 = null;
|
|
2348
|
+
_compatMock = null;
|
|
2134
2349
|
}
|
|
2135
2350
|
function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, signal) {
|
|
2136
2351
|
const toolMap = {};
|
|
@@ -2173,13 +2388,14 @@ function mapToolsToSDK2(sdk, tools, config, sessionApprovals, permissionStore, s
|
|
|
2173
2388
|
return toolMap;
|
|
2174
2389
|
}
|
|
2175
2390
|
function wrapToolExecute(ourTool, supervisor, sessionApprovals, permissionStore, signal) {
|
|
2176
|
-
return async (args) => {
|
|
2391
|
+
return async (args, options) => {
|
|
2177
2392
|
if (ourTool.needsApproval && supervisor?.onPermission) {
|
|
2178
2393
|
const storeApproved = permissionStore && await permissionStore.isApproved(ourTool.name);
|
|
2179
2394
|
if (!storeApproved && !sessionApprovals.has(ourTool.name)) {
|
|
2180
2395
|
const request = {
|
|
2181
2396
|
toolName: ourTool.name,
|
|
2182
|
-
toolArgs: args ?? {}
|
|
2397
|
+
toolArgs: args ?? {},
|
|
2398
|
+
toolCallId: options?.toolCallId
|
|
2183
2399
|
};
|
|
2184
2400
|
const decision = await supervisor.onPermission(
|
|
2185
2401
|
request,
|
|
@@ -2286,7 +2502,8 @@ function mapStreamPart(part) {
|
|
|
2286
2502
|
return {
|
|
2287
2503
|
type: "error",
|
|
2288
2504
|
error: p.error instanceof Error ? p.error.message : String(p.error ?? "Tool execution failed"),
|
|
2289
|
-
recoverable: true
|
|
2505
|
+
recoverable: true,
|
|
2506
|
+
code: "TOOL_EXECUTION" /* TOOL_EXECUTION */
|
|
2290
2507
|
};
|
|
2291
2508
|
}
|
|
2292
2509
|
case "reasoning-start":
|
|
@@ -2307,10 +2524,13 @@ function mapStreamPart(part) {
|
|
|
2307
2524
|
}
|
|
2308
2525
|
case "error": {
|
|
2309
2526
|
const p = part;
|
|
2527
|
+
const errorMsg = p.error instanceof Error ? p.error.message : String(p.error ?? "Unknown error");
|
|
2528
|
+
const code = classifyAgentError(errorMsg);
|
|
2310
2529
|
return {
|
|
2311
2530
|
type: "error",
|
|
2312
|
-
error:
|
|
2313
|
-
recoverable:
|
|
2531
|
+
error: errorMsg,
|
|
2532
|
+
recoverable: isRecoverableErrorCode(code),
|
|
2533
|
+
code
|
|
2314
2534
|
};
|
|
2315
2535
|
}
|
|
2316
2536
|
default:
|
|
@@ -2320,15 +2540,15 @@ function mapStreamPart(part) {
|
|
|
2320
2540
|
function createVercelAIService(options) {
|
|
2321
2541
|
return new VercelAIAgentService(options);
|
|
2322
2542
|
}
|
|
2323
|
-
var
|
|
2543
|
+
var _sdkMock3, _compatMock, DEFAULT_BASE_URL, DEFAULT_PROVIDER, DEFAULT_MAX_TURNS, VercelAIAgent, VercelAIAgentService;
|
|
2324
2544
|
var init_vercel_ai = __esm({
|
|
2325
2545
|
"src/backends/vercel-ai.ts"() {
|
|
2326
|
-
|
|
2546
|
+
init_types2();
|
|
2327
2547
|
init_base_agent();
|
|
2328
|
-
|
|
2548
|
+
init_errors2();
|
|
2329
2549
|
init_schema();
|
|
2330
|
-
|
|
2331
|
-
|
|
2550
|
+
_sdkMock3 = null;
|
|
2551
|
+
_compatMock = null;
|
|
2332
2552
|
DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
2333
2553
|
DEFAULT_PROVIDER = "openrouter";
|
|
2334
2554
|
DEFAULT_MAX_TURNS = 10;
|
|
@@ -2341,28 +2561,33 @@ var init_vercel_ai = __esm({
|
|
|
2341
2561
|
super(config);
|
|
2342
2562
|
this.backendOptions = backendOptions;
|
|
2343
2563
|
}
|
|
2344
|
-
async getModel() {
|
|
2345
|
-
|
|
2564
|
+
async getModel(options) {
|
|
2565
|
+
const requestedModel = options.model;
|
|
2566
|
+
const defaultModel = this.config.model;
|
|
2567
|
+
if (requestedModel === defaultModel && this.model) return this.model;
|
|
2346
2568
|
const compat = await loadCompat();
|
|
2347
2569
|
const provider = compat.createOpenAICompatible({
|
|
2348
2570
|
name: this.backendOptions.provider ?? DEFAULT_PROVIDER,
|
|
2349
2571
|
baseURL: this.backendOptions.baseUrl ?? DEFAULT_BASE_URL,
|
|
2350
2572
|
apiKey: this.backendOptions.apiKey
|
|
2351
2573
|
});
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2574
|
+
const model = provider.chatModel(requestedModel);
|
|
2575
|
+
if (requestedModel === defaultModel) {
|
|
2576
|
+
this.model = model;
|
|
2577
|
+
}
|
|
2578
|
+
return model;
|
|
2355
2579
|
}
|
|
2356
|
-
async getSDKTools(signal) {
|
|
2580
|
+
async getSDKTools(signal, options) {
|
|
2357
2581
|
const sdk = await loadSDK3();
|
|
2358
|
-
|
|
2582
|
+
const tools = this.resolveTools(options);
|
|
2583
|
+
return mapToolsToSDK2(sdk, tools, this.config, this.sessionApprovals, this.config.permissionStore, signal);
|
|
2359
2584
|
}
|
|
2360
2585
|
// ─── executeRun ─────────────────────────────────────────────────
|
|
2361
|
-
async executeRun(messages,
|
|
2586
|
+
async executeRun(messages, options, signal) {
|
|
2362
2587
|
this.checkAbort(signal);
|
|
2363
2588
|
const sdk = await loadSDK3();
|
|
2364
|
-
const model = await this.getModel();
|
|
2365
|
-
const tools = await this.getSDKTools(signal);
|
|
2589
|
+
const model = await this.getModel(options);
|
|
2590
|
+
const tools = await this.getSDKTools(signal, options);
|
|
2366
2591
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
2367
2592
|
const sdkMessages = messagesToSDK(messages);
|
|
2368
2593
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -2418,10 +2643,10 @@ var init_vercel_ai = __esm({
|
|
|
2418
2643
|
};
|
|
2419
2644
|
}
|
|
2420
2645
|
// ─── executeRunStructured ───────────────────────────────────────
|
|
2421
|
-
async executeRunStructured(messages, schema,
|
|
2646
|
+
async executeRunStructured(messages, schema, options, signal) {
|
|
2422
2647
|
this.checkAbort(signal);
|
|
2423
2648
|
const sdk = await loadSDK3();
|
|
2424
|
-
const model = await this.getModel();
|
|
2649
|
+
const model = await this.getModel(options);
|
|
2425
2650
|
const sdkMessages = messagesToSDK(messages);
|
|
2426
2651
|
const jsonSchema = zodToJsonSchema(schema.schema);
|
|
2427
2652
|
const result = await sdk.generateObject({
|
|
@@ -2463,11 +2688,11 @@ var init_vercel_ai = __esm({
|
|
|
2463
2688
|
};
|
|
2464
2689
|
}
|
|
2465
2690
|
// ─── executeStream ──────────────────────────────────────────────
|
|
2466
|
-
async *executeStream(messages,
|
|
2691
|
+
async *executeStream(messages, options, signal) {
|
|
2467
2692
|
this.checkAbort(signal);
|
|
2468
2693
|
const sdk = await loadSDK3();
|
|
2469
|
-
const model = await this.getModel();
|
|
2470
|
-
const tools = await this.getSDKTools(signal);
|
|
2694
|
+
const model = await this.getModel(options);
|
|
2695
|
+
const tools = await this.getSDKTools(signal, options);
|
|
2471
2696
|
const maxTurns = this.config.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
2472
2697
|
const sdkMessages = messagesToSDK(messages);
|
|
2473
2698
|
const hasTools = Object.keys(tools).length > 0;
|
|
@@ -2492,6 +2717,7 @@ var init_vercel_ai = __esm({
|
|
|
2492
2717
|
}
|
|
2493
2718
|
});
|
|
2494
2719
|
let finalText = "";
|
|
2720
|
+
let lastFinishReason;
|
|
2495
2721
|
try {
|
|
2496
2722
|
for await (const part of result.fullStream) {
|
|
2497
2723
|
if (signal.aborted) throw new AbortError();
|
|
@@ -2502,10 +2728,15 @@ var init_vercel_ai = __esm({
|
|
|
2502
2728
|
}
|
|
2503
2729
|
if (part.type === "finish-step") {
|
|
2504
2730
|
const p = part;
|
|
2731
|
+
lastFinishReason = p.finishReason;
|
|
2505
2732
|
if (p.finishReason === "tool-calls") {
|
|
2506
2733
|
finalText = "";
|
|
2507
2734
|
}
|
|
2508
2735
|
}
|
|
2736
|
+
if (part.type === "finish") {
|
|
2737
|
+
const p = part;
|
|
2738
|
+
lastFinishReason = p.finishReason;
|
|
2739
|
+
}
|
|
2509
2740
|
}
|
|
2510
2741
|
const totalUsage = await result.totalUsage;
|
|
2511
2742
|
yield {
|
|
@@ -2513,9 +2744,12 @@ var init_vercel_ai = __esm({
|
|
|
2513
2744
|
promptTokens: Number(totalUsage?.inputTokens ?? 0),
|
|
2514
2745
|
completionTokens: Number(totalUsage?.outputTokens ?? 0)
|
|
2515
2746
|
};
|
|
2747
|
+
const hasStreamed = finalText.length > 0;
|
|
2516
2748
|
yield {
|
|
2517
2749
|
type: "done",
|
|
2518
|
-
finalOutput: finalText || null
|
|
2750
|
+
finalOutput: hasStreamed ? null : finalText || null,
|
|
2751
|
+
...hasStreamed ? { streamed: true } : {},
|
|
2752
|
+
...lastFinishReason ? { finishReason: lastFinishReason } : {}
|
|
2519
2753
|
};
|
|
2520
2754
|
} catch (e) {
|
|
2521
2755
|
if (signal.aborted) throw new AbortError();
|
|
@@ -2544,16 +2778,33 @@ var init_vercel_ai = __esm({
|
|
|
2544
2778
|
const baseUrl = (this.options.baseUrl || "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
2545
2779
|
try {
|
|
2546
2780
|
const res = await globalThis.fetch(`${baseUrl}/models`, {
|
|
2547
|
-
headers: {
|
|
2781
|
+
headers: {
|
|
2782
|
+
Authorization: `Bearer ${this.options.apiKey}`,
|
|
2783
|
+
// OpenRouter requires HTTP-Referer for API access
|
|
2784
|
+
"HTTP-Referer": "https://github.com/nicepkg/agent-sdk"
|
|
2785
|
+
}
|
|
2548
2786
|
});
|
|
2549
2787
|
if (!res.ok) {
|
|
2550
2788
|
return [];
|
|
2551
2789
|
}
|
|
2552
2790
|
const body = await res.json();
|
|
2553
|
-
if (
|
|
2554
|
-
return
|
|
2791
|
+
if (body.data && Array.isArray(body.data)) {
|
|
2792
|
+
return body.data.filter((m) => typeof m.id === "string").map((m) => ({
|
|
2793
|
+
id: m.id,
|
|
2794
|
+
...typeof m.name === "string" && { name: m.name },
|
|
2795
|
+
...typeof m.description === "string" && { description: m.description },
|
|
2796
|
+
...typeof m.context_length === "number" && { contextWindow: m.context_length }
|
|
2797
|
+
}));
|
|
2798
|
+
}
|
|
2799
|
+
if (Array.isArray(body)) {
|
|
2800
|
+
return body.filter((m) => typeof m.id === "string").map((m) => ({
|
|
2801
|
+
id: m.id,
|
|
2802
|
+
...typeof m.name === "string" && { name: m.name },
|
|
2803
|
+
...typeof m.description === "string" && { description: m.description },
|
|
2804
|
+
...typeof m.context_length === "number" && { contextWindow: m.context_length }
|
|
2805
|
+
}));
|
|
2555
2806
|
}
|
|
2556
|
-
return
|
|
2807
|
+
return [];
|
|
2557
2808
|
} catch {
|
|
2558
2809
|
return [];
|
|
2559
2810
|
}
|
|
@@ -2584,263 +2835,7 @@ var init_vercel_ai = __esm({
|
|
|
2584
2835
|
}
|
|
2585
2836
|
});
|
|
2586
2837
|
|
|
2587
|
-
// src/
|
|
2588
|
-
function registerBackend(name, factory) {
|
|
2589
|
-
if (registry.has(name)) {
|
|
2590
|
-
throw new BackendAlreadyRegisteredError(name);
|
|
2591
|
-
}
|
|
2592
|
-
registry.set(name, { factory, builtin: false });
|
|
2593
|
-
}
|
|
2594
|
-
function unregisterBackend(name) {
|
|
2595
|
-
return registry.delete(name);
|
|
2596
|
-
}
|
|
2597
|
-
function hasBackend(name) {
|
|
2598
|
-
return registry.has(name) || isBuiltinName(name);
|
|
2599
|
-
}
|
|
2600
|
-
function listBackends() {
|
|
2601
|
-
const names = new Set(registry.keys());
|
|
2602
|
-
for (const builtin of BUILTIN_BACKENDS) {
|
|
2603
|
-
names.add(builtin);
|
|
2604
|
-
}
|
|
2605
|
-
return [...names];
|
|
2606
|
-
}
|
|
2607
|
-
function resetRegistry() {
|
|
2608
|
-
registry.clear();
|
|
2609
|
-
}
|
|
2610
|
-
function isBuiltinName(name) {
|
|
2611
|
-
return BUILTIN_BACKENDS.has(name);
|
|
2612
|
-
}
|
|
2613
|
-
async function loadBuiltinFactory(name) {
|
|
2614
|
-
switch (name) {
|
|
2615
|
-
case "copilot": {
|
|
2616
|
-
const mod = await Promise.resolve().then(() => (init_copilot(), copilot_exports));
|
|
2617
|
-
return (opts) => mod.createCopilotService(opts);
|
|
2618
|
-
}
|
|
2619
|
-
case "claude": {
|
|
2620
|
-
const mod = await Promise.resolve().then(() => (init_claude(), claude_exports));
|
|
2621
|
-
return (opts) => mod.createClaudeService(opts);
|
|
2622
|
-
}
|
|
2623
|
-
case "vercel-ai": {
|
|
2624
|
-
const mod = await Promise.resolve().then(() => (init_vercel_ai(), vercel_ai_exports));
|
|
2625
|
-
return (opts) => mod.createVercelAIService(opts);
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
}
|
|
2629
|
-
async function createAgentService(name, options) {
|
|
2630
|
-
const entry = registry.get(name);
|
|
2631
|
-
if (entry) {
|
|
2632
|
-
return entry.factory(options);
|
|
2633
|
-
}
|
|
2634
|
-
if (isBuiltinName(name)) {
|
|
2635
|
-
const factory = await loadBuiltinFactory(name);
|
|
2636
|
-
registry.set(name, { factory, builtin: true });
|
|
2637
|
-
return factory(options);
|
|
2638
|
-
}
|
|
2639
|
-
throw new BackendNotFoundError(name);
|
|
2640
|
-
}
|
|
2641
|
-
var registry, BUILTIN_BACKENDS;
|
|
2642
|
-
var init_registry = __esm({
|
|
2643
|
-
"src/registry.ts"() {
|
|
2644
|
-
init_errors();
|
|
2645
|
-
registry = /* @__PURE__ */ new Map();
|
|
2646
|
-
BUILTIN_BACKENDS = /* @__PURE__ */ new Set([
|
|
2647
|
-
"copilot",
|
|
2648
|
-
"claude",
|
|
2649
|
-
"vercel-ai"
|
|
2650
|
-
]);
|
|
2651
|
-
}
|
|
2652
|
-
});
|
|
2653
|
-
|
|
2654
|
-
// src/utils/messages.ts
|
|
2655
|
-
function messagesToPrompt(messages) {
|
|
2656
|
-
return messages.map((msg) => {
|
|
2657
|
-
switch (msg.role) {
|
|
2658
|
-
case "user":
|
|
2659
|
-
return contentToText(msg.content);
|
|
2660
|
-
case "assistant":
|
|
2661
|
-
return contentToText(msg.content);
|
|
2662
|
-
case "system":
|
|
2663
|
-
return msg.content;
|
|
2664
|
-
case "tool":
|
|
2665
|
-
return msg.content ?? "";
|
|
2666
|
-
}
|
|
2667
|
-
}).filter(Boolean).join("\n\n");
|
|
2668
|
-
}
|
|
2669
|
-
function contentToText(content) {
|
|
2670
|
-
return getTextContent(content);
|
|
2671
|
-
}
|
|
2672
|
-
function buildSystemPrompt(base, schemaInstruction) {
|
|
2673
|
-
if (!schemaInstruction) return base;
|
|
2674
|
-
return `${base}
|
|
2675
|
-
|
|
2676
|
-
${schemaInstruction}`;
|
|
2677
|
-
}
|
|
2678
|
-
var init_messages = __esm({
|
|
2679
|
-
"src/utils/messages.ts"() {
|
|
2680
|
-
init_types();
|
|
2681
|
-
}
|
|
2682
|
-
});
|
|
2683
|
-
function createDefaultPermissionStore(projectDir) {
|
|
2684
|
-
const sessionStore = new InMemoryPermissionStore();
|
|
2685
|
-
const projectPath = projectDir ? path.join(projectDir, ".agent-sdk", "permissions.json") : path.join(process.cwd(), ".agent-sdk", "permissions.json");
|
|
2686
|
-
const userPath = path.join(os.homedir(), ".agent-sdk", "permissions.json");
|
|
2687
|
-
const projectStore = new FilePermissionStore(projectPath);
|
|
2688
|
-
const userStore = new FilePermissionStore(userPath);
|
|
2689
|
-
return new CompositePermissionStore(sessionStore, projectStore, userStore);
|
|
2690
|
-
}
|
|
2691
|
-
var InMemoryPermissionStore, FilePermissionStore, CompositePermissionStore;
|
|
2692
|
-
var init_permission_store = __esm({
|
|
2693
|
-
"src/permission-store.ts"() {
|
|
2694
|
-
InMemoryPermissionStore = class {
|
|
2695
|
-
approvals = /* @__PURE__ */ new Map();
|
|
2696
|
-
async isApproved(toolName) {
|
|
2697
|
-
return this.approvals.has(toolName);
|
|
2698
|
-
}
|
|
2699
|
-
async approve(toolName, scope) {
|
|
2700
|
-
if (scope === "once") return;
|
|
2701
|
-
this.approvals.set(toolName, scope);
|
|
2702
|
-
}
|
|
2703
|
-
async revoke(toolName) {
|
|
2704
|
-
this.approvals.delete(toolName);
|
|
2705
|
-
}
|
|
2706
|
-
async clear() {
|
|
2707
|
-
this.approvals.clear();
|
|
2708
|
-
}
|
|
2709
|
-
async dispose() {
|
|
2710
|
-
this.approvals.clear();
|
|
2711
|
-
}
|
|
2712
|
-
};
|
|
2713
|
-
FilePermissionStore = class {
|
|
2714
|
-
filePath;
|
|
2715
|
-
constructor(filePath) {
|
|
2716
|
-
this.filePath = path.resolve(filePath);
|
|
2717
|
-
}
|
|
2718
|
-
async isApproved(toolName) {
|
|
2719
|
-
const data = this.readFile();
|
|
2720
|
-
return toolName in data.approvals;
|
|
2721
|
-
}
|
|
2722
|
-
async approve(toolName, scope) {
|
|
2723
|
-
if (scope === "once") return;
|
|
2724
|
-
const data = this.readFile();
|
|
2725
|
-
data.approvals[toolName] = { scope, timestamp: Date.now() };
|
|
2726
|
-
this.writeFileAtomic(data);
|
|
2727
|
-
}
|
|
2728
|
-
async revoke(toolName) {
|
|
2729
|
-
const data = this.readFile();
|
|
2730
|
-
delete data.approvals[toolName];
|
|
2731
|
-
this.writeFileAtomic(data);
|
|
2732
|
-
}
|
|
2733
|
-
async clear() {
|
|
2734
|
-
this.writeFileAtomic({ approvals: {} });
|
|
2735
|
-
}
|
|
2736
|
-
async dispose() {
|
|
2737
|
-
}
|
|
2738
|
-
readFile() {
|
|
2739
|
-
try {
|
|
2740
|
-
const raw = fs.readFileSync(this.filePath, "utf-8");
|
|
2741
|
-
const parsed = JSON.parse(raw);
|
|
2742
|
-
if (parsed && typeof parsed.approvals === "object") return parsed;
|
|
2743
|
-
} catch {
|
|
2744
|
-
}
|
|
2745
|
-
return { approvals: {} };
|
|
2746
|
-
}
|
|
2747
|
-
writeFileAtomic(data) {
|
|
2748
|
-
const dir = path.dirname(this.filePath);
|
|
2749
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
2750
|
-
const tmpPath = this.filePath + `.tmp.${process.pid}.${Date.now()}`;
|
|
2751
|
-
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
2752
|
-
fs.renameSync(tmpPath, this.filePath);
|
|
2753
|
-
}
|
|
2754
|
-
};
|
|
2755
|
-
CompositePermissionStore = class {
|
|
2756
|
-
sessionStore;
|
|
2757
|
-
projectStore;
|
|
2758
|
-
userStore;
|
|
2759
|
-
constructor(sessionStore, projectStore, userStore) {
|
|
2760
|
-
this.sessionStore = sessionStore;
|
|
2761
|
-
this.projectStore = projectStore;
|
|
2762
|
-
this.userStore = userStore ?? projectStore;
|
|
2763
|
-
}
|
|
2764
|
-
async isApproved(toolName) {
|
|
2765
|
-
return await this.sessionStore.isApproved(toolName) || await this.projectStore.isApproved(toolName) || await this.userStore.isApproved(toolName);
|
|
2766
|
-
}
|
|
2767
|
-
async approve(toolName, scope) {
|
|
2768
|
-
if (scope === "once") return;
|
|
2769
|
-
if (scope === "session") {
|
|
2770
|
-
await this.sessionStore.approve(toolName, scope);
|
|
2771
|
-
} else if (scope === "project") {
|
|
2772
|
-
await this.projectStore.approve(toolName, scope);
|
|
2773
|
-
} else {
|
|
2774
|
-
await this.userStore.approve(toolName, scope);
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
async revoke(toolName) {
|
|
2778
|
-
await this.sessionStore.revoke(toolName);
|
|
2779
|
-
await this.projectStore.revoke(toolName);
|
|
2780
|
-
await this.userStore.revoke(toolName);
|
|
2781
|
-
}
|
|
2782
|
-
async clear() {
|
|
2783
|
-
await this.sessionStore.clear();
|
|
2784
|
-
await this.projectStore.clear();
|
|
2785
|
-
await this.userStore.clear();
|
|
2786
|
-
}
|
|
2787
|
-
async dispose() {
|
|
2788
|
-
await this.sessionStore.dispose();
|
|
2789
|
-
await this.projectStore.dispose();
|
|
2790
|
-
if (this.userStore !== this.projectStore) {
|
|
2791
|
-
await this.userStore.dispose();
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
};
|
|
2795
|
-
}
|
|
2796
|
-
});
|
|
2797
|
-
|
|
2798
|
-
// src/index.ts
|
|
2799
|
-
var src_exports = {};
|
|
2800
|
-
__export(src_exports, {
|
|
2801
|
-
AbortError: () => AbortError,
|
|
2802
|
-
AgentSDKError: () => AgentSDKError,
|
|
2803
|
-
BackendAlreadyRegisteredError: () => BackendAlreadyRegisteredError,
|
|
2804
|
-
BackendNotFoundError: () => BackendNotFoundError,
|
|
2805
|
-
BaseAgent: () => BaseAgent,
|
|
2806
|
-
CompositePermissionStore: () => CompositePermissionStore,
|
|
2807
|
-
DependencyError: () => DependencyError,
|
|
2808
|
-
DisposedError: () => DisposedError,
|
|
2809
|
-
FilePermissionStore: () => FilePermissionStore,
|
|
2810
|
-
InMemoryPermissionStore: () => InMemoryPermissionStore,
|
|
2811
|
-
ReentrancyError: () => ReentrancyError,
|
|
2812
|
-
StructuredOutputError: () => StructuredOutputError,
|
|
2813
|
-
SubprocessError: () => SubprocessError,
|
|
2814
|
-
ToolExecutionError: () => ToolExecutionError,
|
|
2815
|
-
buildSystemPrompt: () => buildSystemPrompt,
|
|
2816
|
-
contentToText: () => contentToText,
|
|
2817
|
-
createAgentService: () => createAgentService,
|
|
2818
|
-
createDefaultPermissionStore: () => createDefaultPermissionStore,
|
|
2819
|
-
getTextContent: () => getTextContent,
|
|
2820
|
-
hasBackend: () => hasBackend,
|
|
2821
|
-
isMultiPartContent: () => isMultiPartContent,
|
|
2822
|
-
isTextContent: () => isTextContent,
|
|
2823
|
-
isToolDefinition: () => isToolDefinition,
|
|
2824
|
-
listBackends: () => listBackends,
|
|
2825
|
-
messagesToPrompt: () => messagesToPrompt,
|
|
2826
|
-
registerBackend: () => registerBackend,
|
|
2827
|
-
resetRegistry: () => resetRegistry,
|
|
2828
|
-
unregisterBackend: () => unregisterBackend,
|
|
2829
|
-
zodToJsonSchema: () => zodToJsonSchema
|
|
2830
|
-
});
|
|
2831
|
-
var init_src = __esm({
|
|
2832
|
-
"src/index.ts"() {
|
|
2833
|
-
init_types();
|
|
2834
|
-
init_errors();
|
|
2835
|
-
init_registry();
|
|
2836
|
-
init_base_agent();
|
|
2837
|
-
init_schema();
|
|
2838
|
-
init_messages();
|
|
2839
|
-
init_permission_store();
|
|
2840
|
-
}
|
|
2841
|
-
});
|
|
2842
|
-
|
|
2843
|
-
// src/chat/core.ts
|
|
2838
|
+
// src/chat/types.ts
|
|
2844
2839
|
function createChatId() {
|
|
2845
2840
|
return crypto.randomUUID();
|
|
2846
2841
|
}
|
|
@@ -2851,6 +2846,20 @@ function toChatId(value) {
|
|
|
2851
2846
|
}
|
|
2852
2847
|
return value;
|
|
2853
2848
|
}
|
|
2849
|
+
function createTextMessage(text, role = "user") {
|
|
2850
|
+
return {
|
|
2851
|
+
id: createChatId(),
|
|
2852
|
+
role,
|
|
2853
|
+
parts: [{ type: "text", text, status: "complete" }],
|
|
2854
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2855
|
+
status: "complete"
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
function isObservableSession(session) {
|
|
2859
|
+
return "subscribe" in session && typeof session.subscribe === "function" && "getSnapshot" in session && typeof session.getSnapshot === "function";
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
// src/chat/chat-utils.ts
|
|
2854
2863
|
function getMessageText(message) {
|
|
2855
2864
|
return message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
2856
2865
|
}
|
|
@@ -2860,6 +2869,8 @@ function getMessageToolCalls(message) {
|
|
|
2860
2869
|
function getMessageReasoning(message) {
|
|
2861
2870
|
return message.parts.filter((p) => p.type === "reasoning").map((p) => p.text).join("");
|
|
2862
2871
|
}
|
|
2872
|
+
|
|
2873
|
+
// src/chat/guards.ts
|
|
2863
2874
|
function isChatMessage(value) {
|
|
2864
2875
|
if (typeof value !== "object" || value === null) return false;
|
|
2865
2876
|
const obj = value;
|
|
@@ -2900,31 +2911,33 @@ function isFilePart(value) {
|
|
|
2900
2911
|
const obj = value;
|
|
2901
2912
|
return obj.type === "file" && typeof obj.name === "string" && typeof obj.mimeType === "string";
|
|
2902
2913
|
}
|
|
2914
|
+
var VALID_CHAT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
2915
|
+
"message:start",
|
|
2916
|
+
"message:delta",
|
|
2917
|
+
"message:complete",
|
|
2918
|
+
"tool:start",
|
|
2919
|
+
"tool:complete",
|
|
2920
|
+
"thinking:start",
|
|
2921
|
+
"thinking:delta",
|
|
2922
|
+
"thinking:end",
|
|
2923
|
+
"permission:request",
|
|
2924
|
+
"permission:response",
|
|
2925
|
+
"usage",
|
|
2926
|
+
"session:created",
|
|
2927
|
+
"session:updated",
|
|
2928
|
+
"error",
|
|
2929
|
+
"typing:start",
|
|
2930
|
+
"typing:end",
|
|
2931
|
+
"heartbeat",
|
|
2932
|
+
"done"
|
|
2933
|
+
]);
|
|
2903
2934
|
function isChatEvent(value) {
|
|
2904
2935
|
if (typeof value !== "object" || value === null) return false;
|
|
2905
2936
|
const obj = value;
|
|
2906
|
-
|
|
2907
|
-
"message:start",
|
|
2908
|
-
"message:delta",
|
|
2909
|
-
"message:complete",
|
|
2910
|
-
"tool:start",
|
|
2911
|
-
"tool:complete",
|
|
2912
|
-
"thinking:start",
|
|
2913
|
-
"thinking:delta",
|
|
2914
|
-
"thinking:end",
|
|
2915
|
-
"permission:request",
|
|
2916
|
-
"permission:response",
|
|
2917
|
-
"usage",
|
|
2918
|
-
"session:created",
|
|
2919
|
-
"session:updated",
|
|
2920
|
-
"error",
|
|
2921
|
-
"typing:start",
|
|
2922
|
-
"typing:end",
|
|
2923
|
-
"heartbeat",
|
|
2924
|
-
"done"
|
|
2925
|
-
];
|
|
2926
|
-
return validTypes.includes(obj.type);
|
|
2937
|
+
return VALID_CHAT_EVENT_TYPES.has(obj.type);
|
|
2927
2938
|
}
|
|
2939
|
+
|
|
2940
|
+
// src/chat/bridge.ts
|
|
2928
2941
|
function agentEventToChatEvent(event, messageId) {
|
|
2929
2942
|
switch (event.type) {
|
|
2930
2943
|
case "text_delta":
|
|
@@ -2977,6 +2990,7 @@ function agentEventToChatEvent(event, messageId) {
|
|
|
2977
2990
|
type: "error",
|
|
2978
2991
|
error: event.error,
|
|
2979
2992
|
recoverable: event.recoverable,
|
|
2993
|
+
code: event.code,
|
|
2980
2994
|
messageId
|
|
2981
2995
|
};
|
|
2982
2996
|
case "heartbeat":
|
|
@@ -2984,8 +2998,9 @@ function agentEventToChatEvent(event, messageId) {
|
|
|
2984
2998
|
case "ask_user":
|
|
2985
2999
|
case "ask_user_response":
|
|
2986
3000
|
case "session_info":
|
|
2987
|
-
case "done":
|
|
2988
3001
|
return null;
|
|
3002
|
+
case "done":
|
|
3003
|
+
return { type: "done", finalOutput: event.finalOutput ?? void 0, finishReason: event.finishReason };
|
|
2989
3004
|
default:
|
|
2990
3005
|
return null;
|
|
2991
3006
|
}
|
|
@@ -3023,27 +3038,37 @@ function chatEventToAgentEvent(event) {
|
|
|
3023
3038
|
result: event.result
|
|
3024
3039
|
};
|
|
3025
3040
|
case "error":
|
|
3026
|
-
return { type: "error", error: event.error, recoverable: event.recoverable };
|
|
3041
|
+
return { type: "error", error: event.error, recoverable: event.recoverable, code: event.code };
|
|
3027
3042
|
default:
|
|
3028
3043
|
return null;
|
|
3029
3044
|
}
|
|
3030
3045
|
}
|
|
3046
|
+
|
|
3047
|
+
// src/chat/conversion.ts
|
|
3031
3048
|
function toAgentMessage(message) {
|
|
3049
|
+
return toAgentMessages(message)[0];
|
|
3050
|
+
}
|
|
3051
|
+
function toAgentMessages(message) {
|
|
3032
3052
|
const textContent = getMessageText(message);
|
|
3033
3053
|
const toolCallParts = getMessageToolCalls(message);
|
|
3034
3054
|
switch (message.role) {
|
|
3035
3055
|
case "user":
|
|
3036
|
-
return { role: "user", content: textContent };
|
|
3056
|
+
return [{ role: "user", content: textContent }];
|
|
3037
3057
|
case "assistant": {
|
|
3038
3058
|
const toolCalls = toolCallParts.length > 0 ? toolCallParts.map((p) => ({ id: p.toolCallId, name: p.name, args: p.args })) : void 0;
|
|
3039
|
-
|
|
3059
|
+
const assistantMsg = {
|
|
3040
3060
|
role: "assistant",
|
|
3041
3061
|
content: textContent,
|
|
3042
3062
|
toolCalls
|
|
3043
3063
|
};
|
|
3064
|
+
const toolResults = extractToolResults(message);
|
|
3065
|
+
if (toolResults.length > 0) {
|
|
3066
|
+
return [assistantMsg, { role: "tool", toolResults }];
|
|
3067
|
+
}
|
|
3068
|
+
return [assistantMsg];
|
|
3044
3069
|
}
|
|
3045
3070
|
case "system":
|
|
3046
|
-
return { role: "system", content: textContent };
|
|
3071
|
+
return [{ role: "system", content: textContent }];
|
|
3047
3072
|
}
|
|
3048
3073
|
}
|
|
3049
3074
|
function fromAgentMessage(message, id) {
|
|
@@ -3089,6 +3114,14 @@ function fromAgentMessage(message, id) {
|
|
|
3089
3114
|
status: "complete"
|
|
3090
3115
|
};
|
|
3091
3116
|
}
|
|
3117
|
+
function extractToolResults(message) {
|
|
3118
|
+
return getMessageToolCalls(message).filter((p) => p.result !== void 0).map((p) => ({
|
|
3119
|
+
toolCallId: p.toolCallId,
|
|
3120
|
+
name: p.name,
|
|
3121
|
+
result: p.result,
|
|
3122
|
+
isError: p.status === "error" ? true : void 0
|
|
3123
|
+
}));
|
|
3124
|
+
}
|
|
3092
3125
|
|
|
3093
3126
|
// src/chat/context.ts
|
|
3094
3127
|
function estimateTokens(message, options) {
|
|
@@ -3198,6 +3231,56 @@ var ContextWindowManager = class {
|
|
|
3198
3231
|
});
|
|
3199
3232
|
return { ...result, messages: updatedMessages };
|
|
3200
3233
|
}
|
|
3234
|
+
/**
|
|
3235
|
+
* Trim messages using real token usage data from the previous API call.
|
|
3236
|
+
* Uses average-based algorithm: `avgTokensPerMessage = lastPromptTokens / messageCount`.
|
|
3237
|
+
* Removes oldest non-system messages until freed budget brings usage under modelContextWindow.
|
|
3238
|
+
*
|
|
3239
|
+
* @param messages - All messages in the session
|
|
3240
|
+
* @param lastPromptTokens - Real prompt tokens from the last API response
|
|
3241
|
+
* @param modelContextWindow - Model's total context window size in tokens
|
|
3242
|
+
* @returns Result with fitted messages and metadata
|
|
3243
|
+
*/
|
|
3244
|
+
fitMessagesWithUsage(messages, lastPromptTokens, modelContextWindow) {
|
|
3245
|
+
if (messages.length === 0) {
|
|
3246
|
+
return { messages: [], totalTokens: 0, removedCount: 0, wasTruncated: false };
|
|
3247
|
+
}
|
|
3248
|
+
const budget = modelContextWindow - this.config.reservedTokens;
|
|
3249
|
+
if (budget <= 0 || lastPromptTokens <= budget) {
|
|
3250
|
+
return {
|
|
3251
|
+
messages: [...messages],
|
|
3252
|
+
totalTokens: lastPromptTokens,
|
|
3253
|
+
removedCount: 0,
|
|
3254
|
+
wasTruncated: false
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3257
|
+
const avgTokensPerMessage = lastPromptTokens / messages.length;
|
|
3258
|
+
const tokensToFree = lastPromptTokens - budget;
|
|
3259
|
+
const messagesToRemove = Math.ceil(tokensToFree / avgTokensPerMessage);
|
|
3260
|
+
const nonSystemIndices = [];
|
|
3261
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3262
|
+
if (messages[i].role === "system") ; else {
|
|
3263
|
+
nonSystemIndices.push(i);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
const removableCount = Math.min(messagesToRemove, nonSystemIndices.length);
|
|
3267
|
+
const removedIndices = new Set(nonSystemIndices.slice(0, removableCount));
|
|
3268
|
+
const result = [];
|
|
3269
|
+
for (let i = 0; i < messages.length; i++) {
|
|
3270
|
+
if (!removedIndices.has(i)) {
|
|
3271
|
+
result.push(messages[i]);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
const estimatedTokens = Math.round(
|
|
3275
|
+
lastPromptTokens * (result.length / messages.length)
|
|
3276
|
+
);
|
|
3277
|
+
return {
|
|
3278
|
+
messages: result,
|
|
3279
|
+
totalTokens: estimatedTokens,
|
|
3280
|
+
removedCount: removableCount,
|
|
3281
|
+
wasTruncated: removableCount > 0
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3201
3284
|
/**
|
|
3202
3285
|
* Truncate oldest: keeps system messages, removes oldest non-system messages first.
|
|
3203
3286
|
* Always keeps the most recent user message.
|
|
@@ -3315,37 +3398,19 @@ var ContextWindowManager = class {
|
|
|
3315
3398
|
};
|
|
3316
3399
|
|
|
3317
3400
|
// src/chat/errors.ts
|
|
3401
|
+
init_errors2();
|
|
3318
3402
|
init_errors();
|
|
3319
|
-
var ChatErrorCode = /* @__PURE__ */ ((ChatErrorCode2) => {
|
|
3320
|
-
ChatErrorCode2["NETWORK"] = "NETWORK";
|
|
3321
|
-
ChatErrorCode2["TIMEOUT"] = "TIMEOUT";
|
|
3322
|
-
ChatErrorCode2["AUTH_EXPIRED"] = "AUTH_EXPIRED";
|
|
3323
|
-
ChatErrorCode2["AUTH_INVALID"] = "AUTH_INVALID";
|
|
3324
|
-
ChatErrorCode2["RATE_LIMIT"] = "RATE_LIMIT";
|
|
3325
|
-
ChatErrorCode2["PROVIDER_ERROR"] = "PROVIDER_ERROR";
|
|
3326
|
-
ChatErrorCode2["MODEL_NOT_FOUND"] = "MODEL_NOT_FOUND";
|
|
3327
|
-
ChatErrorCode2["MODEL_OVERLOADED"] = "MODEL_OVERLOADED";
|
|
3328
|
-
ChatErrorCode2["CONTEXT_OVERFLOW"] = "CONTEXT_OVERFLOW";
|
|
3329
|
-
ChatErrorCode2["INVALID_INPUT"] = "INVALID_INPUT";
|
|
3330
|
-
ChatErrorCode2["INVALID_RESPONSE"] = "INVALID_RESPONSE";
|
|
3331
|
-
ChatErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
3332
|
-
ChatErrorCode2["BACKEND_NOT_INSTALLED"] = "BACKEND_NOT_INSTALLED";
|
|
3333
|
-
ChatErrorCode2["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
|
|
3334
|
-
ChatErrorCode2["STORAGE_ERROR"] = "STORAGE_ERROR";
|
|
3335
|
-
ChatErrorCode2["SESSION_EXPIRED"] = "SESSION_EXPIRED";
|
|
3336
|
-
ChatErrorCode2["DISPOSED"] = "DISPOSED";
|
|
3337
|
-
ChatErrorCode2["ABORTED"] = "ABORTED";
|
|
3338
|
-
ChatErrorCode2["INVALID_TRANSITION"] = "INVALID_TRANSITION";
|
|
3339
|
-
ChatErrorCode2["REENTRANCY"] = "REENTRANCY";
|
|
3340
|
-
return ChatErrorCode2;
|
|
3341
|
-
})(ChatErrorCode || {});
|
|
3342
3403
|
var ChatError = class extends AgentSDKError {
|
|
3343
3404
|
code;
|
|
3344
3405
|
retryable;
|
|
3345
3406
|
retryAfter;
|
|
3346
3407
|
timestamp;
|
|
3347
3408
|
constructor(message, options) {
|
|
3348
|
-
super(message, {
|
|
3409
|
+
super(message, {
|
|
3410
|
+
cause: options.cause,
|
|
3411
|
+
code: options.code,
|
|
3412
|
+
retryable: options.retryable
|
|
3413
|
+
});
|
|
3349
3414
|
this.name = "ChatError";
|
|
3350
3415
|
this.code = options.code;
|
|
3351
3416
|
this.retryable = options.retryable ?? false;
|
|
@@ -3524,12 +3589,12 @@ function isRetryable(error) {
|
|
|
3524
3589
|
return classified.retryable;
|
|
3525
3590
|
}
|
|
3526
3591
|
function sleep(ms, signal) {
|
|
3527
|
-
return new Promise((
|
|
3592
|
+
return new Promise((resolve, reject) => {
|
|
3528
3593
|
if (signal?.aborted) {
|
|
3529
3594
|
reject(new ChatError("Retry aborted", { code: "ABORTED" /* ABORTED */ }));
|
|
3530
3595
|
return;
|
|
3531
3596
|
}
|
|
3532
|
-
const timer = setTimeout(
|
|
3597
|
+
const timer = setTimeout(resolve, ms);
|
|
3533
3598
|
signal?.addEventListener(
|
|
3534
3599
|
"abort",
|
|
3535
3600
|
() => {
|
|
@@ -3853,6 +3918,35 @@ var CancellableTimeout = class {
|
|
|
3853
3918
|
}
|
|
3854
3919
|
};
|
|
3855
3920
|
|
|
3921
|
+
// src/chat/listener-set.ts
|
|
3922
|
+
var ListenerSet = class {
|
|
3923
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
3924
|
+
/** Add a listener. Returns an unsubscribe function. */
|
|
3925
|
+
add(callback) {
|
|
3926
|
+
this._listeners.add(callback);
|
|
3927
|
+
return () => {
|
|
3928
|
+
this._listeners.delete(callback);
|
|
3929
|
+
};
|
|
3930
|
+
}
|
|
3931
|
+
/** Notify all listeners with the given arguments. Errors are isolated per listener. */
|
|
3932
|
+
notify(...args) {
|
|
3933
|
+
for (const cb of this._listeners) {
|
|
3934
|
+
try {
|
|
3935
|
+
cb(...args);
|
|
3936
|
+
} catch {
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
/** Remove all listeners. */
|
|
3941
|
+
clear() {
|
|
3942
|
+
this._listeners.clear();
|
|
3943
|
+
}
|
|
3944
|
+
/** Current number of listeners. */
|
|
3945
|
+
get size() {
|
|
3946
|
+
return this._listeners.size;
|
|
3947
|
+
}
|
|
3948
|
+
};
|
|
3949
|
+
|
|
3856
3950
|
// src/chat/runtime.ts
|
|
3857
3951
|
var ChatRuntime = class {
|
|
3858
3952
|
_state;
|
|
@@ -3860,26 +3954,29 @@ var ChatRuntime = class {
|
|
|
3860
3954
|
_backends;
|
|
3861
3955
|
_sessionStore;
|
|
3862
3956
|
_contextConfig;
|
|
3957
|
+
_ctxManager;
|
|
3863
3958
|
_middleware;
|
|
3864
3959
|
_tools = /* @__PURE__ */ new Map();
|
|
3865
3960
|
_retryConfig;
|
|
3866
3961
|
_contextStats = /* @__PURE__ */ new Map();
|
|
3962
|
+
_sessionUsage = /* @__PURE__ */ new Map();
|
|
3963
|
+
_modelContextWindows = /* @__PURE__ */ new Map();
|
|
3867
3964
|
_onContextTrimmed;
|
|
3868
3965
|
_streamTimeoutMs;
|
|
3869
|
-
_sessionListeners =
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
_currentModel;
|
|
3873
|
-
_activeSessionId = null;
|
|
3966
|
+
_sessionListeners = new ListenerSet();
|
|
3967
|
+
_adapterPool = /* @__PURE__ */ new Map();
|
|
3968
|
+
_defaultBackend;
|
|
3874
3969
|
_abortController = null;
|
|
3875
3970
|
constructor(options) {
|
|
3876
3971
|
this._state = new StateMachine("idle", RUNTIME_TRANSITIONS);
|
|
3877
3972
|
this._guard = new ChatReentrancyGuard();
|
|
3878
3973
|
this._backends = options.backends;
|
|
3879
|
-
this.
|
|
3880
|
-
this._currentModel = options.defaultModel;
|
|
3974
|
+
this._defaultBackend = options.defaultBackend;
|
|
3881
3975
|
this._sessionStore = options.sessionStore;
|
|
3882
3976
|
this._contextConfig = options.context;
|
|
3977
|
+
if (this._contextConfig) {
|
|
3978
|
+
this._ctxManager = new ContextWindowManager(this._contextConfig);
|
|
3979
|
+
}
|
|
3883
3980
|
this._middleware = [...options.middleware ?? []];
|
|
3884
3981
|
this._retryConfig = options.retryConfig;
|
|
3885
3982
|
this._onContextTrimmed = options.onContextTrimmed;
|
|
@@ -3890,6 +3987,11 @@ var ChatRuntime = class {
|
|
|
3890
3987
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
3891
3988
|
);
|
|
3892
3989
|
}
|
|
3990
|
+
if (options.tools) {
|
|
3991
|
+
for (const tool of options.tools) {
|
|
3992
|
+
this._tools.set(tool.name, tool);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3893
3995
|
}
|
|
3894
3996
|
// ── Lifecycle ──────────────────────────────────────────────
|
|
3895
3997
|
get status() {
|
|
@@ -3901,24 +4003,23 @@ var ChatRuntime = class {
|
|
|
3901
4003
|
this._abortController?.dispose();
|
|
3902
4004
|
this._abortController = null;
|
|
3903
4005
|
this._state.transition("disposed");
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
return this._activeSessionId;
|
|
4006
|
+
for (const adapter of this._adapterPool.values()) {
|
|
4007
|
+
try {
|
|
4008
|
+
await adapter.dispose();
|
|
4009
|
+
} catch {
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
this._adapterPool.clear();
|
|
3912
4013
|
}
|
|
4014
|
+
// ── Sessions ───────────────────────────────────────────────
|
|
3913
4015
|
async createSession(options) {
|
|
3914
4016
|
this.assertNotDisposed();
|
|
3915
4017
|
const config = {
|
|
3916
|
-
model: options.config?.model ??
|
|
3917
|
-
backend: options.config?.backend ?? this.
|
|
4018
|
+
model: options.config?.model ?? "",
|
|
4019
|
+
backend: options.config?.backend ?? this._defaultBackend,
|
|
3918
4020
|
...options.config
|
|
3919
4021
|
};
|
|
3920
4022
|
const session = await this._sessionStore.createSession({ ...options, config });
|
|
3921
|
-
this._activeSessionId = session.id;
|
|
3922
4023
|
this._notifySessionChange();
|
|
3923
4024
|
return session;
|
|
3924
4025
|
}
|
|
@@ -3938,36 +4039,12 @@ var ChatRuntime = class {
|
|
|
3938
4039
|
if (!session) return;
|
|
3939
4040
|
await this._sessionStore.deleteSession(cid);
|
|
3940
4041
|
this._contextStats.delete(cid);
|
|
3941
|
-
|
|
3942
|
-
this._activeSessionId = null;
|
|
3943
|
-
}
|
|
3944
|
-
this._notifySessionChange();
|
|
3945
|
-
}
|
|
3946
|
-
async archiveSession(id) {
|
|
3947
|
-
this.assertNotDisposed();
|
|
3948
|
-
const cid = toChatId(id);
|
|
3949
|
-
await this._sessionStore.archiveSession(cid);
|
|
4042
|
+
this._sessionUsage.delete(cid);
|
|
3950
4043
|
this._notifySessionChange();
|
|
3951
4044
|
}
|
|
3952
|
-
async switchSession(id) {
|
|
3953
|
-
this.assertNotDisposed();
|
|
3954
|
-
const cid = toChatId(id);
|
|
3955
|
-
const session = await this._sessionStore.getSession(cid);
|
|
3956
|
-
if (!session) {
|
|
3957
|
-
throw new ChatError(
|
|
3958
|
-
`Session "${id}" not found`,
|
|
3959
|
-
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
3960
|
-
);
|
|
3961
|
-
}
|
|
3962
|
-
this._activeSessionId = session.id;
|
|
3963
|
-
return session;
|
|
3964
|
-
}
|
|
3965
4045
|
// ── Messaging ──────────────────────────────────────────────
|
|
3966
4046
|
async *send(sessionId, message, options) {
|
|
3967
|
-
this.
|
|
3968
|
-
if (!message || message.trim().length === 0) {
|
|
3969
|
-
throw new ChatError("Message cannot be empty", { code: "INVALID_INPUT" /* INVALID_INPUT */ });
|
|
3970
|
-
}
|
|
4047
|
+
this.validateSendInput(message, options);
|
|
3971
4048
|
this._guard.acquire();
|
|
3972
4049
|
const cid = toChatId(sessionId);
|
|
3973
4050
|
this._abortController = new ChatAbortController(options?.signal);
|
|
@@ -3976,150 +4053,274 @@ var ChatRuntime = class {
|
|
|
3976
4053
|
this._state.transition("idle");
|
|
3977
4054
|
}
|
|
3978
4055
|
this._state.transition("streaming");
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
throw new ChatError(
|
|
3982
|
-
`Session "${cid}" not found`,
|
|
3983
|
-
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
3984
|
-
);
|
|
3985
|
-
}
|
|
3986
|
-
const middlewareContext = {
|
|
4056
|
+
await this.loadSession(cid);
|
|
4057
|
+
const mwCtx = {
|
|
3987
4058
|
sessionId: cid,
|
|
3988
4059
|
signal: this._abortController.signal
|
|
3989
4060
|
};
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
const updatedSession = await this._sessionStore.getSession(cid);
|
|
3998
|
-
let messagesToSend = updatedSession.messages;
|
|
3999
|
-
if (this._contextConfig) {
|
|
4000
|
-
const ctxManager = new ContextWindowManager(this._contextConfig);
|
|
4001
|
-
const result = await ctxManager.fitMessagesAsync(messagesToSend);
|
|
4002
|
-
this._contextStats.set(cid, {
|
|
4003
|
-
totalTokens: result.totalTokens,
|
|
4004
|
-
removedCount: result.removedCount,
|
|
4005
|
-
wasTruncated: result.wasTruncated,
|
|
4006
|
-
availableBudget: ctxManager.availableBudget
|
|
4007
|
-
});
|
|
4008
|
-
if (result.wasTruncated && this._onContextTrimmed) {
|
|
4009
|
-
const keptIds = new Set(result.messages.map((m) => m.id));
|
|
4010
|
-
const removed = messagesToSend.filter((m) => !keptIds.has(m.id));
|
|
4011
|
-
if (removed.length > 0) {
|
|
4012
|
-
try {
|
|
4013
|
-
this._onContextTrimmed(cid, removed);
|
|
4014
|
-
} catch {
|
|
4015
|
-
}
|
|
4016
|
-
}
|
|
4017
|
-
}
|
|
4018
|
-
messagesToSend = result.messages;
|
|
4061
|
+
const userMessage = await this.applyBeforeSendMiddleware(
|
|
4062
|
+
this.createUserMessage(message),
|
|
4063
|
+
mwCtx
|
|
4064
|
+
);
|
|
4065
|
+
if (userMessage === null) {
|
|
4066
|
+
this._state.transition("idle");
|
|
4067
|
+
return;
|
|
4019
4068
|
}
|
|
4020
|
-
const
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4069
|
+
const updatedSession = await this.persistAndReload(cid, userMessage);
|
|
4070
|
+
const sessionForAdapter = await this.trimSessionContext(cid, updatedSession, options.model);
|
|
4071
|
+
const stream = await this.prepareEventStream(
|
|
4072
|
+
cid,
|
|
4073
|
+
sessionForAdapter,
|
|
4074
|
+
updatedSession,
|
|
4075
|
+
message,
|
|
4076
|
+
options
|
|
4077
|
+
);
|
|
4025
4078
|
const accumulator = new MessageAccumulator();
|
|
4026
|
-
const runtimeTools = this._tools.size > 0 ? this.injectToolContext([...this._tools.values()], {
|
|
4027
|
-
sessionId: cid,
|
|
4028
|
-
custom: updatedSession.metadata?.custom
|
|
4029
|
-
}) : void 0;
|
|
4030
|
-
const streamOptions = {
|
|
4031
|
-
...options,
|
|
4032
|
-
signal: this._abortController.signal,
|
|
4033
|
-
model: options?.model ?? this._currentModel,
|
|
4034
|
-
tools: runtimeTools
|
|
4035
|
-
};
|
|
4036
|
-
const stream = await this.createStreamWithRetry(adapter, sessionForAdapter, message, streamOptions);
|
|
4037
4079
|
const eventSource = this._streamTimeoutMs ? withStreamWatchdog(stream, { timeoutMs: this._streamTimeoutMs, signal: this._abortController.signal }) : stream;
|
|
4038
4080
|
for await (const event of eventSource) {
|
|
4039
4081
|
if (this._abortController.isAborted) break;
|
|
4040
4082
|
this.feedAccumulator(accumulator, event);
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
}
|
|
4046
|
-
|
|
4047
|
-
if (processedEvent) {
|
|
4048
|
-
yield processedEvent;
|
|
4049
|
-
}
|
|
4050
|
-
}
|
|
4051
|
-
if (this._state.current === "disposed") {
|
|
4052
|
-
return;
|
|
4053
|
-
}
|
|
4054
|
-
let assistantMessage = accumulator.finalize();
|
|
4055
|
-
for (const mw of this._middleware) {
|
|
4056
|
-
if (mw.onAfterReceive) {
|
|
4057
|
-
assistantMessage = await mw.onAfterReceive(assistantMessage, middlewareContext);
|
|
4083
|
+
if (event.type === "usage") {
|
|
4084
|
+
this._sessionUsage.set(cid, {
|
|
4085
|
+
promptTokens: event.promptTokens,
|
|
4086
|
+
completionTokens: event.completionTokens
|
|
4087
|
+
});
|
|
4088
|
+
this.updateContextStatsWithUsage(cid, event.promptTokens, event.completionTokens, options);
|
|
4058
4089
|
}
|
|
4090
|
+
const processed = await this.applyOnEventMiddleware(event, mwCtx);
|
|
4091
|
+
if (processed) yield processed;
|
|
4059
4092
|
}
|
|
4060
|
-
|
|
4061
|
-
this.
|
|
4093
|
+
if (this._state.current === "disposed") return;
|
|
4094
|
+
await this.finalizeAssistantMessage(cid, accumulator, mwCtx);
|
|
4062
4095
|
this._state.transition("idle");
|
|
4063
4096
|
} catch (error) {
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
sessionId: cid,
|
|
4067
|
-
signal: this._abortController?.signal ?? new AbortController().signal
|
|
4068
|
-
};
|
|
4069
|
-
for (const mw of this._middleware) {
|
|
4070
|
-
if (mw.onError) {
|
|
4071
|
-
const result = await mw.onError(processedError, middlewareContext);
|
|
4072
|
-
if (result === null) {
|
|
4073
|
-
if (this._state.canTransition("idle")) {
|
|
4074
|
-
this._state.transition("idle");
|
|
4075
|
-
}
|
|
4076
|
-
return;
|
|
4077
|
-
}
|
|
4078
|
-
processedError = result;
|
|
4079
|
-
}
|
|
4080
|
-
}
|
|
4081
|
-
if (this._state.canTransition("error")) {
|
|
4082
|
-
this._state.transition("error");
|
|
4083
|
-
}
|
|
4084
|
-
throw processedError;
|
|
4097
|
+
const result = await this.handleSendError(error, cid);
|
|
4098
|
+
if (result !== null) throw result;
|
|
4085
4099
|
} finally {
|
|
4086
4100
|
this._guard.release();
|
|
4087
4101
|
this._abortController?.dispose();
|
|
4088
4102
|
this._abortController = null;
|
|
4089
4103
|
}
|
|
4090
4104
|
}
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
// ── Backend / Model ────────────────────────────────────────
|
|
4095
|
-
get currentBackend() {
|
|
4096
|
-
return this._currentBackend;
|
|
4097
|
-
}
|
|
4098
|
-
get currentModel() {
|
|
4099
|
-
return this._currentModel;
|
|
4100
|
-
}
|
|
4101
|
-
async switchBackend(name) {
|
|
4105
|
+
// ── Send Pipeline Stages ──────────────────────────────────────
|
|
4106
|
+
/** Stage 1: Validate send inputs (message content + required fields). */
|
|
4107
|
+
validateSendInput(message, options) {
|
|
4102
4108
|
this.assertNotDisposed();
|
|
4103
|
-
if (!
|
|
4109
|
+
if (!message || message.trim().length === 0) {
|
|
4110
|
+
throw new ChatError("Message cannot be empty", { code: "INVALID_INPUT" /* INVALID_INPUT */ });
|
|
4111
|
+
}
|
|
4112
|
+
if (!options.model) {
|
|
4113
|
+
throw new ChatError(
|
|
4114
|
+
"options.model is required \u2014 caller must specify which model to use",
|
|
4115
|
+
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
4116
|
+
);
|
|
4117
|
+
}
|
|
4118
|
+
if (!options.backend) {
|
|
4119
|
+
throw new ChatError(
|
|
4120
|
+
"options.backend is required \u2014 caller must specify which backend to use",
|
|
4121
|
+
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
4122
|
+
);
|
|
4123
|
+
}
|
|
4124
|
+
if (!options.credentials) {
|
|
4104
4125
|
throw new ChatError(
|
|
4105
|
-
|
|
4126
|
+
"options.credentials is required \u2014 caller must provide authentication credentials",
|
|
4106
4127
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
4107
4128
|
);
|
|
4108
4129
|
}
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4130
|
+
}
|
|
4131
|
+
/** Stage 2: Load session from store. */
|
|
4132
|
+
async loadSession(cid) {
|
|
4133
|
+
const session = await this._sessionStore.getSession(cid);
|
|
4134
|
+
if (!session) {
|
|
4135
|
+
throw new ChatError(
|
|
4136
|
+
`Session "${cid}" not found`,
|
|
4137
|
+
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
4138
|
+
);
|
|
4112
4139
|
}
|
|
4113
|
-
|
|
4140
|
+
return session;
|
|
4114
4141
|
}
|
|
4115
|
-
|
|
4142
|
+
/** Stage 3: Apply onBeforeSend middleware pipeline. Returns null if middleware rejected the send. */
|
|
4143
|
+
async applyBeforeSendMiddleware(userMessage, ctx) {
|
|
4144
|
+
let msg = userMessage;
|
|
4145
|
+
for (const mw of this._middleware) {
|
|
4146
|
+
if (mw.onBeforeSend && msg) {
|
|
4147
|
+
msg = await mw.onBeforeSend(msg, ctx);
|
|
4148
|
+
if (msg === null) return null;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
return msg;
|
|
4152
|
+
}
|
|
4153
|
+
/** Stage 4: Persist user message and reload session with full history. */
|
|
4154
|
+
async persistAndReload(cid, userMessage) {
|
|
4155
|
+
await this._sessionStore.appendMessage(cid, userMessage);
|
|
4156
|
+
return await this._sessionStore.getSession(cid);
|
|
4157
|
+
}
|
|
4158
|
+
/** Stage 5: Auto-trim context window if configured. Returns session snapshot for adapter. */
|
|
4159
|
+
async trimSessionContext(cid, session, model) {
|
|
4160
|
+
if (!this._ctxManager) return session;
|
|
4161
|
+
const ctxManager = this._ctxManager;
|
|
4162
|
+
const lastUsage = this._sessionUsage.get(cid);
|
|
4163
|
+
const modelContextWindow = model ? this._modelContextWindows.get(model) : void 0;
|
|
4164
|
+
if (lastUsage && modelContextWindow) {
|
|
4165
|
+
const result2 = ctxManager.fitMessagesWithUsage(
|
|
4166
|
+
session.messages,
|
|
4167
|
+
lastUsage.promptTokens,
|
|
4168
|
+
modelContextWindow
|
|
4169
|
+
);
|
|
4170
|
+
this._contextStats.set(cid, {
|
|
4171
|
+
totalTokens: result2.totalTokens,
|
|
4172
|
+
removedCount: result2.removedCount,
|
|
4173
|
+
wasTruncated: result2.wasTruncated,
|
|
4174
|
+
availableBudget: Math.max(0, modelContextWindow - result2.totalTokens),
|
|
4175
|
+
realPromptTokens: lastUsage.promptTokens,
|
|
4176
|
+
realCompletionTokens: lastUsage.completionTokens,
|
|
4177
|
+
modelContextWindow
|
|
4178
|
+
});
|
|
4179
|
+
if (result2.wasTruncated && this._onContextTrimmed) {
|
|
4180
|
+
const keptIds = new Set(result2.messages.map((m) => m.id));
|
|
4181
|
+
const removed = session.messages.filter((m) => !keptIds.has(m.id));
|
|
4182
|
+
if (removed.length > 0) {
|
|
4183
|
+
try {
|
|
4184
|
+
this._onContextTrimmed(cid, removed);
|
|
4185
|
+
} catch {
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
return { ...session, messages: result2.messages };
|
|
4190
|
+
}
|
|
4191
|
+
const result = await ctxManager.fitMessagesAsync(session.messages);
|
|
4192
|
+
this._contextStats.set(cid, {
|
|
4193
|
+
totalTokens: result.totalTokens,
|
|
4194
|
+
removedCount: result.removedCount,
|
|
4195
|
+
wasTruncated: result.wasTruncated,
|
|
4196
|
+
availableBudget: ctxManager.availableBudget,
|
|
4197
|
+
modelContextWindow
|
|
4198
|
+
});
|
|
4199
|
+
if (result.wasTruncated && this._onContextTrimmed) {
|
|
4200
|
+
const keptIds = new Set(result.messages.map((m) => m.id));
|
|
4201
|
+
const removed = session.messages.filter((m) => !keptIds.has(m.id));
|
|
4202
|
+
if (removed.length > 0) {
|
|
4203
|
+
try {
|
|
4204
|
+
this._onContextTrimmed(cid, removed);
|
|
4205
|
+
} catch {
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
return { ...session, messages: result.messages };
|
|
4210
|
+
}
|
|
4211
|
+
/** Update context stats with real usage data from a usage event. */
|
|
4212
|
+
updateContextStatsWithUsage(cid, promptTokens, completionTokens, options) {
|
|
4213
|
+
const modelContextWindow = options.model ? this._modelContextWindows.get(options.model) : void 0;
|
|
4214
|
+
const existing = this._contextStats.get(cid);
|
|
4215
|
+
this._contextStats.set(cid, {
|
|
4216
|
+
totalTokens: promptTokens,
|
|
4217
|
+
removedCount: existing?.removedCount ?? 0,
|
|
4218
|
+
wasTruncated: existing?.wasTruncated ?? false,
|
|
4219
|
+
availableBudget: modelContextWindow ? Math.max(0, modelContextWindow - promptTokens) : existing?.availableBudget ?? 0,
|
|
4220
|
+
realPromptTokens: promptTokens,
|
|
4221
|
+
realCompletionTokens: completionTokens,
|
|
4222
|
+
modelContextWindow
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
4225
|
+
/** Stage 6: Prepare event stream — adapter with retry, tool injection. */
|
|
4226
|
+
async prepareEventStream(cid, sessionForAdapter, fullSession, message, options) {
|
|
4227
|
+
const adapter = await this.getOrCreateAdapterWithRetry(options.backend, options.credentials);
|
|
4228
|
+
const runtimeTools = this._tools.size > 0 ? this.injectToolContext([...this._tools.values()], {
|
|
4229
|
+
sessionId: cid,
|
|
4230
|
+
custom: fullSession.metadata?.custom
|
|
4231
|
+
}) : void 0;
|
|
4232
|
+
const streamOptions = {
|
|
4233
|
+
signal: this._abortController.signal,
|
|
4234
|
+
model: options.model,
|
|
4235
|
+
systemPrompt: options.systemPrompt,
|
|
4236
|
+
tools: runtimeTools
|
|
4237
|
+
};
|
|
4238
|
+
return this.createStreamWithRetry(
|
|
4239
|
+
adapter,
|
|
4240
|
+
sessionForAdapter,
|
|
4241
|
+
message,
|
|
4242
|
+
streamOptions,
|
|
4243
|
+
options.backend,
|
|
4244
|
+
options.credentials
|
|
4245
|
+
);
|
|
4246
|
+
}
|
|
4247
|
+
/** Stage 7: Apply onEvent middleware pipeline (sequential transform/suppress). */
|
|
4248
|
+
async applyOnEventMiddleware(event, ctx) {
|
|
4249
|
+
let processed = event;
|
|
4250
|
+
for (const mw of this._middleware) {
|
|
4251
|
+
if (mw.onEvent && processed) {
|
|
4252
|
+
processed = await mw.onEvent(processed, ctx);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
return processed;
|
|
4256
|
+
}
|
|
4257
|
+
/** Stage 8: Finalize accumulator, apply afterReceive middleware, persist assistant message. */
|
|
4258
|
+
async finalizeAssistantMessage(cid, accumulator, ctx) {
|
|
4259
|
+
let assistantMessage = accumulator.finalize();
|
|
4260
|
+
for (const mw of this._middleware) {
|
|
4261
|
+
if (mw.onAfterReceive) {
|
|
4262
|
+
assistantMessage = await mw.onAfterReceive(assistantMessage, ctx);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
await this._sessionStore.appendMessage(cid, assistantMessage);
|
|
4266
|
+
this._notifySessionChange();
|
|
4267
|
+
}
|
|
4268
|
+
/** Stage 9: Error handling — apply onError middleware, transition state. Returns null if suppressed. */
|
|
4269
|
+
async handleSendError(error, cid) {
|
|
4270
|
+
let processedError = error instanceof Error ? error : new Error(String(error));
|
|
4271
|
+
const ctx = {
|
|
4272
|
+
sessionId: cid,
|
|
4273
|
+
signal: this._abortController?.signal ?? new AbortController().signal
|
|
4274
|
+
};
|
|
4275
|
+
for (const mw of this._middleware) {
|
|
4276
|
+
if (mw.onError) {
|
|
4277
|
+
const result = await mw.onError(processedError, ctx);
|
|
4278
|
+
if (result === null) {
|
|
4279
|
+
if (this._state.canTransition("idle")) {
|
|
4280
|
+
this._state.transition("idle");
|
|
4281
|
+
}
|
|
4282
|
+
return null;
|
|
4283
|
+
}
|
|
4284
|
+
processedError = result;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
if (this._state.canTransition("error")) {
|
|
4288
|
+
this._state.transition("error");
|
|
4289
|
+
}
|
|
4290
|
+
return processedError;
|
|
4291
|
+
}
|
|
4292
|
+
abort() {
|
|
4293
|
+
this._abortController?.abort("User abort");
|
|
4294
|
+
}
|
|
4295
|
+
// ── Backend / Model ────────────────────────────────────────
|
|
4296
|
+
async listModels(options) {
|
|
4116
4297
|
this.assertNotDisposed();
|
|
4117
|
-
|
|
4298
|
+
let models = [];
|
|
4299
|
+
const firstAdapter = [...this._adapterPool.values()][0];
|
|
4300
|
+
if (firstAdapter) {
|
|
4301
|
+
try {
|
|
4302
|
+
models = await firstAdapter.listModels();
|
|
4303
|
+
} catch {
|
|
4304
|
+
return [];
|
|
4305
|
+
}
|
|
4306
|
+
} else if (options?.backend && options?.credentials) {
|
|
4307
|
+
try {
|
|
4308
|
+
const adapter = await this.getOrCreateAdapter(options.backend, options.credentials);
|
|
4309
|
+
models = await adapter.listModels();
|
|
4310
|
+
} catch {
|
|
4311
|
+
return [];
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
for (const model of models) {
|
|
4315
|
+
if (model.contextWindow != null) {
|
|
4316
|
+
this._modelContextWindows.set(model.id, model.contextWindow);
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
return models;
|
|
4118
4320
|
}
|
|
4119
|
-
async
|
|
4321
|
+
async listBackends() {
|
|
4120
4322
|
this.assertNotDisposed();
|
|
4121
|
-
|
|
4122
|
-
return adapter.listModels();
|
|
4323
|
+
return Object.keys(this._backends).map((name) => ({ name }));
|
|
4123
4324
|
}
|
|
4124
4325
|
// ── Tools ──────────────────────────────────────────────────
|
|
4125
4326
|
get registeredTools() {
|
|
@@ -4144,37 +4345,46 @@ var ChatRuntime = class {
|
|
|
4144
4345
|
if (idx >= 0) this._middleware.splice(idx, 1);
|
|
4145
4346
|
}
|
|
4146
4347
|
// ── Context Stats ─────────────────────────────────────────
|
|
4147
|
-
getContextStats(sessionId) {
|
|
4348
|
+
async getContextStats(sessionId) {
|
|
4148
4349
|
const cid = toChatId(sessionId);
|
|
4149
4350
|
return this._contextStats.get(cid) ?? null;
|
|
4150
4351
|
}
|
|
4151
4352
|
// ── Session Subscription ──────────────────────────────────
|
|
4152
4353
|
onSessionChange(callback) {
|
|
4153
|
-
this._sessionListeners.add(callback);
|
|
4154
|
-
return () => {
|
|
4155
|
-
this._sessionListeners.delete(callback);
|
|
4156
|
-
};
|
|
4354
|
+
return this._sessionListeners.add(callback);
|
|
4157
4355
|
}
|
|
4158
4356
|
_notifySessionChange() {
|
|
4159
|
-
|
|
4160
|
-
try {
|
|
4161
|
-
cb();
|
|
4162
|
-
} catch {
|
|
4163
|
-
}
|
|
4164
|
-
}
|
|
4357
|
+
this._sessionListeners.notify();
|
|
4165
4358
|
}
|
|
4166
4359
|
// ── Private Helpers ────────────────────────────────────────
|
|
4167
|
-
async getOrCreateAdapter() {
|
|
4168
|
-
|
|
4169
|
-
const
|
|
4360
|
+
async getOrCreateAdapter(backend, credentials) {
|
|
4361
|
+
const key = this.getPoolKey(backend, credentials);
|
|
4362
|
+
const existing = this._adapterPool.get(key);
|
|
4363
|
+
if (existing) return existing;
|
|
4364
|
+
for (const [oldKey, oldAdapter] of this._adapterPool) {
|
|
4365
|
+
if (oldKey.startsWith(backend + ":")) {
|
|
4366
|
+
try {
|
|
4367
|
+
await oldAdapter.dispose();
|
|
4368
|
+
} catch {
|
|
4369
|
+
}
|
|
4370
|
+
this._adapterPool.delete(oldKey);
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
const factory = this._backends[backend];
|
|
4170
4374
|
if (!factory) {
|
|
4171
4375
|
throw new ChatError(
|
|
4172
|
-
`Backend "${
|
|
4376
|
+
`Backend "${backend}" not found`,
|
|
4173
4377
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
4174
4378
|
);
|
|
4175
4379
|
}
|
|
4176
|
-
|
|
4177
|
-
|
|
4380
|
+
const adapter = await factory(credentials);
|
|
4381
|
+
this._adapterPool.set(key, adapter);
|
|
4382
|
+
return adapter;
|
|
4383
|
+
}
|
|
4384
|
+
getPoolKey(backend, credentials) {
|
|
4385
|
+
const token = credentials.accessToken;
|
|
4386
|
+
const hash = token.length > 16 ? token.slice(0, 8) + token.slice(-8) : token;
|
|
4387
|
+
return `${backend}:${hash}`;
|
|
4178
4388
|
}
|
|
4179
4389
|
/** Wrap each tool's execute to inject ToolContext as 2nd argument */
|
|
4180
4390
|
injectToolContext(tools, context) {
|
|
@@ -4206,17 +4416,25 @@ var ChatRuntime = class {
|
|
|
4206
4416
|
}
|
|
4207
4417
|
}
|
|
4208
4418
|
/** Get or create adapter with retry on connection errors */
|
|
4209
|
-
async getOrCreateAdapterWithRetry() {
|
|
4419
|
+
async getOrCreateAdapterWithRetry(backend, credentials) {
|
|
4210
4420
|
const maxAttempts = this._retryConfig?.maxAttempts ?? 1;
|
|
4211
4421
|
const delayMs = this._retryConfig?.delayMs ?? 0;
|
|
4212
4422
|
let lastError;
|
|
4213
4423
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
4214
4424
|
try {
|
|
4215
|
-
return await this.getOrCreateAdapter();
|
|
4425
|
+
return await this.getOrCreateAdapter(backend, credentials);
|
|
4216
4426
|
} catch (err) {
|
|
4217
4427
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
4218
4428
|
if (attempt < maxAttempts) {
|
|
4219
|
-
this.
|
|
4429
|
+
const key = this.getPoolKey(backend, credentials);
|
|
4430
|
+
const old = this._adapterPool.get(key);
|
|
4431
|
+
if (old) {
|
|
4432
|
+
try {
|
|
4433
|
+
await old.dispose();
|
|
4434
|
+
} catch {
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
this._adapterPool.delete(key);
|
|
4220
4438
|
await delay(delayMs);
|
|
4221
4439
|
}
|
|
4222
4440
|
}
|
|
@@ -4229,7 +4447,7 @@ var ChatRuntime = class {
|
|
|
4229
4447
|
* retries with a fresh adapter. Once first event is received,
|
|
4230
4448
|
* the stream is committed (no more retries).
|
|
4231
4449
|
*/
|
|
4232
|
-
async createStreamWithRetry(adapter, session, message, options) {
|
|
4450
|
+
async createStreamWithRetry(adapter, session, message, options, backend, credentials) {
|
|
4233
4451
|
const maxAttempts = this._retryConfig?.maxAttempts ?? 1;
|
|
4234
4452
|
const delayMs = this._retryConfig?.delayMs ?? 0;
|
|
4235
4453
|
let lastError;
|
|
@@ -4250,13 +4468,14 @@ var ChatRuntime = class {
|
|
|
4250
4468
|
} catch (err) {
|
|
4251
4469
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
4252
4470
|
if (attempt < maxAttempts) {
|
|
4253
|
-
|
|
4254
|
-
await
|
|
4255
|
-
|
|
4471
|
+
try {
|
|
4472
|
+
await currentAdapter.dispose();
|
|
4473
|
+
} catch {
|
|
4256
4474
|
}
|
|
4257
|
-
this.
|
|
4475
|
+
const key = this.getPoolKey(backend, credentials);
|
|
4476
|
+
this._adapterPool.delete(key);
|
|
4258
4477
|
await delay(delayMs);
|
|
4259
|
-
currentAdapter = await this.getOrCreateAdapter();
|
|
4478
|
+
currentAdapter = await this.getOrCreateAdapter(backend, credentials);
|
|
4260
4479
|
}
|
|
4261
4480
|
}
|
|
4262
4481
|
}
|
|
@@ -4264,16 +4483,17 @@ var ChatRuntime = class {
|
|
|
4264
4483
|
}
|
|
4265
4484
|
};
|
|
4266
4485
|
function delay(ms) {
|
|
4267
|
-
return new Promise((
|
|
4486
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4268
4487
|
}
|
|
4269
4488
|
function createChatRuntime(options) {
|
|
4270
4489
|
return new ChatRuntime(options);
|
|
4271
4490
|
}
|
|
4272
4491
|
|
|
4273
4492
|
// src/chat/storage.ts
|
|
4493
|
+
init_errors2();
|
|
4274
4494
|
init_errors();
|
|
4275
4495
|
var StorageError = class extends AgentSDKError {
|
|
4276
|
-
/** Machine-readable error code */
|
|
4496
|
+
/** Machine-readable error code from the unified ErrorCode enum */
|
|
4277
4497
|
code;
|
|
4278
4498
|
constructor(message, code) {
|
|
4279
4499
|
super(message);
|
|
@@ -4310,7 +4530,7 @@ var InMemoryStorage = class {
|
|
|
4310
4530
|
if (this.data.has(key)) {
|
|
4311
4531
|
throw new StorageError(
|
|
4312
4532
|
`Item with key "${key}" already exists`,
|
|
4313
|
-
"
|
|
4533
|
+
"STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
|
|
4314
4534
|
);
|
|
4315
4535
|
}
|
|
4316
4536
|
this.data.set(key, structuredClone(item));
|
|
@@ -4320,7 +4540,7 @@ var InMemoryStorage = class {
|
|
|
4320
4540
|
if (!this.data.has(key)) {
|
|
4321
4541
|
throw new StorageError(
|
|
4322
4542
|
`Item with key "${key}" not found`,
|
|
4323
|
-
"
|
|
4543
|
+
"STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
|
|
4324
4544
|
);
|
|
4325
4545
|
}
|
|
4326
4546
|
this.data.set(key, structuredClone(item));
|
|
@@ -4330,7 +4550,7 @@ var InMemoryStorage = class {
|
|
|
4330
4550
|
if (!this.data.has(key)) {
|
|
4331
4551
|
throw new StorageError(
|
|
4332
4552
|
`Item with key "${key}" not found`,
|
|
4333
|
-
"
|
|
4553
|
+
"STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
|
|
4334
4554
|
);
|
|
4335
4555
|
}
|
|
4336
4556
|
this.data.delete(key);
|
|
@@ -4354,25 +4574,25 @@ var FileStorage = class {
|
|
|
4354
4574
|
constructor(options) {
|
|
4355
4575
|
this.directory = options.directory;
|
|
4356
4576
|
this.extension = options.extension ?? ".json";
|
|
4357
|
-
this.
|
|
4577
|
+
this.ensureDirectorySync();
|
|
4358
4578
|
}
|
|
4359
4579
|
/** @inheritdoc */
|
|
4360
4580
|
async get(key) {
|
|
4361
4581
|
const filePath = this.keyToPath(key);
|
|
4362
|
-
if (!
|
|
4582
|
+
if (!await this.fileExists(filePath)) {
|
|
4363
4583
|
return null;
|
|
4364
4584
|
}
|
|
4365
|
-
return this.
|
|
4585
|
+
return this.readJsonFile(filePath);
|
|
4366
4586
|
}
|
|
4367
4587
|
/** @inheritdoc */
|
|
4368
4588
|
async list(options) {
|
|
4369
|
-
this.
|
|
4370
|
-
const files =
|
|
4589
|
+
await this.ensureDirectoryAsync();
|
|
4590
|
+
const files = (await readdir(this.directory)).filter(
|
|
4371
4591
|
(f) => f.endsWith(this.extension)
|
|
4372
4592
|
);
|
|
4373
4593
|
let items = [];
|
|
4374
4594
|
for (const file of files) {
|
|
4375
|
-
const item = this.
|
|
4595
|
+
const item = await this.readJsonFile(join(this.directory, file));
|
|
4376
4596
|
items.push(item);
|
|
4377
4597
|
}
|
|
4378
4598
|
if (options?.filter) {
|
|
@@ -4392,55 +4612,55 @@ var FileStorage = class {
|
|
|
4392
4612
|
/** @inheritdoc */
|
|
4393
4613
|
async create(key, item) {
|
|
4394
4614
|
const filePath = this.keyToPath(key);
|
|
4395
|
-
if (
|
|
4615
|
+
if (await this.fileExists(filePath)) {
|
|
4396
4616
|
throw new StorageError(
|
|
4397
4617
|
`Item with key "${key}" already exists`,
|
|
4398
|
-
"
|
|
4618
|
+
"STORAGE_DUPLICATE_KEY" /* STORAGE_DUPLICATE_KEY */
|
|
4399
4619
|
);
|
|
4400
4620
|
}
|
|
4401
|
-
this.
|
|
4621
|
+
await this.writeJsonFile(filePath, item);
|
|
4402
4622
|
}
|
|
4403
4623
|
/** @inheritdoc */
|
|
4404
4624
|
async update(key, item) {
|
|
4405
4625
|
const filePath = this.keyToPath(key);
|
|
4406
|
-
if (!
|
|
4626
|
+
if (!await this.fileExists(filePath)) {
|
|
4407
4627
|
throw new StorageError(
|
|
4408
4628
|
`Item with key "${key}" not found`,
|
|
4409
|
-
"
|
|
4629
|
+
"STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
|
|
4410
4630
|
);
|
|
4411
4631
|
}
|
|
4412
|
-
this.
|
|
4632
|
+
await this.writeJsonFile(filePath, item);
|
|
4413
4633
|
}
|
|
4414
4634
|
/** @inheritdoc */
|
|
4415
4635
|
async delete(key) {
|
|
4416
4636
|
const filePath = this.keyToPath(key);
|
|
4417
|
-
if (!
|
|
4637
|
+
if (!await this.fileExists(filePath)) {
|
|
4418
4638
|
throw new StorageError(
|
|
4419
4639
|
`Item with key "${key}" not found`,
|
|
4420
|
-
"
|
|
4640
|
+
"STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */
|
|
4421
4641
|
);
|
|
4422
4642
|
}
|
|
4423
|
-
|
|
4643
|
+
await unlink(filePath);
|
|
4424
4644
|
}
|
|
4425
4645
|
/** @inheritdoc */
|
|
4426
4646
|
async has(key) {
|
|
4427
|
-
return
|
|
4647
|
+
return this.fileExists(this.keyToPath(key));
|
|
4428
4648
|
}
|
|
4429
4649
|
/** @inheritdoc */
|
|
4430
4650
|
async count() {
|
|
4431
|
-
this.
|
|
4432
|
-
return
|
|
4651
|
+
await this.ensureDirectoryAsync();
|
|
4652
|
+
return (await readdir(this.directory)).filter(
|
|
4433
4653
|
(f) => f.endsWith(this.extension)
|
|
4434
4654
|
).length;
|
|
4435
4655
|
}
|
|
4436
4656
|
/** @inheritdoc */
|
|
4437
4657
|
async clear() {
|
|
4438
|
-
this.
|
|
4439
|
-
const files =
|
|
4658
|
+
await this.ensureDirectoryAsync();
|
|
4659
|
+
const files = (await readdir(this.directory)).filter(
|
|
4440
4660
|
(f) => f.endsWith(this.extension)
|
|
4441
4661
|
);
|
|
4442
4662
|
for (const file of files) {
|
|
4443
|
-
|
|
4663
|
+
await unlink(join(this.directory, file));
|
|
4444
4664
|
}
|
|
4445
4665
|
}
|
|
4446
4666
|
keyToPath(key) {
|
|
@@ -4450,42 +4670,58 @@ var FileStorage = class {
|
|
|
4450
4670
|
);
|
|
4451
4671
|
return join(this.directory, `${safeKey}${this.extension}`);
|
|
4452
4672
|
}
|
|
4453
|
-
|
|
4673
|
+
/** Sync directory init — used only in constructor (one-time). */
|
|
4674
|
+
ensureDirectorySync() {
|
|
4454
4675
|
if (!existsSync(this.directory)) {
|
|
4455
4676
|
mkdirSync(this.directory, { recursive: true });
|
|
4456
4677
|
}
|
|
4457
4678
|
}
|
|
4458
|
-
|
|
4679
|
+
/** Async directory init — used in operations. */
|
|
4680
|
+
async ensureDirectoryAsync() {
|
|
4681
|
+
if (!await this.fileExists(this.directory)) {
|
|
4682
|
+
await mkdir(this.directory, { recursive: true });
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
async fileExists(filePath) {
|
|
4459
4686
|
try {
|
|
4460
|
-
|
|
4687
|
+
await access(filePath);
|
|
4688
|
+
return true;
|
|
4689
|
+
} catch {
|
|
4690
|
+
return false;
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
async readJsonFile(filePath) {
|
|
4694
|
+
try {
|
|
4695
|
+
const content = await readFile(filePath, "utf-8");
|
|
4461
4696
|
return JSON.parse(content);
|
|
4462
4697
|
} catch (error) {
|
|
4463
4698
|
if (error instanceof SyntaxError) {
|
|
4464
4699
|
throw new StorageError(
|
|
4465
4700
|
`Failed to parse file: ${filePath}`,
|
|
4466
|
-
"
|
|
4701
|
+
"STORAGE_SERIALIZATION_ERROR" /* STORAGE_SERIALIZATION_ERROR */
|
|
4467
4702
|
);
|
|
4468
4703
|
}
|
|
4469
4704
|
throw new StorageError(
|
|
4470
4705
|
`Failed to read file: ${filePath}`,
|
|
4471
|
-
"
|
|
4706
|
+
"STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
|
|
4472
4707
|
);
|
|
4473
4708
|
}
|
|
4474
4709
|
}
|
|
4475
|
-
|
|
4710
|
+
async writeJsonFile(filePath, item) {
|
|
4476
4711
|
try {
|
|
4477
4712
|
const content = JSON.stringify(item, null, 2);
|
|
4478
|
-
|
|
4713
|
+
await writeFile(filePath, content, "utf-8");
|
|
4479
4714
|
} catch {
|
|
4480
4715
|
throw new StorageError(
|
|
4481
4716
|
`Failed to write file: ${filePath}`,
|
|
4482
|
-
"
|
|
4717
|
+
"STORAGE_IO_ERROR" /* STORAGE_IO_ERROR */
|
|
4483
4718
|
);
|
|
4484
4719
|
}
|
|
4485
4720
|
}
|
|
4486
4721
|
};
|
|
4487
4722
|
|
|
4488
4723
|
// src/chat/sessions.ts
|
|
4724
|
+
init_errors();
|
|
4489
4725
|
var BaseSessionStore = class {
|
|
4490
4726
|
constructor(adapter) {
|
|
4491
4727
|
this.adapter = adapter;
|
|
@@ -4524,7 +4760,7 @@ var BaseSessionStore = class {
|
|
|
4524
4760
|
async updateTitle(id, title) {
|
|
4525
4761
|
const session = await this.adapter.get(id);
|
|
4526
4762
|
if (!session) {
|
|
4527
|
-
throw new StorageError(`Session "${id}" not found`, "
|
|
4763
|
+
throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
|
|
4528
4764
|
}
|
|
4529
4765
|
session.title = title;
|
|
4530
4766
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4533,7 +4769,7 @@ var BaseSessionStore = class {
|
|
|
4533
4769
|
async updateConfig(id, config) {
|
|
4534
4770
|
const session = await this.adapter.get(id);
|
|
4535
4771
|
if (!session) {
|
|
4536
|
-
throw new StorageError(`Session "${id}" not found`, "
|
|
4772
|
+
throw new StorageError(`Session "${id}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
|
|
4537
4773
|
}
|
|
4538
4774
|
session.config = { ...session.config, ...config };
|
|
4539
4775
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4545,7 +4781,7 @@ var BaseSessionStore = class {
|
|
|
4545
4781
|
async appendMessage(sessionId, message) {
|
|
4546
4782
|
const session = await this.adapter.get(sessionId);
|
|
4547
4783
|
if (!session) {
|
|
4548
|
-
throw new StorageError(`Session "${sessionId}" not found`, "
|
|
4784
|
+
throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
|
|
4549
4785
|
}
|
|
4550
4786
|
session.messages.push(structuredClone(message));
|
|
4551
4787
|
session.metadata.messageCount = session.messages.length;
|
|
@@ -4556,7 +4792,7 @@ var BaseSessionStore = class {
|
|
|
4556
4792
|
if (messages.length === 0) return;
|
|
4557
4793
|
const session = await this.adapter.get(sessionId);
|
|
4558
4794
|
if (!session) {
|
|
4559
|
-
throw new StorageError(`Session "${sessionId}" not found`, "
|
|
4795
|
+
throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
|
|
4560
4796
|
}
|
|
4561
4797
|
for (const msg of messages) {
|
|
4562
4798
|
session.messages.push(structuredClone(msg));
|
|
@@ -4568,7 +4804,7 @@ var BaseSessionStore = class {
|
|
|
4568
4804
|
async loadMessages(sessionId, options) {
|
|
4569
4805
|
const session = await this.adapter.get(sessionId);
|
|
4570
4806
|
if (!session) {
|
|
4571
|
-
throw new StorageError(`Session "${sessionId}" not found`, "
|
|
4807
|
+
throw new StorageError(`Session "${sessionId}" not found`, "STORAGE_NOT_FOUND" /* STORAGE_NOT_FOUND */);
|
|
4572
4808
|
}
|
|
4573
4809
|
const total = session.messages.length;
|
|
4574
4810
|
const offset = options?.offset ?? 0;
|
|
@@ -4580,24 +4816,6 @@ var BaseSessionStore = class {
|
|
|
4580
4816
|
hasMore: offset + limit < total
|
|
4581
4817
|
};
|
|
4582
4818
|
}
|
|
4583
|
-
async archiveSession(id) {
|
|
4584
|
-
const session = await this.adapter.get(id);
|
|
4585
|
-
if (!session) {
|
|
4586
|
-
throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
|
|
4587
|
-
}
|
|
4588
|
-
session.status = "archived";
|
|
4589
|
-
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4590
|
-
await this.adapter.update(id, session);
|
|
4591
|
-
}
|
|
4592
|
-
async unarchiveSession(id) {
|
|
4593
|
-
const session = await this.adapter.get(id);
|
|
4594
|
-
if (!session) {
|
|
4595
|
-
throw new StorageError(`Session "${id}" not found`, "NOT_FOUND");
|
|
4596
|
-
}
|
|
4597
|
-
session.status = "active";
|
|
4598
|
-
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4599
|
-
await this.adapter.update(id, session);
|
|
4600
|
-
}
|
|
4601
4819
|
async searchSessions(options) {
|
|
4602
4820
|
const query = options.query.toLowerCase();
|
|
4603
4821
|
const limit = options.limit ?? 20;
|
|
@@ -4619,15 +4837,6 @@ var BaseSessionStore = class {
|
|
|
4619
4837
|
async clear() {
|
|
4620
4838
|
return this.adapter.clear();
|
|
4621
4839
|
}
|
|
4622
|
-
// ── Deprecated Aliases ──────────────────────────────────────
|
|
4623
|
-
/** @deprecated Use `appendMessage()` instead */
|
|
4624
|
-
async addMessage(sessionId, message) {
|
|
4625
|
-
return this.appendMessage(sessionId, message);
|
|
4626
|
-
}
|
|
4627
|
-
/** @deprecated Use `loadMessages()` instead */
|
|
4628
|
-
async getMessages(sessionId, options) {
|
|
4629
|
-
return this.loadMessages(sessionId, options);
|
|
4630
|
-
}
|
|
4631
4840
|
};
|
|
4632
4841
|
var InMemorySessionStore = class extends BaseSessionStore {
|
|
4633
4842
|
constructor() {
|
|
@@ -4640,28 +4849,59 @@ var FileSessionStore = class extends BaseSessionStore {
|
|
|
4640
4849
|
}
|
|
4641
4850
|
};
|
|
4642
4851
|
|
|
4852
|
+
// src/chat/backends/types.ts
|
|
4853
|
+
function isResumableBackend(adapter) {
|
|
4854
|
+
return "canResume" in adapter && typeof adapter.canResume === "function";
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4643
4857
|
// src/chat/backends/base.ts
|
|
4644
4858
|
var BaseBackendAdapter = class {
|
|
4645
4859
|
name;
|
|
4646
|
-
_agentService;
|
|
4647
|
-
|
|
4860
|
+
_agentService = null;
|
|
4861
|
+
_agentServiceFactory = null;
|
|
4648
4862
|
_disposed = false;
|
|
4649
4863
|
_agentConfig;
|
|
4650
4864
|
_ownsService;
|
|
4865
|
+
// Agent lifecycle: tracks current agent and the model it was created with.
|
|
4866
|
+
// For persistent sessions, reused across calls when model matches.
|
|
4867
|
+
// For non-persistent, recreated every call.
|
|
4868
|
+
_currentAgent = null;
|
|
4651
4869
|
constructor(name, options) {
|
|
4652
4870
|
this.name = name;
|
|
4653
4871
|
this._agentConfig = options.agentConfig;
|
|
4654
4872
|
if (options.agentService) {
|
|
4655
4873
|
this._agentService = options.agentService;
|
|
4656
4874
|
this._ownsService = false;
|
|
4875
|
+
} else if (options.agentServiceFactory) {
|
|
4876
|
+
this._agentServiceFactory = options.agentServiceFactory;
|
|
4877
|
+
this._ownsService = true;
|
|
4657
4878
|
} else {
|
|
4658
4879
|
this._agentService = this.createService();
|
|
4659
4880
|
this._ownsService = true;
|
|
4660
4881
|
}
|
|
4661
4882
|
}
|
|
4662
4883
|
get agentService() {
|
|
4884
|
+
if (!this._agentService) {
|
|
4885
|
+
if (this._agentServiceFactory) {
|
|
4886
|
+
this._agentService = this._agentServiceFactory();
|
|
4887
|
+
this._agentServiceFactory = null;
|
|
4888
|
+
} else {
|
|
4889
|
+
throw new ChatError("Agent service not available", {
|
|
4890
|
+
code: "BACKEND_NOT_INSTALLED" /* BACKEND_NOT_INSTALLED */
|
|
4891
|
+
});
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4663
4894
|
return this._agentService;
|
|
4664
4895
|
}
|
|
4896
|
+
get currentModel() {
|
|
4897
|
+
return this._agentConfig.model;
|
|
4898
|
+
}
|
|
4899
|
+
/**
|
|
4900
|
+
* @deprecated No-op. Tools are passed per-call via SendMessageOptions.tools.
|
|
4901
|
+
* Kept for backward compatibility with code that calls setTools() directly.
|
|
4902
|
+
*/
|
|
4903
|
+
setTools() {
|
|
4904
|
+
}
|
|
4665
4905
|
async sendMessage(session, message, options) {
|
|
4666
4906
|
this.assertNotDisposed();
|
|
4667
4907
|
const events = this.streamMessage(session, message, options);
|
|
@@ -4689,7 +4929,7 @@ var BaseBackendAdapter = class {
|
|
|
4689
4929
|
async *streamMessage(session, message, options) {
|
|
4690
4930
|
this.assertNotDisposed();
|
|
4691
4931
|
const agent = this.getOrCreateAgent(options);
|
|
4692
|
-
const messages = session.messages.
|
|
4932
|
+
const messages = session.messages.flatMap(toAgentMessages);
|
|
4693
4933
|
messages.push({ role: "user", content: message });
|
|
4694
4934
|
yield* this.streamAgentEvents(agent, messages, options);
|
|
4695
4935
|
}
|
|
@@ -4699,9 +4939,13 @@ var BaseBackendAdapter = class {
|
|
|
4699
4939
|
*/
|
|
4700
4940
|
async *streamAgentEvents(agent, messages, options) {
|
|
4701
4941
|
const messageId = createChatId();
|
|
4942
|
+
const model = options?.model ?? this._agentConfig.model ?? "";
|
|
4702
4943
|
const agentEvents = agent.streamWithContext(messages, {
|
|
4944
|
+
model,
|
|
4703
4945
|
signal: options?.signal,
|
|
4704
|
-
context: options?.context
|
|
4946
|
+
context: options?.context,
|
|
4947
|
+
tools: options?.tools,
|
|
4948
|
+
...options?.systemPrompt ? { systemMessage: options.systemPrompt } : {}
|
|
4705
4949
|
});
|
|
4706
4950
|
yield { type: "message:start", messageId, role: "assistant" };
|
|
4707
4951
|
let text = "";
|
|
@@ -4727,31 +4971,46 @@ var BaseBackendAdapter = class {
|
|
|
4727
4971
|
}
|
|
4728
4972
|
async listModels() {
|
|
4729
4973
|
this.assertNotDisposed();
|
|
4730
|
-
return this.
|
|
4974
|
+
return this.agentService.listModels();
|
|
4731
4975
|
}
|
|
4732
4976
|
async validate() {
|
|
4733
4977
|
this.assertNotDisposed();
|
|
4734
|
-
return this.
|
|
4978
|
+
return this.agentService.validate();
|
|
4735
4979
|
}
|
|
4736
4980
|
async dispose() {
|
|
4737
4981
|
if (this._disposed) return;
|
|
4738
4982
|
this._disposed = true;
|
|
4739
|
-
this.
|
|
4740
|
-
|
|
4741
|
-
|
|
4983
|
+
if (this._currentAgent) {
|
|
4984
|
+
this._currentAgent.instance.dispose();
|
|
4985
|
+
this._currentAgent = null;
|
|
4986
|
+
}
|
|
4987
|
+
if (this._ownsService && this._agentService && typeof this._agentService.dispose === "function") {
|
|
4742
4988
|
await this._agentService.dispose();
|
|
4743
4989
|
}
|
|
4744
4990
|
}
|
|
4745
|
-
/** Get or create an agent
|
|
4991
|
+
/** Get or create an agent. Model is passed per-call via RunOptions.
|
|
4992
|
+
* Tools are passed per-call via SendMessageOptions — not baked into config.
|
|
4993
|
+
* For persistent sessions, reuses agent when model matches. */
|
|
4746
4994
|
getOrCreateAgent(options) {
|
|
4747
|
-
const
|
|
4748
|
-
if (this._agentConfig.sessionMode === "persistent" && this.
|
|
4749
|
-
|
|
4995
|
+
const model = options?.model ?? this._agentConfig.model;
|
|
4996
|
+
if (this._agentConfig.sessionMode === "persistent" && this._currentAgent) {
|
|
4997
|
+
if (this._currentAgent.model === model) {
|
|
4998
|
+
return this._currentAgent.instance;
|
|
4999
|
+
}
|
|
5000
|
+
this._currentAgent.instance.dispose();
|
|
5001
|
+
this._currentAgent = null;
|
|
4750
5002
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
this.
|
|
5003
|
+
if (this._currentAgent) {
|
|
5004
|
+
this._currentAgent.instance.dispose();
|
|
5005
|
+
this._currentAgent = null;
|
|
4754
5006
|
}
|
|
5007
|
+
const config = {
|
|
5008
|
+
...this._agentConfig,
|
|
5009
|
+
...model !== void 0 && { model },
|
|
5010
|
+
...options?.tools?.length ? { tools: options.tools } : {}
|
|
5011
|
+
};
|
|
5012
|
+
const agent = this.agentService.createAgent(config);
|
|
5013
|
+
this._currentAgent = { instance: agent, model };
|
|
4755
5014
|
return agent;
|
|
4756
5015
|
}
|
|
4757
5016
|
assertNotDisposed() {
|
|
@@ -4763,21 +5022,15 @@ var BaseBackendAdapter = class {
|
|
|
4763
5022
|
}
|
|
4764
5023
|
};
|
|
4765
5024
|
|
|
4766
|
-
// src/chat/backends/
|
|
4767
|
-
var
|
|
5025
|
+
// src/chat/backends/resumable.ts
|
|
5026
|
+
var ResumableChatAdapter = class extends BaseBackendAdapter {
|
|
4768
5027
|
_backendSessionId = null;
|
|
4769
|
-
|
|
4770
|
-
constructor(options) {
|
|
5028
|
+
constructor(name, options) {
|
|
4771
5029
|
const agentConfig = {
|
|
4772
5030
|
...options.agentConfig,
|
|
4773
5031
|
sessionMode: "persistent"
|
|
4774
5032
|
};
|
|
4775
|
-
super(
|
|
4776
|
-
this._copilotOptions = options.copilotOptions;
|
|
4777
|
-
}
|
|
4778
|
-
createService() {
|
|
4779
|
-
const { createAgentService: createAgentService2 } = (init_src(), __toCommonJS(src_exports));
|
|
4780
|
-
return createAgentService2("copilot", this._copilotOptions);
|
|
5033
|
+
super(name, { ...options, agentConfig });
|
|
4781
5034
|
}
|
|
4782
5035
|
get backendSessionId() {
|
|
4783
5036
|
return this._backendSessionId;
|
|
@@ -4806,7 +5059,7 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
|
|
|
4806
5059
|
{ code: "SESSION_EXPIRED" /* SESSION_EXPIRED */ }
|
|
4807
5060
|
);
|
|
4808
5061
|
}
|
|
4809
|
-
const messages = session.messages.
|
|
5062
|
+
const messages = session.messages.flatMap(toAgentMessages);
|
|
4810
5063
|
yield* this.streamAgentEvents(agent, messages, options);
|
|
4811
5064
|
}
|
|
4812
5065
|
captureSessionId(agent) {
|
|
@@ -4816,81 +5069,325 @@ var CopilotChatAdapter = class extends BaseBackendAdapter {
|
|
|
4816
5069
|
}
|
|
4817
5070
|
};
|
|
4818
5071
|
|
|
5072
|
+
// src/chat/backends/copilot.ts
|
|
5073
|
+
var CopilotChatAdapter = class extends ResumableChatAdapter {
|
|
5074
|
+
_copilotOptions;
|
|
5075
|
+
constructor(options) {
|
|
5076
|
+
super("copilot", options);
|
|
5077
|
+
this._copilotOptions = options.copilotOptions;
|
|
5078
|
+
}
|
|
5079
|
+
createService() {
|
|
5080
|
+
const { createCopilotService: createCopilotService2 } = (init_copilot(), __toCommonJS(copilot_exports));
|
|
5081
|
+
return createCopilotService2(this._copilotOptions || {});
|
|
5082
|
+
}
|
|
5083
|
+
};
|
|
5084
|
+
|
|
4819
5085
|
// src/chat/backends/claude.ts
|
|
4820
|
-
var ClaudeChatAdapter = class extends
|
|
4821
|
-
_backendSessionId = null;
|
|
5086
|
+
var ClaudeChatAdapter = class extends ResumableChatAdapter {
|
|
4822
5087
|
_claudeOptions;
|
|
4823
5088
|
constructor(options) {
|
|
4824
|
-
|
|
4825
|
-
...options.agentConfig,
|
|
4826
|
-
sessionMode: "persistent"
|
|
4827
|
-
};
|
|
4828
|
-
super("claude", { ...options, agentConfig });
|
|
5089
|
+
super("claude", options);
|
|
4829
5090
|
this._claudeOptions = options.claudeOptions;
|
|
4830
5091
|
}
|
|
4831
5092
|
createService() {
|
|
4832
|
-
const {
|
|
4833
|
-
return
|
|
5093
|
+
const { createClaudeService: createClaudeService2 } = (init_claude(), __toCommonJS(claude_exports));
|
|
5094
|
+
return createClaudeService2(this._claudeOptions || {});
|
|
4834
5095
|
}
|
|
4835
|
-
|
|
4836
|
-
|
|
5096
|
+
};
|
|
5097
|
+
|
|
5098
|
+
// src/chat/backends/vercel-ai.ts
|
|
5099
|
+
var VercelAIChatAdapter = class extends BaseBackendAdapter {
|
|
5100
|
+
_vercelOptions;
|
|
5101
|
+
constructor(options) {
|
|
5102
|
+
super("vercel-ai", options);
|
|
5103
|
+
this._vercelOptions = options.vercelOptions;
|
|
4837
5104
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
5105
|
+
createService() {
|
|
5106
|
+
const { createVercelAIService: createVercelAIService2 } = (init_vercel_ai(), __toCommonJS(vercel_ai_exports));
|
|
5107
|
+
return createVercelAIService2(this._vercelOptions || {});
|
|
4840
5108
|
}
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
5109
|
+
captureSessionId(_agent) {
|
|
5110
|
+
}
|
|
5111
|
+
};
|
|
5112
|
+
|
|
5113
|
+
// src/backends/mock-llm.ts
|
|
5114
|
+
init_base_agent();
|
|
5115
|
+
init_errors2();
|
|
5116
|
+
function extractPrompt(messages) {
|
|
5117
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
5118
|
+
const msg = messages[i];
|
|
5119
|
+
if (msg.role === "user") {
|
|
5120
|
+
return typeof msg.content === "string" ? msg.content : msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
return "";
|
|
5124
|
+
}
|
|
5125
|
+
function resolveResponse(mode, messages, callIndex) {
|
|
5126
|
+
switch (mode.type) {
|
|
5127
|
+
case "echo":
|
|
5128
|
+
return extractPrompt(messages);
|
|
5129
|
+
case "static":
|
|
5130
|
+
return mode.response;
|
|
5131
|
+
case "scripted": {
|
|
5132
|
+
if (mode.loop) {
|
|
5133
|
+
return mode.responses[callIndex % mode.responses.length];
|
|
5134
|
+
}
|
|
5135
|
+
if (callIndex < mode.responses.length) {
|
|
5136
|
+
return mode.responses[callIndex];
|
|
5137
|
+
}
|
|
5138
|
+
return mode.responses[mode.responses.length - 1];
|
|
5139
|
+
}
|
|
5140
|
+
case "error":
|
|
5141
|
+
throw new AgentSDKError(mode.error, {
|
|
5142
|
+
code: mode.code ?? "backend_error",
|
|
5143
|
+
retryable: mode.recoverable ?? false
|
|
4846
5144
|
});
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
async function applyLatency(latency, signal) {
|
|
5148
|
+
if (!latency) return;
|
|
5149
|
+
const ms = latency.type === "fixed" ? latency.ms : latency.minMs + Math.random() * (latency.maxMs - latency.minMs);
|
|
5150
|
+
if (ms <= 0) return;
|
|
5151
|
+
await new Promise((resolve, reject) => {
|
|
5152
|
+
const timer = setTimeout(resolve, ms);
|
|
5153
|
+
const onAbort = () => {
|
|
5154
|
+
clearTimeout(timer);
|
|
5155
|
+
reject(new Error("aborted"));
|
|
5156
|
+
};
|
|
5157
|
+
if (signal.aborted) {
|
|
5158
|
+
clearTimeout(timer);
|
|
5159
|
+
reject(new Error("aborted"));
|
|
5160
|
+
return;
|
|
4847
5161
|
}
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
5162
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5163
|
+
});
|
|
5164
|
+
}
|
|
5165
|
+
function chunkText(text, streaming) {
|
|
5166
|
+
if (streaming?.chunkSize && streaming.chunkSize > 0) {
|
|
5167
|
+
const chunks = [];
|
|
5168
|
+
for (let i = 0; i < text.length; i += streaming.chunkSize) {
|
|
5169
|
+
chunks.push(text.slice(i, i + streaming.chunkSize));
|
|
4855
5170
|
}
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
5171
|
+
return chunks;
|
|
5172
|
+
}
|
|
5173
|
+
return text.split(/(\s+)/).filter(Boolean);
|
|
5174
|
+
}
|
|
5175
|
+
async function chunkDelay(streaming, signal) {
|
|
5176
|
+
const ms = streaming?.chunkDelayMs;
|
|
5177
|
+
if (!ms || ms <= 0) return;
|
|
5178
|
+
await new Promise((resolve, reject) => {
|
|
5179
|
+
const timer = setTimeout(resolve, ms);
|
|
5180
|
+
const onAbort = () => {
|
|
5181
|
+
clearTimeout(timer);
|
|
5182
|
+
reject(new Error("aborted"));
|
|
5183
|
+
};
|
|
5184
|
+
if (signal.aborted) {
|
|
5185
|
+
clearTimeout(timer);
|
|
5186
|
+
reject(new Error("aborted"));
|
|
5187
|
+
return;
|
|
4861
5188
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
5189
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5190
|
+
});
|
|
5191
|
+
}
|
|
5192
|
+
var MockLLMAgent = class extends BaseAgent {
|
|
5193
|
+
backendName = "mock-llm";
|
|
5194
|
+
mode;
|
|
5195
|
+
latency;
|
|
5196
|
+
streaming;
|
|
5197
|
+
finishReason;
|
|
5198
|
+
permissions;
|
|
5199
|
+
toolCallConfigs;
|
|
5200
|
+
configuredStructuredOutput;
|
|
5201
|
+
callIndex = 0;
|
|
5202
|
+
constructor(config, options) {
|
|
5203
|
+
super(config);
|
|
5204
|
+
this.mode = options.mode ?? { type: "echo" };
|
|
5205
|
+
this.latency = options.latency;
|
|
5206
|
+
this.streaming = options.streaming;
|
|
5207
|
+
this.finishReason = options.finishReason ?? "stop";
|
|
5208
|
+
this.permissions = options.permissions;
|
|
5209
|
+
this.toolCallConfigs = options.toolCalls ?? [];
|
|
5210
|
+
this.configuredStructuredOutput = options.structuredOutput;
|
|
5211
|
+
}
|
|
5212
|
+
async executeRun(messages, _options, signal) {
|
|
5213
|
+
this.checkAbort(signal);
|
|
5214
|
+
await applyLatency(this.latency, signal);
|
|
5215
|
+
this.checkAbort(signal);
|
|
5216
|
+
const idx = this.callIndex++;
|
|
5217
|
+
const output = resolveResponse(this.mode, messages, idx);
|
|
5218
|
+
const toolCalls = this.toolCallConfigs.map((tc) => ({
|
|
5219
|
+
toolName: tc.toolName,
|
|
5220
|
+
args: tc.args ?? {},
|
|
5221
|
+
result: tc.result ?? null,
|
|
5222
|
+
approved: true
|
|
5223
|
+
}));
|
|
5224
|
+
return {
|
|
5225
|
+
output,
|
|
5226
|
+
structuredOutput: void 0,
|
|
5227
|
+
toolCalls,
|
|
5228
|
+
messages: [
|
|
5229
|
+
...messages,
|
|
5230
|
+
{ role: "assistant", content: output }
|
|
5231
|
+
],
|
|
5232
|
+
usage: { promptTokens: 10, completionTokens: output.length }
|
|
5233
|
+
};
|
|
4864
5234
|
}
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
5235
|
+
async executeRunStructured(messages, _schema, _options, signal) {
|
|
5236
|
+
this.checkAbort(signal);
|
|
5237
|
+
await applyLatency(this.latency, signal);
|
|
5238
|
+
this.checkAbort(signal);
|
|
5239
|
+
const idx = this.callIndex++;
|
|
5240
|
+
const output = resolveResponse(this.mode, messages, idx);
|
|
5241
|
+
let parsed;
|
|
5242
|
+
if (this.configuredStructuredOutput !== void 0) {
|
|
5243
|
+
parsed = this.configuredStructuredOutput;
|
|
5244
|
+
} else {
|
|
5245
|
+
try {
|
|
5246
|
+
parsed = JSON.parse(output);
|
|
5247
|
+
} catch {
|
|
5248
|
+
parsed = output;
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
return {
|
|
5252
|
+
output,
|
|
5253
|
+
structuredOutput: parsed,
|
|
5254
|
+
toolCalls: [],
|
|
5255
|
+
messages: [
|
|
5256
|
+
...messages,
|
|
5257
|
+
{ role: "assistant", content: output }
|
|
5258
|
+
],
|
|
5259
|
+
usage: { promptTokens: 10, completionTokens: output.length }
|
|
5260
|
+
};
|
|
5261
|
+
}
|
|
5262
|
+
async *executeStream(messages, _options, signal) {
|
|
5263
|
+
this.checkAbort(signal);
|
|
5264
|
+
await applyLatency(this.latency, signal);
|
|
5265
|
+
this.checkAbort(signal);
|
|
5266
|
+
if (this.permissions) {
|
|
5267
|
+
yield* this.simulatePermissions(signal);
|
|
5268
|
+
}
|
|
5269
|
+
if (this.toolCallConfigs.length > 0) {
|
|
5270
|
+
yield* this.simulateToolCalls(signal);
|
|
5271
|
+
}
|
|
5272
|
+
const idx = this.callIndex++;
|
|
5273
|
+
const output = resolveResponse(this.mode, messages, idx);
|
|
5274
|
+
const chunks = chunkText(output, this.streaming);
|
|
5275
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
5276
|
+
this.checkAbort(signal);
|
|
5277
|
+
if (i > 0) {
|
|
5278
|
+
await chunkDelay(this.streaming, signal);
|
|
5279
|
+
}
|
|
5280
|
+
yield { type: "text_delta", text: chunks[i] };
|
|
5281
|
+
}
|
|
5282
|
+
yield {
|
|
5283
|
+
type: "usage_update",
|
|
5284
|
+
promptTokens: 10,
|
|
5285
|
+
completionTokens: output.length
|
|
5286
|
+
};
|
|
5287
|
+
yield {
|
|
5288
|
+
type: "done",
|
|
5289
|
+
finalOutput: output,
|
|
5290
|
+
finishReason: this.finishReason
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
async *simulateToolCalls(signal) {
|
|
5294
|
+
for (let i = 0; i < this.toolCallConfigs.length; i++) {
|
|
5295
|
+
this.checkAbort(signal);
|
|
5296
|
+
const tc = this.toolCallConfigs[i];
|
|
5297
|
+
const toolCallId = tc.toolCallId ?? `mock-tc-${i}`;
|
|
5298
|
+
yield {
|
|
5299
|
+
type: "tool_call_start",
|
|
5300
|
+
toolCallId,
|
|
5301
|
+
toolName: tc.toolName,
|
|
5302
|
+
args: tc.args ?? {}
|
|
5303
|
+
};
|
|
5304
|
+
yield {
|
|
5305
|
+
type: "tool_call_end",
|
|
5306
|
+
toolCallId,
|
|
5307
|
+
toolName: tc.toolName,
|
|
5308
|
+
result: tc.result ?? null
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
async *simulatePermissions(signal) {
|
|
5313
|
+
const perms = this.permissions;
|
|
5314
|
+
for (const toolName of perms.toolNames) {
|
|
5315
|
+
this.checkAbort(signal);
|
|
5316
|
+
const request = {
|
|
5317
|
+
toolName,
|
|
5318
|
+
toolArgs: {}
|
|
5319
|
+
};
|
|
5320
|
+
yield { type: "permission_request", request };
|
|
5321
|
+
if (perms.denyTools?.includes(toolName)) {
|
|
5322
|
+
yield {
|
|
5323
|
+
type: "permission_response",
|
|
5324
|
+
toolName,
|
|
5325
|
+
decision: { allowed: false, reason: "Denied by mock configuration" }
|
|
5326
|
+
};
|
|
5327
|
+
} else if (perms.autoApprove) {
|
|
5328
|
+
yield {
|
|
5329
|
+
type: "permission_response",
|
|
5330
|
+
toolName,
|
|
5331
|
+
decision: { allowed: true, scope: "once" }
|
|
5332
|
+
};
|
|
5333
|
+
} else {
|
|
5334
|
+
const supervisor = this.getConfig().supervisor;
|
|
5335
|
+
if (supervisor?.onPermission) {
|
|
5336
|
+
const decision = await supervisor.onPermission(request, signal);
|
|
5337
|
+
yield { type: "permission_response", toolName, decision };
|
|
5338
|
+
} else {
|
|
5339
|
+
yield {
|
|
5340
|
+
type: "permission_response",
|
|
5341
|
+
toolName,
|
|
5342
|
+
decision: { allowed: true, scope: "once" }
|
|
5343
|
+
};
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
4868
5346
|
}
|
|
4869
5347
|
}
|
|
4870
5348
|
};
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
constructor(options) {
|
|
4876
|
-
|
|
4877
|
-
this.
|
|
5349
|
+
var MockLLMService = class {
|
|
5350
|
+
name = "mock-llm";
|
|
5351
|
+
options;
|
|
5352
|
+
models;
|
|
5353
|
+
constructor(options = {}) {
|
|
5354
|
+
this.options = options;
|
|
5355
|
+
this.models = (options.models ?? [
|
|
5356
|
+
{ id: "mock-fast", name: "Mock Fast" },
|
|
5357
|
+
{ id: "mock-quality", name: "Mock Quality" }
|
|
5358
|
+
]).map((m) => ({
|
|
5359
|
+
id: m.id,
|
|
5360
|
+
name: m.name,
|
|
5361
|
+
description: m.description
|
|
5362
|
+
}));
|
|
4878
5363
|
}
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
return createAgentService2("vercel-ai", this._vercelOptions);
|
|
5364
|
+
createAgent(config) {
|
|
5365
|
+
return new MockLLMAgent(config, this.options);
|
|
4882
5366
|
}
|
|
4883
|
-
|
|
4884
|
-
return
|
|
5367
|
+
async listModels() {
|
|
5368
|
+
return this.models;
|
|
4885
5369
|
}
|
|
4886
|
-
|
|
4887
|
-
return
|
|
5370
|
+
async validate() {
|
|
5371
|
+
return { valid: true, errors: [] };
|
|
4888
5372
|
}
|
|
4889
|
-
async
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
5373
|
+
async dispose() {
|
|
5374
|
+
}
|
|
5375
|
+
};
|
|
5376
|
+
function createMockLLMService(options = {}) {
|
|
5377
|
+
return new MockLLMService(options);
|
|
5378
|
+
}
|
|
5379
|
+
|
|
5380
|
+
// src/chat/backends/mock-llm.ts
|
|
5381
|
+
var MockLLMChatAdapter = class extends BaseBackendAdapter {
|
|
5382
|
+
constructor(options) {
|
|
5383
|
+
const mockOpts = options.mockOptions;
|
|
5384
|
+
super("mock-llm", {
|
|
5385
|
+
...options,
|
|
5386
|
+
agentServiceFactory: () => createMockLLMService(mockOpts || {})
|
|
5387
|
+
});
|
|
5388
|
+
}
|
|
5389
|
+
createService() {
|
|
5390
|
+
return createMockLLMService({});
|
|
4894
5391
|
}
|
|
4895
5392
|
captureSessionId(_agent) {
|
|
4896
5393
|
}
|
|
@@ -4971,16 +5468,22 @@ var SSEChatTransport = class {
|
|
|
4971
5468
|
};
|
|
4972
5469
|
async function streamToTransport(events, transport) {
|
|
4973
5470
|
try {
|
|
4974
|
-
|
|
5471
|
+
const textChunks = [];
|
|
5472
|
+
let finishReason;
|
|
4975
5473
|
for await (const event of events) {
|
|
4976
5474
|
if (!transport.isOpen) break;
|
|
5475
|
+
if (event.type === "done") {
|
|
5476
|
+
finishReason = event.finishReason;
|
|
5477
|
+
continue;
|
|
5478
|
+
}
|
|
4977
5479
|
transport.send(event);
|
|
4978
5480
|
if (event.type === "message:delta") {
|
|
4979
|
-
|
|
5481
|
+
textChunks.push(event.text);
|
|
4980
5482
|
}
|
|
4981
5483
|
}
|
|
4982
5484
|
if (transport.isOpen) {
|
|
4983
|
-
|
|
5485
|
+
const finalOutput = textChunks.length > 0 ? textChunks.join("") : void 0;
|
|
5486
|
+
transport.send({ type: "done", finalOutput, finishReason });
|
|
4984
5487
|
}
|
|
4985
5488
|
transport.close();
|
|
4986
5489
|
} catch (err) {
|
|
@@ -5067,9 +5570,9 @@ var InProcessChatTransport = class {
|
|
|
5067
5570
|
send(event) {
|
|
5068
5571
|
if (!this._open) return;
|
|
5069
5572
|
if (this._resolve) {
|
|
5070
|
-
const
|
|
5573
|
+
const resolve = this._resolve;
|
|
5071
5574
|
this._resolve = null;
|
|
5072
|
-
|
|
5575
|
+
resolve({ value: event, done: false });
|
|
5073
5576
|
} else {
|
|
5074
5577
|
this._buffer.push(event);
|
|
5075
5578
|
}
|
|
@@ -5078,9 +5581,9 @@ var InProcessChatTransport = class {
|
|
|
5078
5581
|
if (!this._open) return;
|
|
5079
5582
|
this._open = false;
|
|
5080
5583
|
if (this._resolve) {
|
|
5081
|
-
const
|
|
5584
|
+
const resolve = this._resolve;
|
|
5082
5585
|
this._resolve = null;
|
|
5083
|
-
|
|
5586
|
+
resolve({ value: void 0, done: true });
|
|
5084
5587
|
}
|
|
5085
5588
|
}
|
|
5086
5589
|
error(err) {
|
|
@@ -5092,9 +5595,9 @@ var InProcessChatTransport = class {
|
|
|
5092
5595
|
recoverable: false
|
|
5093
5596
|
};
|
|
5094
5597
|
if (this._resolve) {
|
|
5095
|
-
const
|
|
5598
|
+
const resolve = this._resolve;
|
|
5096
5599
|
this._resolve = null;
|
|
5097
|
-
|
|
5600
|
+
resolve({ value: errorEvent, done: false });
|
|
5098
5601
|
} else {
|
|
5099
5602
|
this._error = err;
|
|
5100
5603
|
}
|
|
@@ -5119,8 +5622,8 @@ var InProcessChatTransport = class {
|
|
|
5119
5622
|
if (!this._open) {
|
|
5120
5623
|
return Promise.resolve({ value: void 0, done: true });
|
|
5121
5624
|
}
|
|
5122
|
-
return new Promise((
|
|
5123
|
-
this._resolve =
|
|
5625
|
+
return new Promise((resolve) => {
|
|
5626
|
+
this._resolve = resolve;
|
|
5124
5627
|
});
|
|
5125
5628
|
}
|
|
5126
5629
|
};
|
|
@@ -5291,6 +5794,6 @@ var ChatEventBus = class extends TypedEventEmitter {
|
|
|
5291
5794
|
}
|
|
5292
5795
|
};
|
|
5293
5796
|
|
|
5294
|
-
export { BaseBackendAdapter, ChatError,
|
|
5797
|
+
export { BaseBackendAdapter, ChatError, ChatEventBus, ClaudeChatAdapter, ContextWindowManager, CopilotChatAdapter, ErrorCode, ExponentialBackoffStrategy, FileSessionStore, InMemorySessionStore, InProcessChatTransport, ListenerSet, MessageAccumulator, MockLLMChatAdapter, SSEChatTransport, TypedEventEmitter, VercelAIChatAdapter, WsChatTransport, adaptAgentEvents, agentEventToChatEvent, classifyError, createChatId, createChatRuntime, createTextMessage, estimateTokens, fromAgentMessage, getMessageReasoning, getMessageText, getMessageToolCalls, isChatEvent, isChatMessage, isChatSession, isFilePart, isMessagePart, isObservableSession, isReasoningPart, isResumableBackend, isRetryable, isSourcePart, isTextPart, isToolCallPart, streamToTransport, toAgentMessage, toAgentMessages, toChatId, withRetry, withStreamWatchdog };
|
|
5295
5798
|
//# sourceMappingURL=index.js.map
|
|
5296
5799
|
//# sourceMappingURL=index.js.map
|