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