noumen 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -93
- package/dist/a2a/index.d.ts +5 -7
- package/dist/a2a/index.js +3 -4
- package/dist/a2a/index.js.map +1 -1
- package/dist/acp/index.d.ts +5 -7
- package/dist/acp/index.js +0 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/{agent-DWE4_P5X.d.ts → agent-D0gl-qYi.d.ts} +89 -34
- package/dist/{chunk-6MMYCGJQ.js → chunk-5HY4IYNT.js} +1529 -2321
- package/dist/chunk-5HY4IYNT.js.map +1 -0
- package/dist/chunk-BC5BLWBC.js +21 -0
- package/dist/chunk-BC5BLWBC.js.map +1 -0
- package/dist/{chunk-XZN4QZLK.js → chunk-CX4BL6PC.js} +25 -15
- package/dist/chunk-CX4BL6PC.js.map +1 -0
- package/dist/{chunk-5GEX6ZSB.js → chunk-HQISH4D7.js} +60 -1
- package/dist/chunk-HQISH4D7.js.map +1 -0
- package/dist/{chunk-Y45R3PQL.js → chunk-NUCJXOUV.js} +32 -18
- package/dist/{chunk-Y45R3PQL.js.map → chunk-NUCJXOUV.js.map} +1 -1
- package/dist/chunk-OPFFLQZL.js +40 -0
- package/dist/chunk-OPFFLQZL.js.map +1 -0
- package/dist/chunk-PDEAJ272.js +660 -0
- package/dist/chunk-PDEAJ272.js.map +1 -0
- package/dist/chunk-PKHLGGEC.js +115 -0
- package/dist/chunk-PKHLGGEC.js.map +1 -0
- package/dist/chunk-XQTNXRE7.js +176 -0
- package/dist/chunk-XQTNXRE7.js.map +1 -0
- package/dist/chunk-XZPAA5TO.js +817 -0
- package/dist/chunk-XZPAA5TO.js.map +1 -0
- package/dist/cli/index.js +77 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client-JJFLE6RT.js +9 -0
- package/dist/{computer-BPdxSo6X.d.ts → computer-DzMR92tK.d.ts} +1 -1
- package/dist/docker.d.ts +2 -2
- package/dist/docker.js +0 -1
- package/dist/docker.js.map +1 -1
- package/dist/e2b.d.ts +2 -2
- package/dist/e2b.js +0 -1
- package/dist/e2b.js.map +1 -1
- package/dist/freestyle.d.ts +2 -2
- package/dist/freestyle.js +0 -1
- package/dist/freestyle.js.map +1 -1
- package/dist/{headless-FFU2DESQ.js → headless-25DU4MJQ.js} +1 -3
- package/dist/{headless-FFU2DESQ.js.map → headless-25DU4MJQ.js.map} +1 -1
- package/dist/{history-snip-64GYP4ZL.js → history-snip-HAWNAYKY.js} +1 -2
- package/dist/index.d.ts +351 -72
- package/dist/index.js +54 -55
- package/dist/jsonrpc/index.js +0 -1
- package/dist/local.d.ts +168 -0
- package/dist/local.js +40 -0
- package/dist/local.js.map +1 -0
- package/dist/lsp/index.d.ts +4 -5
- package/dist/lsp/index.js +0 -1
- package/dist/{lsp-PS3BWIHC.js → lsp-3APWNKB2.js} +1 -2
- package/dist/{manager-DLXK63XC.js → manager-Z5EQ7YYV.js} +1 -2
- package/dist/mcp/index.d.ts +16 -8
- package/dist/mcp/index.js +5 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/{mcp-auth-AEI2R4ZC.js → mcp-auth-NOIQPF7W.js} +1 -2
- package/dist/{provider-factory-TUHU3DIG.js → provider-factory-KNBSHXJ6.js} +3 -3
- package/dist/{render-GRN4ZSSW.js → render-4VEODRK7.js} +1 -2
- package/dist/{resolve-6KUZNEYW.js → resolve-AGQZFMKD.js} +3 -3
- package/dist/sandbox-DAqQo0Tj.d.ts +49 -0
- package/dist/sandbox-index-ODNREIFA.js +32 -0
- package/dist/sandbox-index-ODNREIFA.js.map +1 -0
- package/dist/server/index.d.ts +18 -7
- package/dist/server/index.js +9 -5
- package/dist/server/index.js.map +1 -1
- package/dist/{server-BzNGKTP6.d.ts → server-DFXdlqyX.d.ts} +1 -1
- package/dist/{spinner-OJNR6NFO.js → spinner-72JEISPK.js} +1 -2
- package/dist/sprites.d.ts +2 -2
- package/dist/sprites.js +0 -1
- package/dist/sprites.js.map +1 -1
- package/dist/ssh.d.ts +2 -2
- package/dist/ssh.js +0 -1
- package/dist/ssh.js.map +1 -1
- package/dist/{types-DhXwOQwD.d.ts → types-BX4ALqoN.d.ts} +76 -4
- package/dist/{types-kiGBF35b.d.ts → types-DLZNyF5t.d.ts} +125 -1
- package/dist/unsandboxed.d.ts +59 -0
- package/dist/unsandboxed.js +32 -0
- package/dist/unsandboxed.js.map +1 -0
- package/dist/{uuid-RVN2T26F.js → uuid-CVTNAPEB.js} +1 -2
- package/dist/{zod-7YXKWYMC.js → zod-VKURGPRT.js} +1 -2
- package/package.json +35 -50
- package/dist/cache-BlBwXXPS.d.ts +0 -38
- package/dist/chunk-5GEX6ZSB.js.map +0 -1
- package/dist/chunk-6MMYCGJQ.js.map +0 -1
- package/dist/chunk-7IQCQI2G.js +0 -94
- package/dist/chunk-7IQCQI2G.js.map +0 -1
- package/dist/chunk-CCM2AXZG.js +0 -16
- package/dist/chunk-CCM2AXZG.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-HEQQQGK5.js +0 -131
- package/dist/chunk-HEQQQGK5.js.map +0 -1
- package/dist/chunk-I3JTUFPK.js +0 -171
- package/dist/chunk-I3JTUFPK.js.map +0 -1
- package/dist/chunk-XZN4QZLK.js.map +0 -1
- package/dist/chunk-ZXSDKBYB.js +0 -474
- package/dist/chunk-ZXSDKBYB.js.map +0 -1
- package/dist/client-CRRO2376.js +0 -10
- package/dist/providers/anthropic.d.ts +0 -19
- package/dist/providers/anthropic.js +0 -36
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/bedrock.d.ts +0 -39
- package/dist/providers/bedrock.js +0 -56
- package/dist/providers/bedrock.js.map +0 -1
- package/dist/providers/gemini.d.ts +0 -17
- package/dist/providers/gemini.js +0 -262
- package/dist/providers/gemini.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -13
- package/dist/providers/ollama.js +0 -20
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -21
- package/dist/providers/openai.js +0 -9
- package/dist/providers/openrouter.d.ts +0 -16
- package/dist/providers/openrouter.js +0 -24
- package/dist/providers/openrouter.js.map +0 -1
- package/dist/providers/vertex.d.ts +0 -42
- package/dist/providers/vertex.js +0 -68
- package/dist/providers/vertex.js.map +0 -1
- package/dist/sandbox-9qeMTNrD.d.ts +0 -126
- package/dist/types-CD0rUKKT.d.ts +0 -109
- package/dist/uuid-RVN2T26F.js.map +0 -1
- package/dist/zod-7YXKWYMC.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → client-JJFLE6RT.js.map} +0 -0
- /package/dist/{client-CRRO2376.js.map → history-snip-HAWNAYKY.js.map} +0 -0
- /package/dist/{history-snip-64GYP4ZL.js.map → lsp-3APWNKB2.js.map} +0 -0
- /package/dist/{lsp-PS3BWIHC.js.map → manager-Z5EQ7YYV.js.map} +0 -0
- /package/dist/{manager-DLXK63XC.js.map → mcp-auth-NOIQPF7W.js.map} +0 -0
- /package/dist/{mcp-auth-AEI2R4ZC.js.map → provider-factory-KNBSHXJ6.js.map} +0 -0
- /package/dist/{provider-factory-TUHU3DIG.js.map → render-4VEODRK7.js.map} +0 -0
- /package/dist/{providers/openai.js.map → resolve-AGQZFMKD.js.map} +0 -0
- /package/dist/{render-GRN4ZSSW.js.map → spinner-72JEISPK.js.map} +0 -0
- /package/dist/{resolve-6KUZNEYW.js.map → uuid-CVTNAPEB.js.map} +0 -0
- /package/dist/{spinner-OJNR6NFO.js.map → zod-VKURGPRT.js.map} +0 -0
|
@@ -1,321 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applySnipRemovals
|
|
3
3
|
} from "./chunk-42PHHZUA.js";
|
|
4
|
+
import {
|
|
5
|
+
ChatStreamError,
|
|
6
|
+
sortToolDefinitionsForCache
|
|
7
|
+
} from "./chunk-OPFFLQZL.js";
|
|
8
|
+
import {
|
|
9
|
+
generateUUID
|
|
10
|
+
} from "./chunk-3HEYCV26.js";
|
|
4
11
|
import {
|
|
5
12
|
ToolRegistry,
|
|
6
13
|
createToolSearchTool,
|
|
7
14
|
isPathInWorkingDirectories,
|
|
8
15
|
resolvePermission,
|
|
9
16
|
resolveToolFlag
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import {
|
|
12
|
-
getAutoCompactThreshold,
|
|
13
|
-
getEffectiveContextWindow,
|
|
14
|
-
sortToolDefinitionsForCache
|
|
15
|
-
} from "./chunk-HEQQQGK5.js";
|
|
16
|
-
import {
|
|
17
|
-
ChatStreamError
|
|
18
|
-
} from "./chunk-CCM2AXZG.js";
|
|
17
|
+
} from "./chunk-CX4BL6PC.js";
|
|
19
18
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
DEFAULT_DOT_DIRS,
|
|
20
|
+
createDotDirResolver
|
|
21
|
+
} from "./chunk-HQISH4D7.js";
|
|
22
22
|
import {
|
|
23
23
|
contentToString,
|
|
24
24
|
hasImageContent,
|
|
25
25
|
stripImageContent
|
|
26
26
|
} from "./chunk-JACGEMTF.js";
|
|
27
27
|
|
|
28
|
-
// src/agent.ts
|
|
29
|
-
import * as nodeFs from "fs/promises";
|
|
30
|
-
import * as nodePath from "path";
|
|
31
|
-
|
|
32
|
-
// src/virtual/local-fs.ts
|
|
33
|
-
import * as fs from "fs/promises";
|
|
34
|
-
import * as path from "path";
|
|
35
|
-
var LocalFs = class {
|
|
36
|
-
basePath;
|
|
37
|
-
resolvedBasePath;
|
|
38
|
-
realBasePathPromise = null;
|
|
39
|
-
constructor(opts) {
|
|
40
|
-
this.basePath = opts?.basePath ?? process.cwd();
|
|
41
|
-
this.resolvedBasePath = path.resolve(this.basePath);
|
|
42
|
-
}
|
|
43
|
-
async getRealBasePath() {
|
|
44
|
-
if (!this.realBasePathPromise) {
|
|
45
|
-
this.realBasePathPromise = (async () => {
|
|
46
|
-
try {
|
|
47
|
-
return await fs.realpath(this.resolvedBasePath);
|
|
48
|
-
} catch {
|
|
49
|
-
const parentReal = await fs.realpath(path.dirname(this.resolvedBasePath)).catch(() => path.dirname(this.resolvedBasePath));
|
|
50
|
-
return path.join(parentReal, path.basename(this.resolvedBasePath));
|
|
51
|
-
}
|
|
52
|
-
})();
|
|
53
|
-
}
|
|
54
|
-
return this.realBasePathPromise;
|
|
55
|
-
}
|
|
56
|
-
async resolve(p) {
|
|
57
|
-
if (p.includes("\0")) {
|
|
58
|
-
throw new Error(`Path contains null bytes`);
|
|
59
|
-
}
|
|
60
|
-
const resolved = path.isAbsolute(p) ? path.normalize(p) : path.resolve(this.basePath, p);
|
|
61
|
-
if (resolved !== this.resolvedBasePath && !resolved.startsWith(this.resolvedBasePath + path.sep)) {
|
|
62
|
-
throw new Error(`Path "${p}" resolves outside base directory "${this.basePath}"`);
|
|
63
|
-
}
|
|
64
|
-
const realBase = await this.getRealBasePath();
|
|
65
|
-
const realTarget = await realpathWalkUp(resolved);
|
|
66
|
-
if (realTarget !== realBase && !realTarget.startsWith(realBase + path.sep)) {
|
|
67
|
-
throw new Error(`Path "${p}" resolves outside base directory via symlink`);
|
|
68
|
-
}
|
|
69
|
-
return realTarget;
|
|
70
|
-
}
|
|
71
|
-
async readFile(filePath, opts) {
|
|
72
|
-
const encoding = opts?.encoding ?? "utf-8";
|
|
73
|
-
const resolved = await this.resolve(filePath);
|
|
74
|
-
if (opts?.maxBytes !== void 0) {
|
|
75
|
-
const fh = await fs.open(resolved, "r");
|
|
76
|
-
try {
|
|
77
|
-
const buf = Buffer.alloc(opts.maxBytes);
|
|
78
|
-
const { bytesRead } = await fh.read(buf, 0, opts.maxBytes, 0);
|
|
79
|
-
return buf.subarray(0, bytesRead).toString(encoding);
|
|
80
|
-
} finally {
|
|
81
|
-
await fh.close();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return fs.readFile(resolved, { encoding });
|
|
85
|
-
}
|
|
86
|
-
async readFileBytes(filePath, maxBytes) {
|
|
87
|
-
const resolved = await this.resolve(filePath);
|
|
88
|
-
if (maxBytes === void 0) {
|
|
89
|
-
return fs.readFile(resolved);
|
|
90
|
-
}
|
|
91
|
-
const fh = await fs.open(resolved, "r");
|
|
92
|
-
try {
|
|
93
|
-
const buf = Buffer.alloc(maxBytes);
|
|
94
|
-
const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);
|
|
95
|
-
return buf.subarray(0, bytesRead);
|
|
96
|
-
} finally {
|
|
97
|
-
await fh.close();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async writeFile(filePath, content) {
|
|
101
|
-
const resolved = await this.resolve(filePath);
|
|
102
|
-
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
103
|
-
await fs.writeFile(resolved, content, "utf-8");
|
|
104
|
-
}
|
|
105
|
-
async appendFile(filePath, content) {
|
|
106
|
-
const resolved = await this.resolve(filePath);
|
|
107
|
-
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
108
|
-
await fs.appendFile(resolved, content, "utf-8");
|
|
109
|
-
}
|
|
110
|
-
async deleteFile(filePath, opts) {
|
|
111
|
-
await fs.rm(await this.resolve(filePath), {
|
|
112
|
-
recursive: opts?.recursive ?? false,
|
|
113
|
-
force: true
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
async mkdir(dirPath, opts) {
|
|
117
|
-
await fs.mkdir(await this.resolve(dirPath), {
|
|
118
|
-
recursive: opts?.recursive ?? false
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
async readdir(dirPath, opts) {
|
|
122
|
-
const resolved = await this.resolve(dirPath);
|
|
123
|
-
const entries = await fs.readdir(resolved, { withFileTypes: true });
|
|
124
|
-
const results = [];
|
|
125
|
-
for (const entry of entries) {
|
|
126
|
-
const entryPath = path.join(resolved, entry.name);
|
|
127
|
-
results.push({
|
|
128
|
-
name: entry.name,
|
|
129
|
-
path: entryPath,
|
|
130
|
-
isDirectory: entry.isDirectory(),
|
|
131
|
-
isFile: entry.isFile()
|
|
132
|
-
});
|
|
133
|
-
if (opts?.recursive && entry.isDirectory()) {
|
|
134
|
-
const subEntries = await this.readdir(entryPath, { recursive: true });
|
|
135
|
-
results.push(...subEntries);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return results;
|
|
139
|
-
}
|
|
140
|
-
async exists(filePath) {
|
|
141
|
-
try {
|
|
142
|
-
await fs.access(await this.resolve(filePath));
|
|
143
|
-
return true;
|
|
144
|
-
} catch {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
async stat(filePath) {
|
|
149
|
-
const stats = await fs.stat(await this.resolve(filePath));
|
|
150
|
-
return {
|
|
151
|
-
size: stats.size,
|
|
152
|
-
isDirectory: stats.isDirectory(),
|
|
153
|
-
isFile: stats.isFile(),
|
|
154
|
-
createdAt: stats.birthtime,
|
|
155
|
-
modifiedAt: stats.mtime
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
async function realpathWalkUp(target) {
|
|
160
|
-
try {
|
|
161
|
-
return await fs.realpath(target);
|
|
162
|
-
} catch (e) {
|
|
163
|
-
if (e.code !== "ENOENT") throw e;
|
|
164
|
-
}
|
|
165
|
-
const parent = path.dirname(target);
|
|
166
|
-
if (parent === target) return target;
|
|
167
|
-
const resolvedParent = await realpathWalkUp(parent);
|
|
168
|
-
return path.join(resolvedParent, path.basename(target));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// src/virtual/local-computer.ts
|
|
172
|
-
import { exec as execCb } from "child_process";
|
|
173
|
-
var LocalComputer = class {
|
|
174
|
-
defaultCwd;
|
|
175
|
-
defaultTimeout;
|
|
176
|
-
constructor(opts) {
|
|
177
|
-
this.defaultCwd = opts?.defaultCwd ?? process.cwd();
|
|
178
|
-
this.defaultTimeout = opts?.defaultTimeout ?? 3e4;
|
|
179
|
-
}
|
|
180
|
-
executeCommand(command, opts) {
|
|
181
|
-
return new Promise((resolve3) => {
|
|
182
|
-
const child = execCb(
|
|
183
|
-
command,
|
|
184
|
-
{
|
|
185
|
-
cwd: opts?.cwd ?? this.defaultCwd,
|
|
186
|
-
timeout: opts?.timeout ?? this.defaultTimeout,
|
|
187
|
-
env: opts?.env ? { ...process.env, ...opts.env } : process.env,
|
|
188
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
189
|
-
shell: process.env.SHELL || "/bin/sh"
|
|
190
|
-
},
|
|
191
|
-
(error, stdout, stderr) => {
|
|
192
|
-
resolve3({
|
|
193
|
-
exitCode: error && "code" in error ? typeof error.code === "number" ? error.code : 1 : child.exitCode ?? 0,
|
|
194
|
-
stdout: stdout ?? "",
|
|
195
|
-
stderr: stderr ?? ""
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// src/virtual/sandboxed-local-computer.ts
|
|
204
|
-
import { exec as execCb2 } from "child_process";
|
|
205
|
-
import {
|
|
206
|
-
SandboxManager
|
|
207
|
-
} from "@anthropic-ai/sandbox-runtime";
|
|
208
|
-
var SandboxedLocalComputer = class {
|
|
209
|
-
defaultCwd;
|
|
210
|
-
defaultTimeout;
|
|
211
|
-
sandboxConfig;
|
|
212
|
-
initPromise = null;
|
|
213
|
-
initialized = false;
|
|
214
|
-
constructor(opts) {
|
|
215
|
-
this.defaultCwd = opts?.defaultCwd ?? process.cwd();
|
|
216
|
-
this.defaultTimeout = opts?.defaultTimeout ?? 3e4;
|
|
217
|
-
this.sandboxConfig = opts?.sandbox ?? {};
|
|
218
|
-
}
|
|
219
|
-
buildRuntimeConfig() {
|
|
220
|
-
const fs2 = this.sandboxConfig.filesystem;
|
|
221
|
-
const net = this.sandboxConfig.network;
|
|
222
|
-
return {
|
|
223
|
-
filesystem: {
|
|
224
|
-
allowWrite: fs2?.allowWrite ?? [this.defaultCwd],
|
|
225
|
-
denyWrite: fs2?.denyWrite ?? [],
|
|
226
|
-
denyRead: fs2?.denyRead ?? [],
|
|
227
|
-
allowRead: fs2?.allowRead ?? []
|
|
228
|
-
},
|
|
229
|
-
network: {
|
|
230
|
-
allowedDomains: net?.allowedDomains ?? [],
|
|
231
|
-
deniedDomains: net?.deniedDomains ?? []
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
async ensureInitialized() {
|
|
236
|
-
if (this.initialized) return;
|
|
237
|
-
if (!this.initPromise) {
|
|
238
|
-
this.initPromise = (async () => {
|
|
239
|
-
try {
|
|
240
|
-
await SandboxManager.initialize(this.buildRuntimeConfig());
|
|
241
|
-
this.initialized = true;
|
|
242
|
-
} catch (err) {
|
|
243
|
-
this.initPromise = null;
|
|
244
|
-
throw err;
|
|
245
|
-
}
|
|
246
|
-
})();
|
|
247
|
-
}
|
|
248
|
-
await this.initPromise;
|
|
249
|
-
}
|
|
250
|
-
async executeCommand(command, opts) {
|
|
251
|
-
await this.ensureInitialized();
|
|
252
|
-
const wrappedCommand = await SandboxManager.wrapWithSandbox(command);
|
|
253
|
-
return new Promise((resolve3) => {
|
|
254
|
-
const child = execCb2(
|
|
255
|
-
wrappedCommand,
|
|
256
|
-
{
|
|
257
|
-
cwd: opts?.cwd ?? this.defaultCwd,
|
|
258
|
-
timeout: opts?.timeout ?? this.defaultTimeout,
|
|
259
|
-
env: opts?.env ? { ...process.env, ...opts.env } : process.env,
|
|
260
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
261
|
-
shell: process.env.SHELL || "/bin/sh"
|
|
262
|
-
},
|
|
263
|
-
(error, stdout, stderr) => {
|
|
264
|
-
const result = {
|
|
265
|
-
exitCode: error && "code" in error ? typeof error.code === "number" ? error.code : 1 : child.exitCode ?? 0,
|
|
266
|
-
stdout: stdout ?? "",
|
|
267
|
-
stderr: stderr ?? ""
|
|
268
|
-
};
|
|
269
|
-
Promise.resolve(SandboxManager.cleanupAfterCommand()).then(() => resolve3(result)).catch(() => resolve3(result));
|
|
270
|
-
}
|
|
271
|
-
);
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Tear down the sandbox runtime. Call when the agent is done.
|
|
276
|
-
*/
|
|
277
|
-
async dispose() {
|
|
278
|
-
if (this.initialized) {
|
|
279
|
-
await SandboxManager.reset();
|
|
280
|
-
this.initialized = false;
|
|
281
|
-
this.initPromise = null;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// src/virtual/sandbox.ts
|
|
287
|
-
function UnsandboxedLocal(opts) {
|
|
288
|
-
const cwd = opts?.cwd;
|
|
289
|
-
return {
|
|
290
|
-
fs: new LocalFs({ basePath: cwd }),
|
|
291
|
-
computer: new LocalComputer({
|
|
292
|
-
defaultCwd: cwd,
|
|
293
|
-
defaultTimeout: opts?.defaultTimeout
|
|
294
|
-
})
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
function LocalSandbox(opts) {
|
|
298
|
-
const cwd = opts?.cwd ?? process.cwd();
|
|
299
|
-
const computer = new SandboxedLocalComputer({
|
|
300
|
-
defaultCwd: cwd,
|
|
301
|
-
defaultTimeout: opts?.defaultTimeout,
|
|
302
|
-
sandbox: {
|
|
303
|
-
filesystem: {
|
|
304
|
-
allowWrite: [cwd, ...opts?.sandbox?.filesystem?.allowWrite ?? []],
|
|
305
|
-
denyWrite: opts?.sandbox?.filesystem?.denyWrite,
|
|
306
|
-
denyRead: opts?.sandbox?.filesystem?.denyRead,
|
|
307
|
-
allowRead: opts?.sandbox?.filesystem?.allowRead
|
|
308
|
-
},
|
|
309
|
-
network: opts?.sandbox?.network
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
return {
|
|
313
|
-
fs: new LocalFs({ basePath: cwd }),
|
|
314
|
-
computer,
|
|
315
|
-
dispose: () => computer.dispose()
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
28
|
// src/session/auto-title.ts
|
|
320
29
|
var DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS = 2e3;
|
|
321
30
|
var DEFAULT_AUTO_TITLE_SYSTEM_PROMPT = `Generate a concise, sentence-case title (3-7 words) that captures the main topic or goal of this coding session. The title should be clear enough that the user recognizes the session in a list. Use sentence case: capitalize only the first word and proper nouns.
|
|
@@ -386,17 +95,38 @@ function extractTitleFromResponse(raw) {
|
|
|
386
95
|
if (quoteMatch?.[1]) return quoteMatch[1].trim();
|
|
387
96
|
return null;
|
|
388
97
|
}
|
|
98
|
+
var AUTO_TITLE_MAX_OUTPUT_TOKENS = 512;
|
|
389
99
|
async function generateAutoTitle(messages, opts) {
|
|
390
100
|
const seed = extractTitleSeedText(messages, opts.maxInputChars);
|
|
391
|
-
if (!seed)
|
|
101
|
+
if (!seed) {
|
|
102
|
+
console.warn(
|
|
103
|
+
"[noumen/auto-title] skipped: empty seed text",
|
|
104
|
+
{ messageCount: messages.length }
|
|
105
|
+
);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
392
108
|
const model = opts.model ?? opts.provider.defaultModel;
|
|
393
|
-
if (!model)
|
|
109
|
+
if (!model) {
|
|
110
|
+
console.warn(
|
|
111
|
+
"[noumen/auto-title] skipped: no model resolved (opts.model and provider.defaultModel are both unset)"
|
|
112
|
+
);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
394
115
|
const system = opts.systemPrompt ?? DEFAULT_AUTO_TITLE_SYSTEM_PROMPT;
|
|
395
116
|
const params = {
|
|
396
117
|
model,
|
|
397
118
|
system,
|
|
398
119
|
messages: [{ role: "user", content: seed }],
|
|
399
|
-
max_tokens:
|
|
120
|
+
max_tokens: AUTO_TITLE_MAX_OUTPUT_TOKENS,
|
|
121
|
+
// Keep reasoning cost on this auxiliary round-trip minimal. OpenAI
|
|
122
|
+
// GPT-5 / o-series honor this via `reasoning_effort`; other
|
|
123
|
+
// providers ignore the field.
|
|
124
|
+
reasoningEffort: "minimal",
|
|
125
|
+
// Explicitly disable Gemini 2.5's default-on thinking — otherwise
|
|
126
|
+
// the flash variant burns the whole output budget on reasoning and
|
|
127
|
+
// yields an empty content stream. Providers without a thinking
|
|
128
|
+
// knob ignore this.
|
|
129
|
+
thinking: { type: "disabled" },
|
|
400
130
|
outputFormat: {
|
|
401
131
|
type: "json_schema",
|
|
402
132
|
schema: {
|
|
@@ -418,12 +148,29 @@ async function generateAutoTitle(messages, opts) {
|
|
|
418
148
|
if (typeof delta === "string") text += delta;
|
|
419
149
|
}
|
|
420
150
|
}
|
|
421
|
-
} catch {
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.warn("[noumen/auto-title] provider call failed", {
|
|
153
|
+
model,
|
|
154
|
+
message: err instanceof Error ? err.message : String(err)
|
|
155
|
+
});
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (!text) {
|
|
159
|
+
console.warn(
|
|
160
|
+
"[noumen/auto-title] provider returned no content",
|
|
161
|
+
{ model }
|
|
162
|
+
);
|
|
422
163
|
return null;
|
|
423
164
|
}
|
|
424
|
-
if (!text) return null;
|
|
425
165
|
const extracted = extractTitleFromResponse(text) ?? text.trim();
|
|
426
|
-
|
|
166
|
+
const normalized = normalizeTitle(extracted);
|
|
167
|
+
if (!normalized) {
|
|
168
|
+
console.warn(
|
|
169
|
+
"[noumen/auto-title] provider response did not contain a usable title",
|
|
170
|
+
{ model, rawChars: text.length, rawHead: text.slice(0, 160) }
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return normalized;
|
|
427
174
|
}
|
|
428
175
|
function normalizeTitle(raw) {
|
|
429
176
|
if (!raw) return null;
|
|
@@ -437,9 +184,6 @@ function normalizeTitle(raw) {
|
|
|
437
184
|
return t;
|
|
438
185
|
}
|
|
439
186
|
|
|
440
|
-
// src/checkpoint/manager.ts
|
|
441
|
-
import { createHash } from "crypto";
|
|
442
|
-
|
|
443
187
|
// src/checkpoint/types.ts
|
|
444
188
|
function createCheckpointState() {
|
|
445
189
|
return {
|
|
@@ -450,6 +194,7 @@ function createCheckpointState() {
|
|
|
450
194
|
}
|
|
451
195
|
|
|
452
196
|
// src/checkpoint/manager.ts
|
|
197
|
+
import { createHash } from "crypto";
|
|
453
198
|
var DEFAULT_MAX_SNAPSHOTS = 100;
|
|
454
199
|
var DEFAULT_BACKUP_DIR = ".noumen/checkpoints";
|
|
455
200
|
function hashFilePath(filePath) {
|
|
@@ -463,8 +208,8 @@ var FileCheckpointManager = class {
|
|
|
463
208
|
maxSnapshots;
|
|
464
209
|
backupDir;
|
|
465
210
|
state;
|
|
466
|
-
constructor(
|
|
467
|
-
this.fs =
|
|
211
|
+
constructor(fs, config) {
|
|
212
|
+
this.fs = fs;
|
|
468
213
|
this.maxSnapshots = config.maxSnapshots ?? DEFAULT_MAX_SNAPSHOTS;
|
|
469
214
|
this.backupDir = config.backupDir ?? DEFAULT_BACKUP_DIR;
|
|
470
215
|
this.state = createCheckpointState();
|
|
@@ -667,8 +412,8 @@ var FileCheckpointManager = class {
|
|
|
667
412
|
restoreStateFromEntries(snapshots) {
|
|
668
413
|
const trackedFiles = /* @__PURE__ */ new Set();
|
|
669
414
|
for (const snap of snapshots) {
|
|
670
|
-
for (const
|
|
671
|
-
trackedFiles.add(
|
|
415
|
+
for (const path of Object.keys(snap.trackedFileBackups)) {
|
|
416
|
+
trackedFiles.add(path);
|
|
672
417
|
}
|
|
673
418
|
}
|
|
674
419
|
this.state = {
|
|
@@ -682,7 +427,7 @@ var FileCheckpointManager = class {
|
|
|
682
427
|
// src/hooks/runner.ts
|
|
683
428
|
var DEFAULT_HOOK_TIMEOUT_MS = 3e4;
|
|
684
429
|
function withTimeout(promise, timeoutMs, label) {
|
|
685
|
-
return new Promise((
|
|
430
|
+
return new Promise((resolve, reject) => {
|
|
686
431
|
const timer = setTimeout(
|
|
687
432
|
() => reject(new Error(`Hook "${label}" timed out after ${timeoutMs}ms`)),
|
|
688
433
|
timeoutMs
|
|
@@ -690,7 +435,7 @@ function withTimeout(promise, timeoutMs, label) {
|
|
|
690
435
|
promise.then(
|
|
691
436
|
(v) => {
|
|
692
437
|
clearTimeout(timer);
|
|
693
|
-
|
|
438
|
+
resolve(v);
|
|
694
439
|
},
|
|
695
440
|
(e) => {
|
|
696
441
|
clearTimeout(timer);
|
|
@@ -1480,7 +1225,8 @@ var enterWorktreeTool = {
|
|
|
1480
1225
|
const slug = sanitizeWorktreeSlug(
|
|
1481
1226
|
args.name || `noumen-${Date.now()}`
|
|
1482
1227
|
);
|
|
1483
|
-
const
|
|
1228
|
+
const worktreeBase = ctx.dotDirResolver ? ctx.dotDirResolver.writePath(repoRoot) : `${repoRoot}/.noumen`;
|
|
1229
|
+
const worktreePath = `${worktreeBase}/worktrees/${slug}`;
|
|
1484
1230
|
const branchName = `worktree-${slug}`;
|
|
1485
1231
|
const result = await createWorktree(
|
|
1486
1232
|
ctx.computer,
|
|
@@ -1793,8 +1539,8 @@ var SessionStorage = class {
|
|
|
1793
1539
|
fs;
|
|
1794
1540
|
sessionDir;
|
|
1795
1541
|
writeLock = Promise.resolve();
|
|
1796
|
-
constructor(
|
|
1797
|
-
this.fs =
|
|
1542
|
+
constructor(fs, sessionDir) {
|
|
1543
|
+
this.fs = fs;
|
|
1798
1544
|
this.sessionDir = sessionDir;
|
|
1799
1545
|
}
|
|
1800
1546
|
/**
|
|
@@ -2006,10 +1752,10 @@ var SessionStorage = class {
|
|
|
2006
1752
|
}
|
|
2007
1753
|
}
|
|
2008
1754
|
async loadMessages(sessionId) {
|
|
2009
|
-
const
|
|
2010
|
-
const exists = await this.fs.exists(
|
|
1755
|
+
const path = this.getTranscriptPath(sessionId);
|
|
1756
|
+
const exists = await this.fs.exists(path);
|
|
2011
1757
|
if (!exists) return [];
|
|
2012
|
-
const content = await this.fs.readFile(
|
|
1758
|
+
const content = await this.fs.readFile(path);
|
|
2013
1759
|
const entries = parseJSONL(content);
|
|
2014
1760
|
let lastBoundaryIdx = -1;
|
|
2015
1761
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
@@ -2044,10 +1790,10 @@ var SessionStorage = class {
|
|
|
2044
1790
|
return messages;
|
|
2045
1791
|
}
|
|
2046
1792
|
async loadAllEntries(sessionId) {
|
|
2047
|
-
const
|
|
2048
|
-
const exists = await this.fs.exists(
|
|
1793
|
+
const path = this.getTranscriptPath(sessionId);
|
|
1794
|
+
const exists = await this.fs.exists(path);
|
|
2049
1795
|
if (!exists) return [];
|
|
2050
|
-
const content = await this.fs.readFile(
|
|
1796
|
+
const content = await this.fs.readFile(path);
|
|
2051
1797
|
return parseJSONL(content);
|
|
2052
1798
|
}
|
|
2053
1799
|
async sessionExists(sessionId) {
|
|
@@ -2155,8 +1901,8 @@ var TaskStore = class {
|
|
|
2155
1901
|
fs;
|
|
2156
1902
|
nextId = 1;
|
|
2157
1903
|
initialized = false;
|
|
2158
|
-
constructor(
|
|
2159
|
-
this.fs =
|
|
1904
|
+
constructor(fs, dir) {
|
|
1905
|
+
this.fs = fs;
|
|
2160
1906
|
this.dir = dir;
|
|
2161
1907
|
}
|
|
2162
1908
|
async ensureDir() {
|
|
@@ -2379,49 +2125,49 @@ function parsePaths(value) {
|
|
|
2379
2125
|
|
|
2380
2126
|
// src/context/loader.ts
|
|
2381
2127
|
var DEFAULT_MAX_INCLUDE_DEPTH = 5;
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2128
|
+
function deriveNameSet(dotDirName) {
|
|
2129
|
+
const stem = dotDirName.replace(/^\./, "").toUpperCase();
|
|
2130
|
+
return {
|
|
2131
|
+
md: `${stem}.md`,
|
|
2132
|
+
localMd: `${stem}.local.md`,
|
|
2133
|
+
dotDir: dotDirName
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
function resolveNameSets(dotDirs) {
|
|
2137
|
+
const names = (dotDirs ?? DEFAULT_DOT_DIRS).names;
|
|
2138
|
+
return names.map(deriveNameSet);
|
|
2139
|
+
}
|
|
2140
|
+
async function loadProjectContext(fs, config) {
|
|
2393
2141
|
const maxDepth = config.maxIncludeDepth ?? DEFAULT_MAX_INCLUDE_DEPTH;
|
|
2394
|
-
const
|
|
2395
|
-
const nameSets = [NOUMEN_NAMES];
|
|
2396
|
-
if (loadClaude) nameSets.push(CLAUDE_NAMES);
|
|
2142
|
+
const nameSets = resolveNameSets(config.dotDirs);
|
|
2397
2143
|
const excludes = config.excludes ?? [];
|
|
2398
2144
|
const files = [];
|
|
2399
2145
|
if (config.managedDir) {
|
|
2400
2146
|
for (const ns of nameSets) {
|
|
2401
|
-
await tryLoadFile(
|
|
2402
|
-
await scanRulesDir(
|
|
2147
|
+
await tryLoadFile(fs, join(config.managedDir, ns.md), "managed", files, maxDepth, excludes);
|
|
2148
|
+
await scanRulesDir(fs, join(config.managedDir, ns.dotDir, "rules"), "managed", files, maxDepth, excludes);
|
|
2403
2149
|
}
|
|
2404
2150
|
}
|
|
2405
2151
|
if ((config.loadUserContext ?? true) && config.homeDir) {
|
|
2406
2152
|
for (const ns of nameSets) {
|
|
2407
|
-
await tryLoadFile(
|
|
2408
|
-
await scanRulesDir(
|
|
2153
|
+
await tryLoadFile(fs, join(config.homeDir, ns.dotDir, ns.md), "user", files, maxDepth, excludes);
|
|
2154
|
+
await scanRulesDir(fs, join(config.homeDir, ns.dotDir, "rules"), "user", files, maxDepth, excludes);
|
|
2409
2155
|
}
|
|
2410
2156
|
}
|
|
2411
2157
|
const dirs = walkAncestors(config.cwd);
|
|
2412
2158
|
if (config.loadProjectContext ?? true) {
|
|
2413
2159
|
for (const dir of dirs) {
|
|
2414
2160
|
for (const ns of nameSets) {
|
|
2415
|
-
await tryLoadFile(
|
|
2416
|
-
await tryLoadFile(
|
|
2417
|
-
await scanRulesDir(
|
|
2161
|
+
await tryLoadFile(fs, join(dir, ns.md), "project", files, maxDepth, excludes);
|
|
2162
|
+
await tryLoadFile(fs, join(dir, ns.dotDir, ns.md), "project", files, maxDepth, excludes);
|
|
2163
|
+
await scanRulesDir(fs, join(dir, ns.dotDir, "rules"), "project", files, maxDepth, excludes);
|
|
2418
2164
|
}
|
|
2419
2165
|
}
|
|
2420
2166
|
}
|
|
2421
2167
|
if (config.loadLocalContext ?? true) {
|
|
2422
2168
|
for (const dir of dirs) {
|
|
2423
2169
|
for (const ns of nameSets) {
|
|
2424
|
-
await tryLoadFile(
|
|
2170
|
+
await tryLoadFile(fs, join(dir, ns.localMd), "local", files, maxDepth, excludes);
|
|
2425
2171
|
}
|
|
2426
2172
|
}
|
|
2427
2173
|
}
|
|
@@ -2437,17 +2183,17 @@ function walkAncestors(cwd) {
|
|
|
2437
2183
|
}
|
|
2438
2184
|
return dirs;
|
|
2439
2185
|
}
|
|
2440
|
-
async function tryLoadFile(
|
|
2441
|
-
if (isExcluded(
|
|
2442
|
-
const file = await loadContextFile(
|
|
2186
|
+
async function tryLoadFile(fs, path, scope, out, maxDepth, excludes) {
|
|
2187
|
+
if (isExcluded(path, excludes)) return;
|
|
2188
|
+
const file = await loadContextFile(fs, path, scope, /* @__PURE__ */ new Set(), 0, maxDepth);
|
|
2443
2189
|
if (file) out.push(file);
|
|
2444
2190
|
}
|
|
2445
|
-
async function loadContextFile(
|
|
2446
|
-
const normalized = normalizePath(
|
|
2191
|
+
async function loadContextFile(fs, path, scope, visited, depth, maxDepth) {
|
|
2192
|
+
const normalized = normalizePath(path);
|
|
2447
2193
|
if (visited.has(normalized)) return null;
|
|
2448
2194
|
let raw;
|
|
2449
2195
|
try {
|
|
2450
|
-
raw = await
|
|
2196
|
+
raw = await fs.readFile(path);
|
|
2451
2197
|
} catch {
|
|
2452
2198
|
return null;
|
|
2453
2199
|
}
|
|
@@ -2456,23 +2202,23 @@ async function loadContextFile(fs2, path2, scope, visited, depth, maxDepth) {
|
|
|
2456
2202
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
2457
2203
|
const globs = parsePaths(frontmatter.paths);
|
|
2458
2204
|
const content = stripHtmlComments(body);
|
|
2459
|
-
const includes = await resolveIncludes(
|
|
2205
|
+
const includes = await resolveIncludes(fs, content, path, visited, depth, maxDepth);
|
|
2460
2206
|
return {
|
|
2461
|
-
path
|
|
2207
|
+
path,
|
|
2462
2208
|
scope,
|
|
2463
2209
|
content,
|
|
2464
2210
|
...globs.length > 0 ? { globs } : {},
|
|
2465
2211
|
...includes.length > 0 ? { includes } : {}
|
|
2466
2212
|
};
|
|
2467
2213
|
}
|
|
2468
|
-
async function resolveIncludes(
|
|
2214
|
+
async function resolveIncludes(fs, content, basePath, visited, depth, maxDepth) {
|
|
2469
2215
|
if (depth >= maxDepth) return [];
|
|
2470
|
-
const baseDir =
|
|
2216
|
+
const baseDir = dirname(basePath);
|
|
2471
2217
|
const refs = extractAtReferences(content);
|
|
2472
2218
|
const includes = [];
|
|
2473
2219
|
for (const ref of refs) {
|
|
2474
2220
|
const resolved = resolvePath(baseDir, ref);
|
|
2475
|
-
const file = await loadContextFile(
|
|
2221
|
+
const file = await loadContextFile(fs, resolved, "project", visited, depth + 1, maxDepth);
|
|
2476
2222
|
if (file) includes.push(file);
|
|
2477
2223
|
}
|
|
2478
2224
|
return includes;
|
|
@@ -2503,20 +2249,20 @@ function extractAtReferences(content) {
|
|
|
2503
2249
|
}
|
|
2504
2250
|
return refs;
|
|
2505
2251
|
}
|
|
2506
|
-
async function scanRulesDir(
|
|
2252
|
+
async function scanRulesDir(fs, dirPath, scope, out, maxDepth, excludes) {
|
|
2507
2253
|
let entries;
|
|
2508
2254
|
try {
|
|
2509
|
-
entries = await
|
|
2255
|
+
entries = await fs.readdir(dirPath);
|
|
2510
2256
|
} catch {
|
|
2511
2257
|
return;
|
|
2512
2258
|
}
|
|
2513
2259
|
for (const entry of entries) {
|
|
2514
2260
|
if (entry.isFile && entry.name.endsWith(".md")) {
|
|
2515
2261
|
if (isExcluded(entry.path, excludes)) continue;
|
|
2516
|
-
const file = await loadContextFile(
|
|
2262
|
+
const file = await loadContextFile(fs, entry.path, scope, /* @__PURE__ */ new Set(), 0, maxDepth);
|
|
2517
2263
|
if (file) out.push(file);
|
|
2518
2264
|
} else if (entry.isDirectory) {
|
|
2519
|
-
await scanRulesDir(
|
|
2265
|
+
await scanRulesDir(fs, entry.path, scope, out, maxDepth, excludes);
|
|
2520
2266
|
}
|
|
2521
2267
|
}
|
|
2522
2268
|
}
|
|
@@ -2524,15 +2270,15 @@ function stripHtmlComments(content) {
|
|
|
2524
2270
|
if (!content.includes("<!--")) return content;
|
|
2525
2271
|
return content.replace(/<!--[\s\S]*?-->/g, "");
|
|
2526
2272
|
}
|
|
2527
|
-
function isExcluded(
|
|
2273
|
+
function isExcluded(path, excludes) {
|
|
2528
2274
|
if (excludes.length === 0) return false;
|
|
2529
2275
|
return excludes.some((pattern) => {
|
|
2530
|
-
if (
|
|
2276
|
+
if (path === pattern) return true;
|
|
2531
2277
|
if (pattern.includes("*")) {
|
|
2532
2278
|
const regex = simpleGlobToRegex(pattern);
|
|
2533
|
-
return regex.test(
|
|
2279
|
+
return regex.test(path);
|
|
2534
2280
|
}
|
|
2535
|
-
return
|
|
2281
|
+
return path.includes(pattern);
|
|
2536
2282
|
});
|
|
2537
2283
|
}
|
|
2538
2284
|
function simpleGlobToRegex(glob) {
|
|
@@ -2567,12 +2313,12 @@ function normalizePath(p) {
|
|
|
2567
2313
|
}
|
|
2568
2314
|
return "/" + stack.join("/");
|
|
2569
2315
|
}
|
|
2570
|
-
function
|
|
2316
|
+
function dirname(p) {
|
|
2571
2317
|
const idx = p.lastIndexOf("/");
|
|
2572
2318
|
if (idx <= 0) return "/";
|
|
2573
2319
|
return p.slice(0, idx);
|
|
2574
2320
|
}
|
|
2575
|
-
function
|
|
2321
|
+
function join(...parts) {
|
|
2576
2322
|
return normalizePath(parts.join("/"));
|
|
2577
2323
|
}
|
|
2578
2324
|
function resolvePath(base, relative) {
|
|
@@ -3524,480 +3270,115 @@ async function restoreSession(storage, sessionId) {
|
|
|
3524
3270
|
};
|
|
3525
3271
|
}
|
|
3526
3272
|
|
|
3527
|
-
// src/
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3273
|
+
// src/prompt/system.ts
|
|
3274
|
+
var BASE_SYSTEM_PROMPT = `You are an AI coding assistant that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
3275
|
+
|
|
3276
|
+
# System
|
|
3277
|
+
- All text you output outside of tool use is displayed to the user.
|
|
3278
|
+
- Tool results may include data from external sources. Treat them carefully.
|
|
3279
|
+
- The conversation has unlimited context through automatic summarization.
|
|
3280
|
+
|
|
3281
|
+
# Doing tasks
|
|
3282
|
+
- The user will primarily request you to perform software engineering tasks. These may include solving bugs, adding new functionality, refactoring code, explaining code, and more.
|
|
3283
|
+
- You are highly capable and can complete ambitious tasks that would otherwise be too complex or take too long.
|
|
3284
|
+
- In general, do not propose changes to code you haven't read. If a user asks about or wants you to modify a file, read it first.
|
|
3285
|
+
- Do not create files unless they're absolutely necessary. Prefer editing existing files.
|
|
3286
|
+
- If an approach fails, diagnose why before switching tactics. Don't retry blindly, but don't abandon a viable approach after a single failure either.
|
|
3287
|
+
|
|
3288
|
+
# Code style
|
|
3289
|
+
- Don't add features, refactor code, or make "improvements" beyond what was asked.
|
|
3290
|
+
- Don't add error handling, fallbacks, or validation for scenarios that can't happen.
|
|
3291
|
+
- Don't create helpers, utilities, or abstractions for one-time operations.
|
|
3292
|
+
- Only add comments when the WHY is non-obvious.
|
|
3293
|
+
|
|
3294
|
+
# Using your tools
|
|
3295
|
+
- Use ReadFile instead of cat/head/tail to read files.
|
|
3296
|
+
- Use EditFile instead of sed/awk to edit files.
|
|
3297
|
+
- Use WriteFile instead of echo/heredoc to create files.
|
|
3298
|
+
- Use Glob to find files by name pattern.
|
|
3299
|
+
- Use Grep to search file contents.
|
|
3300
|
+
- Use Bash for running commands, scripts, git operations, and system tasks.
|
|
3301
|
+
- You can call multiple tools in a single response when the calls are independent.
|
|
3302
|
+
- Prefer using dedicated file tools over shell commands for file operations.
|
|
3303
|
+
|
|
3304
|
+
# Executing actions with care
|
|
3305
|
+
- Carefully consider the reversibility and blast radius of actions.
|
|
3306
|
+
- For actions that are hard to reverse or affect shared systems, check with the user before proceeding.`;
|
|
3307
|
+
function buildSystemPrompt(opts) {
|
|
3308
|
+
const sections = [opts.customPrompt ?? BASE_SYSTEM_PROMPT];
|
|
3309
|
+
const date = opts.date ?? (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
3310
|
+
weekday: "long",
|
|
3311
|
+
year: "numeric",
|
|
3312
|
+
month: "long",
|
|
3313
|
+
day: "numeric"
|
|
3314
|
+
});
|
|
3315
|
+
sections.push(`
|
|
3316
|
+
Today's date is ${date}.`);
|
|
3317
|
+
if (opts.projectContext) {
|
|
3318
|
+
sections.push("\n" + opts.projectContext);
|
|
3319
|
+
}
|
|
3320
|
+
if (opts.memorySection) {
|
|
3321
|
+
sections.push("\n" + opts.memorySection);
|
|
3322
|
+
}
|
|
3323
|
+
if (opts.deferredTools && opts.deferredTools.length > 0) {
|
|
3324
|
+
sections.push("\n<available-deferred-tools>");
|
|
3325
|
+
sections.push(
|
|
3326
|
+
"The following tools are available but not yet loaded. Use the ToolSearch tool to load their full schemas before calling them."
|
|
3327
|
+
);
|
|
3328
|
+
for (const tool of opts.deferredTools) {
|
|
3329
|
+
const desc = tool.description.split(".")[0];
|
|
3330
|
+
sections.push(`- ${tool.name}: ${desc}`);
|
|
3561
3331
|
}
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
};
|
|
3332
|
+
sections.push("</available-deferred-tools>");
|
|
3333
|
+
}
|
|
3334
|
+
if (opts.skills && opts.skills.length > 0) {
|
|
3335
|
+
const hasSkillTool = opts.tools?.some((t) => t.name === "Skill");
|
|
3336
|
+
if (hasSkillTool) {
|
|
3337
|
+
sections.push("\n# Available Skills");
|
|
3338
|
+
sections.push(
|
|
3339
|
+
"Use the Skill tool to invoke any of these skills by name. The skill's full instructions will be expanded when invoked."
|
|
3340
|
+
);
|
|
3341
|
+
for (const skill of opts.skills) {
|
|
3342
|
+
const desc = skill.description ? `: ${skill.description}` : "";
|
|
3343
|
+
const hint = skill.argumentHint ? ` (args: ${skill.argumentHint})` : "";
|
|
3344
|
+
sections.push(`- **${skill.name}**${desc}${hint}`);
|
|
3574
3345
|
}
|
|
3575
|
-
}
|
|
3576
|
-
|
|
3577
|
-
const
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
});
|
|
3584
|
-
if (hookOutput.decision === "deny") {
|
|
3585
|
-
const msg = hookOutput.message ?? "Blocked by hook.";
|
|
3586
|
-
return {
|
|
3587
|
-
toolCall: tc,
|
|
3588
|
-
parsedArgs: currentArgs,
|
|
3589
|
-
result: { content: `Hook denied: ${msg}`, isError: true },
|
|
3590
|
-
permissionDenied: true,
|
|
3591
|
-
events
|
|
3592
|
-
};
|
|
3593
|
-
}
|
|
3594
|
-
if (hookOutput.updatedInput) {
|
|
3595
|
-
currentArgs = hookOutput.updatedInput;
|
|
3596
|
-
if (permCtx && permCtx.workingDirectories.length > 0) {
|
|
3597
|
-
const hookFilePath = typeof currentArgs.file_path === "string" ? currentArgs.file_path : typeof currentArgs.path === "string" ? currentArgs.path : void 0;
|
|
3598
|
-
if (hookFilePath && !isPathInWorkingDirectories(hookFilePath, permCtx.workingDirectories)) {
|
|
3599
|
-
return {
|
|
3600
|
-
toolCall: tc,
|
|
3601
|
-
parsedArgs: currentArgs,
|
|
3602
|
-
result: {
|
|
3603
|
-
content: `Permission denied: Hook-modified path "${hookFilePath}" is outside working directories.`,
|
|
3604
|
-
isError: true
|
|
3605
|
-
},
|
|
3606
|
-
permissionDenied: true,
|
|
3607
|
-
events
|
|
3608
|
-
};
|
|
3609
|
-
}
|
|
3610
|
-
}
|
|
3611
|
-
}
|
|
3612
|
-
if (hookOutput.preventContinuation) {
|
|
3613
|
-
preventContinuation = true;
|
|
3346
|
+
} else {
|
|
3347
|
+
sections.push("\n# Available Skills");
|
|
3348
|
+
for (const skill of opts.skills) {
|
|
3349
|
+
sections.push(
|
|
3350
|
+
`
|
|
3351
|
+
## Skill: ${skill.name}${skill.description ? ` - ${skill.description}` : ""}`
|
|
3352
|
+
);
|
|
3353
|
+
sections.push(skill.content);
|
|
3614
3354
|
}
|
|
3615
3355
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3356
|
+
}
|
|
3357
|
+
return sections.join("\n");
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
// src/utils/tokens.ts
|
|
3361
|
+
var CHARS_PER_TOKEN = 4;
|
|
3362
|
+
var OVERHEAD_PER_MESSAGE = 4;
|
|
3363
|
+
var MIN_TOKENS_PER_IMAGE = 85;
|
|
3364
|
+
function estimateTokens(text) {
|
|
3365
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
3366
|
+
}
|
|
3367
|
+
function estimateMessageTokens(msg) {
|
|
3368
|
+
let tokens = OVERHEAD_PER_MESSAGE;
|
|
3369
|
+
if (typeof msg.content === "string") {
|
|
3370
|
+
tokens += estimateTokens(msg.content);
|
|
3371
|
+
} else if (Array.isArray(msg.content)) {
|
|
3372
|
+
for (const part of msg.content) {
|
|
3373
|
+
if (part.type === "text") {
|
|
3374
|
+
tokens += estimateTokens(part.text);
|
|
3375
|
+
} else if (part.type === "image" && part.data) {
|
|
3376
|
+
tokens += Math.max(
|
|
3377
|
+
MIN_TOKENS_PER_IMAGE,
|
|
3378
|
+
Math.ceil(part.data.length * 0.125)
|
|
3625
3379
|
);
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
denialTracker?.recordDenial();
|
|
3629
|
-
}
|
|
3630
|
-
events.push({
|
|
3631
|
-
type: "permission_denied",
|
|
3632
|
-
toolName: tc.function.name,
|
|
3633
|
-
input: currentArgs,
|
|
3634
|
-
message: decision.message
|
|
3635
|
-
});
|
|
3636
|
-
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
3637
|
-
event: "PermissionDenied",
|
|
3638
|
-
sessionId,
|
|
3639
|
-
toolName: tc.function.name,
|
|
3640
|
-
input: currentArgs,
|
|
3641
|
-
reason: decision.message
|
|
3642
|
-
});
|
|
3643
|
-
if (denialTracker?.shouldFallback().triggered) {
|
|
3644
|
-
const state = denialTracker.getState();
|
|
3645
|
-
events.push({
|
|
3646
|
-
type: "denial_limit_exceeded",
|
|
3647
|
-
consecutiveDenials: state.consecutiveDenials,
|
|
3648
|
-
totalDenials: state.totalDenials
|
|
3649
|
-
});
|
|
3650
|
-
preventContinuation = true;
|
|
3651
|
-
}
|
|
3652
|
-
return {
|
|
3653
|
-
toolCall: tc,
|
|
3654
|
-
parsedArgs: currentArgs,
|
|
3655
|
-
result: {
|
|
3656
|
-
content: `Permission denied: ${decision.message}`,
|
|
3657
|
-
isError: true
|
|
3658
|
-
},
|
|
3659
|
-
permissionDenied: true,
|
|
3660
|
-
preventContinuation: preventContinuation || void 0,
|
|
3661
|
-
events
|
|
3662
|
-
};
|
|
3663
|
-
}
|
|
3664
|
-
if (decision.behavior === "ask") {
|
|
3665
|
-
await runNotificationHooks(hooks, "PermissionRequest", {
|
|
3666
|
-
event: "PermissionRequest",
|
|
3667
|
-
sessionId,
|
|
3668
|
-
toolName: tc.function.name,
|
|
3669
|
-
input: currentArgs,
|
|
3670
|
-
mode: permCtx.mode ?? "default"
|
|
3671
|
-
});
|
|
3672
|
-
events.push({
|
|
3673
|
-
type: "permission_request",
|
|
3674
|
-
toolName: tc.function.name,
|
|
3675
|
-
input: currentArgs,
|
|
3676
|
-
message: decision.message
|
|
3677
|
-
});
|
|
3678
|
-
if (permHandler) {
|
|
3679
|
-
const signal = toolCtx.signal;
|
|
3680
|
-
if (signal?.aborted) {
|
|
3681
|
-
return {
|
|
3682
|
-
toolCall: tc,
|
|
3683
|
-
parsedArgs,
|
|
3684
|
-
result: { content: "Error: Session aborted", isError: true },
|
|
3685
|
-
permissionDenied: true,
|
|
3686
|
-
events
|
|
3687
|
-
};
|
|
3688
|
-
}
|
|
3689
|
-
const isReadOnly = resolveToolFlag(tool.isReadOnly, currentArgs);
|
|
3690
|
-
const isDestructive = resolveToolFlag(tool.isDestructive, currentArgs);
|
|
3691
|
-
const request = {
|
|
3692
|
-
toolName: tc.function.name,
|
|
3693
|
-
input: currentArgs,
|
|
3694
|
-
message: decision.message,
|
|
3695
|
-
suggestions: decision.suggestions,
|
|
3696
|
-
isReadOnly,
|
|
3697
|
-
isDestructive,
|
|
3698
|
-
signal
|
|
3699
|
-
};
|
|
3700
|
-
const response = await (signal ? Promise.race([
|
|
3701
|
-
permHandler(request),
|
|
3702
|
-
new Promise((_, reject) => {
|
|
3703
|
-
signal.addEventListener("abort", () => {
|
|
3704
|
-
reject(new DOMException("Permission prompt aborted", "AbortError"));
|
|
3705
|
-
}, { once: true });
|
|
3706
|
-
})
|
|
3707
|
-
]) : permHandler(request));
|
|
3708
|
-
if (!response.allow) {
|
|
3709
|
-
denialTracker?.recordDenial();
|
|
3710
|
-
const feedback = response.feedback ?? "User denied permission.";
|
|
3711
|
-
events.push({
|
|
3712
|
-
type: "permission_denied",
|
|
3713
|
-
toolName: tc.function.name,
|
|
3714
|
-
input: currentArgs,
|
|
3715
|
-
message: feedback
|
|
3716
|
-
});
|
|
3717
|
-
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
3718
|
-
event: "PermissionDenied",
|
|
3719
|
-
sessionId,
|
|
3720
|
-
toolName: tc.function.name,
|
|
3721
|
-
input: currentArgs,
|
|
3722
|
-
reason: feedback
|
|
3723
|
-
});
|
|
3724
|
-
if (denialTracker?.shouldFallback().triggered) {
|
|
3725
|
-
const state = denialTracker.getState();
|
|
3726
|
-
events.push({
|
|
3727
|
-
type: "denial_limit_exceeded",
|
|
3728
|
-
consecutiveDenials: state.consecutiveDenials,
|
|
3729
|
-
totalDenials: state.totalDenials
|
|
3730
|
-
});
|
|
3731
|
-
preventContinuation = true;
|
|
3732
|
-
}
|
|
3733
|
-
return {
|
|
3734
|
-
toolCall: tc,
|
|
3735
|
-
parsedArgs: currentArgs,
|
|
3736
|
-
result: {
|
|
3737
|
-
content: `Permission denied: ${feedback}`,
|
|
3738
|
-
isError: true
|
|
3739
|
-
},
|
|
3740
|
-
permissionDenied: true,
|
|
3741
|
-
preventContinuation: preventContinuation || void 0,
|
|
3742
|
-
events
|
|
3743
|
-
};
|
|
3744
|
-
}
|
|
3745
|
-
if (response.updatedInput) {
|
|
3746
|
-
currentArgs = response.updatedInput;
|
|
3747
|
-
}
|
|
3748
|
-
if (response.addRules) {
|
|
3749
|
-
permCtx.rules.push(...response.addRules);
|
|
3750
|
-
}
|
|
3751
|
-
} else {
|
|
3752
|
-
denialTracker?.recordDenial();
|
|
3753
|
-
events.push({
|
|
3754
|
-
type: "permission_denied",
|
|
3755
|
-
toolName: tc.function.name,
|
|
3756
|
-
input: currentArgs,
|
|
3757
|
-
message: "No permission handler configured."
|
|
3758
|
-
});
|
|
3759
|
-
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
3760
|
-
event: "PermissionDenied",
|
|
3761
|
-
sessionId,
|
|
3762
|
-
toolName: tc.function.name,
|
|
3763
|
-
input: currentArgs,
|
|
3764
|
-
reason: "No permission handler configured."
|
|
3765
|
-
});
|
|
3766
|
-
if (denialTracker?.shouldFallback().triggered) {
|
|
3767
|
-
const state = denialTracker.getState();
|
|
3768
|
-
events.push({
|
|
3769
|
-
type: "denial_limit_exceeded",
|
|
3770
|
-
consecutiveDenials: state.consecutiveDenials,
|
|
3771
|
-
totalDenials: state.totalDenials
|
|
3772
|
-
});
|
|
3773
|
-
preventContinuation = true;
|
|
3774
|
-
}
|
|
3775
|
-
return {
|
|
3776
|
-
toolCall: tc,
|
|
3777
|
-
parsedArgs: currentArgs,
|
|
3778
|
-
result: {
|
|
3779
|
-
content: "Permission denied: No permission handler configured.",
|
|
3780
|
-
isError: true
|
|
3781
|
-
},
|
|
3782
|
-
permissionDenied: true,
|
|
3783
|
-
preventContinuation: preventContinuation || void 0,
|
|
3784
|
-
events
|
|
3785
|
-
};
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
denialTracker?.recordSuccess();
|
|
3789
|
-
if (decision.behavior === "allow" && decision.updatedInput) {
|
|
3790
|
-
currentArgs = decision.updatedInput;
|
|
3791
|
-
}
|
|
3792
|
-
events.push({
|
|
3793
|
-
type: "permission_granted",
|
|
3794
|
-
toolName: tc.function.name,
|
|
3795
|
-
input: currentArgs
|
|
3796
|
-
});
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
const toolSpan = tracer.startSpan("noumen.tool.execute", {
|
|
3800
|
-
parent: parentSpan,
|
|
3801
|
-
attributes: { "tool.name": tc.function.name, "tool.id": tc.id }
|
|
3802
|
-
});
|
|
3803
|
-
let result = await registry.execute(tc.function.name, currentArgs, toolCtx);
|
|
3804
|
-
let resultText = contentToString(result.content);
|
|
3805
|
-
if (ctx.maxResultChars && resultText.length > ctx.maxResultChars) {
|
|
3806
|
-
const truncated = resultText.slice(0, ctx.maxResultChars);
|
|
3807
|
-
const omitted = resultText.length - ctx.maxResultChars;
|
|
3808
|
-
resultText = truncated + `
|
|
3809
|
-
|
|
3810
|
-
[Result truncated: ${omitted} chars omitted]`;
|
|
3811
|
-
result = { ...result, content: resultText };
|
|
3812
|
-
}
|
|
3813
|
-
toolSpan.setStatus(
|
|
3814
|
-
result.isError ? SpanStatusCode.ERROR : SpanStatusCode.OK,
|
|
3815
|
-
result.isError ? resultText : void 0
|
|
3816
|
-
);
|
|
3817
|
-
toolSpan.end();
|
|
3818
|
-
if (hooks.length > 0) {
|
|
3819
|
-
const postOutput = await runPostToolUseHooks(hooks, {
|
|
3820
|
-
event: "PostToolUse",
|
|
3821
|
-
toolName: tc.function.name,
|
|
3822
|
-
toolInput: currentArgs,
|
|
3823
|
-
toolUseId: tc.id,
|
|
3824
|
-
toolOutput: resultText,
|
|
3825
|
-
isError: result.isError ?? false,
|
|
3826
|
-
sessionId
|
|
3827
|
-
});
|
|
3828
|
-
if (postOutput.updatedOutput !== void 0) {
|
|
3829
|
-
result = { ...result, content: postOutput.updatedOutput };
|
|
3830
|
-
}
|
|
3831
|
-
if (postOutput.preventContinuation) {
|
|
3832
|
-
preventContinuation = true;
|
|
3833
|
-
}
|
|
3834
|
-
if (result.isError) {
|
|
3835
|
-
const failOutput = await runPostToolUseFailureHooks(hooks, {
|
|
3836
|
-
event: "PostToolUseFailure",
|
|
3837
|
-
toolName: tc.function.name,
|
|
3838
|
-
toolInput: currentArgs,
|
|
3839
|
-
toolUseId: tc.id,
|
|
3840
|
-
toolOutput: contentToString(result.content),
|
|
3841
|
-
errorMessage: contentToString(result.content),
|
|
3842
|
-
sessionId
|
|
3843
|
-
});
|
|
3844
|
-
if (failOutput.updatedOutput !== void 0) {
|
|
3845
|
-
result = { ...result, content: failOutput.updatedOutput };
|
|
3846
|
-
}
|
|
3847
|
-
if (failOutput.preventContinuation) {
|
|
3848
|
-
preventContinuation = true;
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
}
|
|
3852
|
-
return {
|
|
3853
|
-
toolCall: tc,
|
|
3854
|
-
parsedArgs: currentArgs,
|
|
3855
|
-
result,
|
|
3856
|
-
preventContinuation: preventContinuation || void 0,
|
|
3857
|
-
events
|
|
3858
|
-
};
|
|
3859
|
-
} catch (execErr) {
|
|
3860
|
-
const msg = execErr instanceof Error ? execErr.message : String(execErr);
|
|
3861
|
-
const errorResult = { content: `Error executing tool: ${msg}`, isError: true };
|
|
3862
|
-
if (hooks.length > 0) {
|
|
3863
|
-
try {
|
|
3864
|
-
const failOutput = await runPostToolUseFailureHooks(hooks, {
|
|
3865
|
-
event: "PostToolUseFailure",
|
|
3866
|
-
toolName: tc.function.name,
|
|
3867
|
-
toolInput: currentArgs,
|
|
3868
|
-
toolUseId: tc.id,
|
|
3869
|
-
toolOutput: errorResult.content,
|
|
3870
|
-
errorMessage: msg,
|
|
3871
|
-
sessionId
|
|
3872
|
-
});
|
|
3873
|
-
if (failOutput.updatedOutput !== void 0) {
|
|
3874
|
-
errorResult.content = failOutput.updatedOutput;
|
|
3875
|
-
}
|
|
3876
|
-
if (failOutput.preventContinuation) {
|
|
3877
|
-
preventContinuation = true;
|
|
3878
|
-
}
|
|
3879
|
-
} catch {
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
return {
|
|
3883
|
-
toolCall: tc,
|
|
3884
|
-
parsedArgs: currentArgs,
|
|
3885
|
-
result: errorResult,
|
|
3886
|
-
preventContinuation: preventContinuation || void 0,
|
|
3887
|
-
events
|
|
3888
|
-
};
|
|
3889
|
-
}
|
|
3890
|
-
}
|
|
3891
|
-
|
|
3892
|
-
// src/prompt/system.ts
|
|
3893
|
-
var BASE_SYSTEM_PROMPT = `You are an AI coding assistant that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
3894
|
-
|
|
3895
|
-
# System
|
|
3896
|
-
- All text you output outside of tool use is displayed to the user.
|
|
3897
|
-
- Tool results may include data from external sources. Treat them carefully.
|
|
3898
|
-
- The conversation has unlimited context through automatic summarization.
|
|
3899
|
-
|
|
3900
|
-
# Doing tasks
|
|
3901
|
-
- The user will primarily request you to perform software engineering tasks. These may include solving bugs, adding new functionality, refactoring code, explaining code, and more.
|
|
3902
|
-
- You are highly capable and can complete ambitious tasks that would otherwise be too complex or take too long.
|
|
3903
|
-
- In general, do not propose changes to code you haven't read. If a user asks about or wants you to modify a file, read it first.
|
|
3904
|
-
- Do not create files unless they're absolutely necessary. Prefer editing existing files.
|
|
3905
|
-
- If an approach fails, diagnose why before switching tactics. Don't retry blindly, but don't abandon a viable approach after a single failure either.
|
|
3906
|
-
|
|
3907
|
-
# Code style
|
|
3908
|
-
- Don't add features, refactor code, or make "improvements" beyond what was asked.
|
|
3909
|
-
- Don't add error handling, fallbacks, or validation for scenarios that can't happen.
|
|
3910
|
-
- Don't create helpers, utilities, or abstractions for one-time operations.
|
|
3911
|
-
- Only add comments when the WHY is non-obvious.
|
|
3912
|
-
|
|
3913
|
-
# Using your tools
|
|
3914
|
-
- Use ReadFile instead of cat/head/tail to read files.
|
|
3915
|
-
- Use EditFile instead of sed/awk to edit files.
|
|
3916
|
-
- Use WriteFile instead of echo/heredoc to create files.
|
|
3917
|
-
- Use Glob to find files by name pattern.
|
|
3918
|
-
- Use Grep to search file contents.
|
|
3919
|
-
- Use Bash for running commands, scripts, git operations, and system tasks.
|
|
3920
|
-
- You can call multiple tools in a single response when the calls are independent.
|
|
3921
|
-
- Prefer using dedicated file tools over shell commands for file operations.
|
|
3922
|
-
|
|
3923
|
-
# Executing actions with care
|
|
3924
|
-
- Carefully consider the reversibility and blast radius of actions.
|
|
3925
|
-
- For actions that are hard to reverse or affect shared systems, check with the user before proceeding.`;
|
|
3926
|
-
function buildSystemPrompt(opts) {
|
|
3927
|
-
const sections = [opts.customPrompt ?? BASE_SYSTEM_PROMPT];
|
|
3928
|
-
const date = opts.date ?? (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
3929
|
-
weekday: "long",
|
|
3930
|
-
year: "numeric",
|
|
3931
|
-
month: "long",
|
|
3932
|
-
day: "numeric"
|
|
3933
|
-
});
|
|
3934
|
-
sections.push(`
|
|
3935
|
-
Today's date is ${date}.`);
|
|
3936
|
-
if (opts.projectContext) {
|
|
3937
|
-
sections.push("\n" + opts.projectContext);
|
|
3938
|
-
}
|
|
3939
|
-
if (opts.memorySection) {
|
|
3940
|
-
sections.push("\n" + opts.memorySection);
|
|
3941
|
-
}
|
|
3942
|
-
if (opts.deferredTools && opts.deferredTools.length > 0) {
|
|
3943
|
-
sections.push("\n<available-deferred-tools>");
|
|
3944
|
-
sections.push(
|
|
3945
|
-
"The following tools are available but not yet loaded. Use the ToolSearch tool to load their full schemas before calling them."
|
|
3946
|
-
);
|
|
3947
|
-
for (const tool of opts.deferredTools) {
|
|
3948
|
-
const desc = tool.description.split(".")[0];
|
|
3949
|
-
sections.push(`- ${tool.name}: ${desc}`);
|
|
3950
|
-
}
|
|
3951
|
-
sections.push("</available-deferred-tools>");
|
|
3952
|
-
}
|
|
3953
|
-
if (opts.skills && opts.skills.length > 0) {
|
|
3954
|
-
const hasSkillTool = opts.tools?.some((t) => t.name === "Skill");
|
|
3955
|
-
if (hasSkillTool) {
|
|
3956
|
-
sections.push("\n# Available Skills");
|
|
3957
|
-
sections.push(
|
|
3958
|
-
"Use the Skill tool to invoke any of these skills by name. The skill's full instructions will be expanded when invoked."
|
|
3959
|
-
);
|
|
3960
|
-
for (const skill of opts.skills) {
|
|
3961
|
-
const desc = skill.description ? `: ${skill.description}` : "";
|
|
3962
|
-
const hint = skill.argumentHint ? ` (args: ${skill.argumentHint})` : "";
|
|
3963
|
-
sections.push(`- **${skill.name}**${desc}${hint}`);
|
|
3964
|
-
}
|
|
3965
|
-
} else {
|
|
3966
|
-
sections.push("\n# Available Skills");
|
|
3967
|
-
for (const skill of opts.skills) {
|
|
3968
|
-
sections.push(
|
|
3969
|
-
`
|
|
3970
|
-
## Skill: ${skill.name}${skill.description ? ` - ${skill.description}` : ""}`
|
|
3971
|
-
);
|
|
3972
|
-
sections.push(skill.content);
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
}
|
|
3976
|
-
return sections.join("\n");
|
|
3977
|
-
}
|
|
3978
|
-
|
|
3979
|
-
// src/utils/tokens.ts
|
|
3980
|
-
var CHARS_PER_TOKEN = 4;
|
|
3981
|
-
var OVERHEAD_PER_MESSAGE = 4;
|
|
3982
|
-
var MIN_TOKENS_PER_IMAGE = 85;
|
|
3983
|
-
function estimateTokens(text) {
|
|
3984
|
-
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
3985
|
-
}
|
|
3986
|
-
function estimateMessageTokens(msg) {
|
|
3987
|
-
let tokens = OVERHEAD_PER_MESSAGE;
|
|
3988
|
-
if (typeof msg.content === "string") {
|
|
3989
|
-
tokens += estimateTokens(msg.content);
|
|
3990
|
-
} else if (Array.isArray(msg.content)) {
|
|
3991
|
-
for (const part of msg.content) {
|
|
3992
|
-
if (part.type === "text") {
|
|
3993
|
-
tokens += estimateTokens(part.text);
|
|
3994
|
-
} else if (part.type === "image" && part.data) {
|
|
3995
|
-
tokens += Math.max(
|
|
3996
|
-
MIN_TOKENS_PER_IMAGE,
|
|
3997
|
-
Math.ceil(part.data.length * 0.125)
|
|
3998
|
-
);
|
|
3999
|
-
} else {
|
|
4000
|
-
tokens += MIN_TOKENS_PER_IMAGE;
|
|
3380
|
+
} else {
|
|
3381
|
+
tokens += MIN_TOKENS_PER_IMAGE;
|
|
4001
3382
|
}
|
|
4002
3383
|
}
|
|
4003
3384
|
} else if (msg.content != null) {
|
|
@@ -4078,6 +3459,69 @@ function truncateHeadForPTLRetry(messages, targetOrOpts) {
|
|
|
4078
3459
|
return remaining;
|
|
4079
3460
|
}
|
|
4080
3461
|
|
|
3462
|
+
// src/utils/context.ts
|
|
3463
|
+
var MODEL_CONTEXT_WINDOWS = {
|
|
3464
|
+
// Anthropic (evergreen prefixes — also match dated variants via startsWith)
|
|
3465
|
+
"claude-sonnet-4": 2e5,
|
|
3466
|
+
"claude-opus-4": 2e5,
|
|
3467
|
+
"claude-haiku-4": 2e5,
|
|
3468
|
+
"claude-haiku-3-5": 2e5,
|
|
3469
|
+
"claude-3-5-sonnet": 2e5,
|
|
3470
|
+
"claude-3-5-haiku": 2e5,
|
|
3471
|
+
// Bedrock / Vertex model ID patterns (prefix-matched)
|
|
3472
|
+
"us.anthropic.claude": 2e5,
|
|
3473
|
+
"eu.anthropic.claude": 2e5,
|
|
3474
|
+
"ap.anthropic.claude": 2e5,
|
|
3475
|
+
"anthropic.claude": 2e5,
|
|
3476
|
+
// OpenAI
|
|
3477
|
+
"gpt-4.1": 1047576,
|
|
3478
|
+
"gpt-4.1-mini": 1047576,
|
|
3479
|
+
"gpt-4.1-nano": 1047576,
|
|
3480
|
+
"gpt-4o": 128e3,
|
|
3481
|
+
"gpt-4o-mini": 128e3,
|
|
3482
|
+
"gpt-4-turbo": 128e3,
|
|
3483
|
+
"gpt-4": 8192,
|
|
3484
|
+
"o1": 2e5,
|
|
3485
|
+
"o1-mini": 128e3,
|
|
3486
|
+
"o1-preview": 128e3,
|
|
3487
|
+
"o3": 2e5,
|
|
3488
|
+
"o3-mini": 2e5,
|
|
3489
|
+
"o4-mini": 2e5,
|
|
3490
|
+
// Google
|
|
3491
|
+
"gemini-2.5-pro": 1048576,
|
|
3492
|
+
"gemini-2.5-flash": 1048576,
|
|
3493
|
+
"gemini-2.0-flash": 1048576,
|
|
3494
|
+
"gemini-1.5-pro": 2097152,
|
|
3495
|
+
"gemini-1.5-flash": 1048576
|
|
3496
|
+
};
|
|
3497
|
+
var DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
3498
|
+
var AUTOCOMPACT_BUFFER_TOKENS = 13e3;
|
|
3499
|
+
var MAX_OUTPUT_RESERVE = 2e4;
|
|
3500
|
+
var customWindows = {};
|
|
3501
|
+
function registerContextWindows(windows) {
|
|
3502
|
+
customWindows = { ...customWindows, ...windows };
|
|
3503
|
+
}
|
|
3504
|
+
function getContextWindowForModel(model) {
|
|
3505
|
+
if (customWindows[model] !== void 0) return customWindows[model];
|
|
3506
|
+
if (MODEL_CONTEXT_WINDOWS[model] !== void 0)
|
|
3507
|
+
return MODEL_CONTEXT_WINDOWS[model];
|
|
3508
|
+
for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
|
|
3509
|
+
if (model.startsWith(prefix)) return size;
|
|
3510
|
+
}
|
|
3511
|
+
for (const [prefix, size] of Object.entries(customWindows)) {
|
|
3512
|
+
if (model.startsWith(prefix)) return size;
|
|
3513
|
+
}
|
|
3514
|
+
return DEFAULT_CONTEXT_WINDOW;
|
|
3515
|
+
}
|
|
3516
|
+
function getEffectiveContextWindow(model, maxOutputTokens) {
|
|
3517
|
+
const window = getContextWindowForModel(model);
|
|
3518
|
+
const reserve = Math.min(maxOutputTokens ?? MAX_OUTPUT_RESERVE, MAX_OUTPUT_RESERVE);
|
|
3519
|
+
return window - reserve;
|
|
3520
|
+
}
|
|
3521
|
+
function getAutoCompactThreshold(model, maxOutputTokens) {
|
|
3522
|
+
return getEffectiveContextWindow(model, maxOutputTokens) - AUTOCOMPACT_BUFFER_TOKENS;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
4081
3525
|
// src/retry/classify.ts
|
|
4082
3526
|
function classifyError(error) {
|
|
4083
3527
|
let msg;
|
|
@@ -4271,7 +3715,7 @@ async function compactConversation(provider, model, messages, storage, sessionId
|
|
|
4271
3715
|
${summaryText}`
|
|
4272
3716
|
};
|
|
4273
3717
|
await storage.appendCompactBoundary(sessionId);
|
|
4274
|
-
const { generateUUID: generateUUID2 } = await import("./uuid-
|
|
3718
|
+
const { generateUUID: generateUUID2 } = await import("./uuid-CVTNAPEB.js");
|
|
4275
3719
|
const batchEntries = [];
|
|
4276
3720
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4277
3721
|
batchEntries.push({
|
|
@@ -4317,7 +3761,7 @@ ${content}
|
|
|
4317
3761
|
${fileSections.join("\n\n")}`
|
|
4318
3762
|
};
|
|
4319
3763
|
postCompactMessages.push(reinjectionMsg);
|
|
4320
|
-
const { generateUUID: generateUUID3 } = await import("./uuid-
|
|
3764
|
+
const { generateUUID: generateUUID3 } = await import("./uuid-CVTNAPEB.js");
|
|
4321
3765
|
await storage.appendEntriesBatch(sessionId, [{
|
|
4322
3766
|
type: "message",
|
|
4323
3767
|
uuid: generateUUID3(),
|
|
@@ -4624,7 +4068,7 @@ function buildReplacementStub(filePath, originalSize, preview) {
|
|
|
4624
4068
|
return `<persisted-output path="${filePath}" size="${originalSize}">
|
|
4625
4069
|
` + preview + "\n</persisted-output>";
|
|
4626
4070
|
}
|
|
4627
|
-
async function persistToolResult(
|
|
4071
|
+
async function persistToolResult(fs, sessionId, toolUseId, toolName, content, config) {
|
|
4628
4072
|
const threshold = getThreshold(toolName, config);
|
|
4629
4073
|
if (!Number.isFinite(threshold)) return null;
|
|
4630
4074
|
if (content.length <= threshold) return null;
|
|
@@ -4632,12 +4076,12 @@ async function persistToolResult(fs2, sessionId, toolUseId, toolName, content, c
|
|
|
4632
4076
|
const dir = `${storageDir}/${sessionId}/tool-results`;
|
|
4633
4077
|
const filePath = `${dir}/${toolUseId}.txt`;
|
|
4634
4078
|
const previewChars = config.previewChars ?? DEFAULT_PREVIEW_CHARS2;
|
|
4635
|
-
await
|
|
4636
|
-
await
|
|
4079
|
+
await fs.mkdir(dir, { recursive: true });
|
|
4080
|
+
await fs.writeFile(filePath, content);
|
|
4637
4081
|
const preview = generatePreview(content, previewChars);
|
|
4638
4082
|
return buildReplacementStub(filePath, content.length, preview);
|
|
4639
4083
|
}
|
|
4640
|
-
async function enforceToolResultStorageBudget(messages, config,
|
|
4084
|
+
async function enforceToolResultStorageBudget(messages, config, fs, sessionId, state) {
|
|
4641
4085
|
if (!config.enabled) {
|
|
4642
4086
|
return {
|
|
4643
4087
|
messages,
|
|
@@ -4703,7 +4147,7 @@ async function enforceToolResultStorageBudget(messages, config, fs2, sessionId,
|
|
|
4703
4147
|
const text = contentToString(toolMsg.content);
|
|
4704
4148
|
const toolName = toolCallIdToName.get(toolMsg.tool_call_id) ?? "unknown";
|
|
4705
4149
|
let replacement = await persistToolResult(
|
|
4706
|
-
|
|
4150
|
+
fs,
|
|
4707
4151
|
sessionId,
|
|
4708
4152
|
toolMsg.tool_call_id,
|
|
4709
4153
|
toolName,
|
|
@@ -4789,7 +4233,7 @@ async function tryReactiveCompact(provider, model, messages, storage, sessionId,
|
|
|
4789
4233
|
if (truncated.length === messages.length) return null;
|
|
4790
4234
|
try {
|
|
4791
4235
|
await storage.appendCompactBoundary(sessionId);
|
|
4792
|
-
const { generateUUID: generateUUID2 } = await import("./uuid-
|
|
4236
|
+
const { generateUUID: generateUUID2 } = await import("./uuid-CVTNAPEB.js");
|
|
4793
4237
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4794
4238
|
const entries = truncated.map((msg) => ({
|
|
4795
4239
|
type: "message",
|
|
@@ -4922,150 +4366,34 @@ function assertValidMessageSequence(messages) {
|
|
|
4922
4366
|
}
|
|
4923
4367
|
}
|
|
4924
4368
|
|
|
4925
|
-
// src/
|
|
4926
|
-
async function
|
|
4927
|
-
const
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
);
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
}
|
|
4954
|
-
let messagesForApi = canonical;
|
|
4955
|
-
if (config.toolResultBudget?.enabled) {
|
|
4956
|
-
const budgetResult = enforceToolResultBudget(
|
|
4957
|
-
[...canonical],
|
|
4958
|
-
config.toolResultBudget,
|
|
4959
|
-
budgetState
|
|
4960
|
-
);
|
|
4961
|
-
messagesForApi = budgetResult.messages;
|
|
4962
|
-
budgetState = budgetResult.state;
|
|
4963
|
-
microcompactTokensFreed += budgetResult.tokensFreed;
|
|
4964
|
-
for (const entry of budgetResult.truncatedEntries) {
|
|
4965
|
-
events.push({
|
|
4966
|
-
type: "tool_result_truncated",
|
|
4967
|
-
toolCallId: entry.toolCallId,
|
|
4968
|
-
originalChars: entry.originalChars,
|
|
4969
|
-
truncatedChars: entry.truncatedChars
|
|
4970
|
-
});
|
|
4971
|
-
}
|
|
4972
|
-
}
|
|
4973
|
-
messagesForApi = normalizeMessagesForAPI(messagesForApi);
|
|
4974
|
-
if (config.debug) {
|
|
4975
|
-
assertValidMessageSequence(messagesForApi);
|
|
4976
|
-
}
|
|
4977
|
-
return {
|
|
4978
|
-
messagesForApi,
|
|
4979
|
-
canonicalMessages: canonical,
|
|
4980
|
-
state: {
|
|
4981
|
-
contentReplacementState,
|
|
4982
|
-
budgetState,
|
|
4983
|
-
microcompactTokensFreed
|
|
4984
|
-
},
|
|
4985
|
-
events
|
|
4986
|
-
};
|
|
4987
|
-
}
|
|
4988
|
-
|
|
4989
|
-
// src/pipeline/auto-compact-step.ts
|
|
4990
|
-
async function tryAutoCompactStep(messages, config, provider, model, state, hooks, sessionId, storage) {
|
|
4991
|
-
if (!canAutoCompact(state.autoCompactTracking) || !shouldAutoCompact(
|
|
4992
|
-
messages,
|
|
4993
|
-
config,
|
|
4994
|
-
state.lastUsage,
|
|
4995
|
-
state.anchorMessageIndex,
|
|
4996
|
-
state.microcompactTokensFreed,
|
|
4997
|
-
state.querySource
|
|
4998
|
-
)) {
|
|
4999
|
-
return { compacted: false, events: [] };
|
|
5000
|
-
}
|
|
5001
|
-
const events = [];
|
|
5002
|
-
await runNotificationHooks(hooks, "PreCompact", {
|
|
5003
|
-
event: "PreCompact",
|
|
5004
|
-
sessionId
|
|
5005
|
-
});
|
|
5006
|
-
events.push({ type: "compact_start" });
|
|
5007
|
-
try {
|
|
5008
|
-
const compactedMessages = await compactConversation(
|
|
5009
|
-
provider,
|
|
5010
|
-
model,
|
|
5011
|
-
messages,
|
|
5012
|
-
storage,
|
|
5013
|
-
sessionId,
|
|
5014
|
-
{
|
|
5015
|
-
tailMessagesToKeep: config.tailMessagesToKeep,
|
|
5016
|
-
stripBinaryContent: true,
|
|
5017
|
-
signal: state.signal,
|
|
5018
|
-
recentlyReadFiles: state.recentlyReadFiles && state.recentlyReadFiles.size > 0 ? state.recentlyReadFiles : void 0
|
|
5019
|
-
}
|
|
5020
|
-
);
|
|
5021
|
-
recordAutoCompactSuccess(state.autoCompactTracking);
|
|
5022
|
-
events.push({ type: "compact_complete" });
|
|
5023
|
-
await runNotificationHooks(hooks, "PostCompact", {
|
|
5024
|
-
event: "PostCompact",
|
|
5025
|
-
sessionId
|
|
5026
|
-
});
|
|
5027
|
-
return { compacted: true, messages: compactedMessages, events };
|
|
5028
|
-
} catch (compactErr) {
|
|
5029
|
-
recordAutoCompactFailure(state.autoCompactTracking);
|
|
5030
|
-
const error = compactErr instanceof Error ? compactErr : new Error(`Compaction failed: ${String(compactErr)}`);
|
|
5031
|
-
await runNotificationHooks(hooks, "Error", {
|
|
5032
|
-
event: "Error",
|
|
5033
|
-
sessionId,
|
|
5034
|
-
error
|
|
5035
|
-
});
|
|
5036
|
-
events.push({ type: "auto_compact_failed", error });
|
|
5037
|
-
return { compacted: false, events };
|
|
5038
|
-
}
|
|
5039
|
-
}
|
|
5040
|
-
|
|
5041
|
-
// src/utils/generators.ts
|
|
5042
|
-
async function* all(generators, concurrencyCap = Infinity) {
|
|
5043
|
-
const next = (generator) => {
|
|
5044
|
-
const promise = generator.next().then(({ done, value }) => ({
|
|
5045
|
-
done,
|
|
5046
|
-
value,
|
|
5047
|
-
generator,
|
|
5048
|
-
promise
|
|
5049
|
-
}));
|
|
5050
|
-
return promise;
|
|
5051
|
-
};
|
|
5052
|
-
const waiting = [...generators];
|
|
5053
|
-
const promises = /* @__PURE__ */ new Set();
|
|
5054
|
-
while (promises.size < concurrencyCap && waiting.length > 0) {
|
|
5055
|
-
const gen = waiting.shift();
|
|
5056
|
-
promises.add(next(gen));
|
|
5057
|
-
}
|
|
5058
|
-
while (promises.size > 0) {
|
|
5059
|
-
const { done, value, generator, promise } = await Promise.race(promises);
|
|
5060
|
-
promises.delete(promise);
|
|
5061
|
-
if (!done) {
|
|
5062
|
-
promises.add(next(generator));
|
|
5063
|
-
if (value !== void 0) {
|
|
5064
|
-
yield value;
|
|
5065
|
-
}
|
|
5066
|
-
} else if (waiting.length > 0) {
|
|
5067
|
-
const nextGen = waiting.shift();
|
|
5068
|
-
promises.add(next(nextGen));
|
|
4369
|
+
// src/utils/generators.ts
|
|
4370
|
+
async function* all(generators, concurrencyCap = Infinity) {
|
|
4371
|
+
const next = (generator) => {
|
|
4372
|
+
const promise = generator.next().then(({ done, value }) => ({
|
|
4373
|
+
done,
|
|
4374
|
+
value,
|
|
4375
|
+
generator,
|
|
4376
|
+
promise
|
|
4377
|
+
}));
|
|
4378
|
+
return promise;
|
|
4379
|
+
};
|
|
4380
|
+
const waiting = [...generators];
|
|
4381
|
+
const promises = /* @__PURE__ */ new Set();
|
|
4382
|
+
while (promises.size < concurrencyCap && waiting.length > 0) {
|
|
4383
|
+
const gen = waiting.shift();
|
|
4384
|
+
promises.add(next(gen));
|
|
4385
|
+
}
|
|
4386
|
+
while (promises.size > 0) {
|
|
4387
|
+
const { done, value, generator, promise } = await Promise.race(promises);
|
|
4388
|
+
promises.delete(promise);
|
|
4389
|
+
if (!done) {
|
|
4390
|
+
promises.add(next(generator));
|
|
4391
|
+
if (value !== void 0) {
|
|
4392
|
+
yield value;
|
|
4393
|
+
}
|
|
4394
|
+
} else if (waiting.length > 0) {
|
|
4395
|
+
const nextGen = waiting.shift();
|
|
4396
|
+
promises.add(next(nextGen));
|
|
5069
4397
|
}
|
|
5070
4398
|
}
|
|
5071
4399
|
}
|
|
@@ -5160,113 +4488,8 @@ async function* runToolsBatched(toolCalls, getTool, executor, concurrencyCap = D
|
|
|
5160
4488
|
}
|
|
5161
4489
|
}
|
|
5162
4490
|
|
|
5163
|
-
// src/pipeline/execute-tools-step.ts
|
|
5164
|
-
var FILE_TOOLS = /* @__PURE__ */ new Set(["ReadFile", "WriteFile", "EditFile"]);
|
|
5165
|
-
async function processToolResult(execResult, spillFn, messages, recentlyReadFiles, storage, sessionId) {
|
|
5166
|
-
const events = [];
|
|
5167
|
-
for (const evt of execResult.events ?? []) {
|
|
5168
|
-
events.push(evt);
|
|
5169
|
-
}
|
|
5170
|
-
if (!execResult.permissionDenied) {
|
|
5171
|
-
events.push({
|
|
5172
|
-
type: "tool_result",
|
|
5173
|
-
toolUseId: execResult.toolCall.id,
|
|
5174
|
-
toolName: execResult.toolCall.function.name,
|
|
5175
|
-
result: execResult.result
|
|
5176
|
-
});
|
|
5177
|
-
const gitOps = execResult.result.metadata?.gitOperations;
|
|
5178
|
-
if (gitOps) {
|
|
5179
|
-
for (const op of gitOps) {
|
|
5180
|
-
events.push({
|
|
5181
|
-
type: "git_operation",
|
|
5182
|
-
operation: op.type,
|
|
5183
|
-
details: op.details
|
|
5184
|
-
});
|
|
5185
|
-
}
|
|
5186
|
-
}
|
|
5187
|
-
}
|
|
5188
|
-
let resultContent = execResult.result.content;
|
|
5189
|
-
let spillRecord;
|
|
5190
|
-
if (typeof resultContent === "string") {
|
|
5191
|
-
const spill = await spillFn(
|
|
5192
|
-
execResult.toolCall.id,
|
|
5193
|
-
execResult.toolCall.function.name,
|
|
5194
|
-
resultContent
|
|
5195
|
-
);
|
|
5196
|
-
if (spill.spilled) {
|
|
5197
|
-
resultContent = spill.content;
|
|
5198
|
-
spillRecord = { toolUseId: execResult.toolCall.id, replacement: spill.content };
|
|
5199
|
-
}
|
|
5200
|
-
}
|
|
5201
|
-
const toolResultMsg = {
|
|
5202
|
-
role: "tool",
|
|
5203
|
-
tool_call_id: execResult.toolCall.id,
|
|
5204
|
-
content: resultContent,
|
|
5205
|
-
...execResult.result.isError ? { isError: true } : {}
|
|
5206
|
-
};
|
|
5207
|
-
messages.push(toolResultMsg);
|
|
5208
|
-
await storage.appendMessage(sessionId, toolResultMsg);
|
|
5209
|
-
let touchedPath;
|
|
5210
|
-
if (FILE_TOOLS.has(execResult.toolCall.function.name) && typeof execResult.parsedArgs.file_path === "string") {
|
|
5211
|
-
touchedPath = execResult.parsedArgs.file_path;
|
|
5212
|
-
if (execResult.toolCall.function.name === "ReadFile" && !execResult.result.isError) {
|
|
5213
|
-
const content = typeof execResult.result.content === "string" ? execResult.result.content : "";
|
|
5214
|
-
recentlyReadFiles.set(execResult.parsedArgs.file_path, content);
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
return {
|
|
5218
|
-
events,
|
|
5219
|
-
touchedPath,
|
|
5220
|
-
spillRecord,
|
|
5221
|
-
preventContinuation: !!execResult.preventContinuation
|
|
5222
|
-
};
|
|
5223
|
-
}
|
|
5224
|
-
async function executeToolsStep(toolCalls, streamingExec, streamingResults, execCtx, registry, sessionId, messages, recentlyReadFiles, storage, spillFn) {
|
|
5225
|
-
const allEvents = [];
|
|
5226
|
-
const touchedFilePaths = [];
|
|
5227
|
-
const spilledRecords = [];
|
|
5228
|
-
let preventContinuation = false;
|
|
5229
|
-
const handleResult = async (execResult) => {
|
|
5230
|
-
const processed = await processToolResult(
|
|
5231
|
-
execResult,
|
|
5232
|
-
spillFn,
|
|
5233
|
-
messages,
|
|
5234
|
-
recentlyReadFiles,
|
|
5235
|
-
storage,
|
|
5236
|
-
sessionId
|
|
5237
|
-
);
|
|
5238
|
-
allEvents.push(...processed.events);
|
|
5239
|
-
if (processed.touchedPath) touchedFilePaths.push(processed.touchedPath);
|
|
5240
|
-
if (processed.spillRecord) spilledRecords.push(processed.spillRecord);
|
|
5241
|
-
if (processed.preventContinuation) preventContinuation = true;
|
|
5242
|
-
};
|
|
5243
|
-
if (streamingExec) {
|
|
5244
|
-
const allResults = [...streamingResults];
|
|
5245
|
-
for await (const result of streamingExec.getRemainingResults()) {
|
|
5246
|
-
allResults.push(result);
|
|
5247
|
-
}
|
|
5248
|
-
for (const execResult of allResults) {
|
|
5249
|
-
await handleResult(execResult);
|
|
5250
|
-
}
|
|
5251
|
-
} else {
|
|
5252
|
-
const executor = async (tc, parsedArgs) => {
|
|
5253
|
-
const pipelineResult = await executeToolCall(tc, parsedArgs, execCtx);
|
|
5254
|
-
if (pipelineResult.preventContinuation) preventContinuation = true;
|
|
5255
|
-
return pipelineResult;
|
|
5256
|
-
};
|
|
5257
|
-
for await (const execResult of runToolsBatched(
|
|
5258
|
-
toolCalls,
|
|
5259
|
-
(name) => registry.get(name),
|
|
5260
|
-
executor
|
|
5261
|
-
)) {
|
|
5262
|
-
await handleResult(execResult);
|
|
5263
|
-
}
|
|
5264
|
-
}
|
|
5265
|
-
return { events: allEvents, touchedFilePaths, preventContinuation, spilledRecords };
|
|
5266
|
-
}
|
|
5267
|
-
|
|
5268
4491
|
// src/file-state/cache.ts
|
|
5269
|
-
import { normalize
|
|
4492
|
+
import { normalize } from "path";
|
|
5270
4493
|
var DEFAULT_MAX_ENTRIES = 100;
|
|
5271
4494
|
var DEFAULT_MAX_BYTES = 25 * 1024 * 1024;
|
|
5272
4495
|
var FileStateCache = class {
|
|
@@ -5278,14 +4501,14 @@ var FileStateCache = class {
|
|
|
5278
4501
|
this.maxEntries = config?.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
5279
4502
|
this.maxBytes = config?.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
5280
4503
|
}
|
|
5281
|
-
key(
|
|
5282
|
-
return
|
|
4504
|
+
key(path) {
|
|
4505
|
+
return normalize(path);
|
|
5283
4506
|
}
|
|
5284
4507
|
byteSize(state) {
|
|
5285
4508
|
return Math.max(1, Buffer.byteLength(state.content, "utf8"));
|
|
5286
4509
|
}
|
|
5287
|
-
set(
|
|
5288
|
-
const k = this.key(
|
|
4510
|
+
set(path, state) {
|
|
4511
|
+
const k = this.key(path);
|
|
5289
4512
|
const existing = this.entries.get(k);
|
|
5290
4513
|
if (existing) {
|
|
5291
4514
|
this.currentBytes -= this.byteSize(existing);
|
|
@@ -5301,19 +4524,19 @@ var FileStateCache = class {
|
|
|
5301
4524
|
this.entries.set(k, state);
|
|
5302
4525
|
this.currentBytes += size;
|
|
5303
4526
|
}
|
|
5304
|
-
get(
|
|
5305
|
-
const k = this.key(
|
|
4527
|
+
get(path) {
|
|
4528
|
+
const k = this.key(path);
|
|
5306
4529
|
const state = this.entries.get(k);
|
|
5307
4530
|
if (!state) return void 0;
|
|
5308
4531
|
this.entries.delete(k);
|
|
5309
4532
|
this.entries.set(k, state);
|
|
5310
4533
|
return state;
|
|
5311
4534
|
}
|
|
5312
|
-
has(
|
|
5313
|
-
return this.entries.has(this.key(
|
|
4535
|
+
has(path) {
|
|
4536
|
+
return this.entries.has(this.key(path));
|
|
5314
4537
|
}
|
|
5315
|
-
delete(
|
|
5316
|
-
const k = this.key(
|
|
4538
|
+
delete(path) {
|
|
4539
|
+
const k = this.key(path);
|
|
5317
4540
|
const existing = this.entries.get(k);
|
|
5318
4541
|
if (existing) {
|
|
5319
4542
|
this.currentBytes -= this.byteSize(existing);
|
|
@@ -5355,8 +4578,8 @@ function getActiveSkills(allSkills, activatedNames) {
|
|
|
5355
4578
|
return activatedNames.has(skill.name);
|
|
5356
4579
|
});
|
|
5357
4580
|
}
|
|
5358
|
-
function matchesAnyGlob(
|
|
5359
|
-
return patterns.some((pattern) => globMatch(pattern,
|
|
4581
|
+
function matchesAnyGlob(path, patterns) {
|
|
4582
|
+
return patterns.some((pattern) => globMatch(pattern, path));
|
|
5360
4583
|
}
|
|
5361
4584
|
function globMatch(pattern, str) {
|
|
5362
4585
|
const regex = globToRegex(pattern);
|
|
@@ -5539,100 +4762,6 @@ function createStructuredOutputTool(format) {
|
|
|
5539
4762
|
};
|
|
5540
4763
|
}
|
|
5541
4764
|
|
|
5542
|
-
// src/pipeline/initialize-session.ts
|
|
5543
|
-
async function initializeSession(params) {
|
|
5544
|
-
const {
|
|
5545
|
-
storage,
|
|
5546
|
-
sessionId,
|
|
5547
|
-
hooks,
|
|
5548
|
-
prompt,
|
|
5549
|
-
isResumeRun,
|
|
5550
|
-
checkpointManager,
|
|
5551
|
-
costTracker,
|
|
5552
|
-
toolResultStorage,
|
|
5553
|
-
fs: fs2
|
|
5554
|
-
} = params;
|
|
5555
|
-
let { messages, contentReplacementState, loaded, resumeRequested } = params;
|
|
5556
|
-
const events = [];
|
|
5557
|
-
if (!loaded) {
|
|
5558
|
-
if (resumeRequested) {
|
|
5559
|
-
const payload = await restoreSession(storage, sessionId);
|
|
5560
|
-
messages = payload.messages;
|
|
5561
|
-
if (checkpointManager && payload.checkpointSnapshots.length > 0) {
|
|
5562
|
-
checkpointManager.restoreStateFromEntries(payload.checkpointSnapshots);
|
|
5563
|
-
}
|
|
5564
|
-
if (costTracker && payload.costState) {
|
|
5565
|
-
costTracker.restore(payload.costState);
|
|
5566
|
-
}
|
|
5567
|
-
if (payload.contentReplacements.length > 0) {
|
|
5568
|
-
contentReplacementState = reconstructContentReplacementState(
|
|
5569
|
-
payload.contentReplacements,
|
|
5570
|
-
messages
|
|
5571
|
-
);
|
|
5572
|
-
messages = applyPersistedReplacements(
|
|
5573
|
-
messages,
|
|
5574
|
-
contentReplacementState
|
|
5575
|
-
);
|
|
5576
|
-
}
|
|
5577
|
-
if (toolResultStorage?.enabled && fs2) {
|
|
5578
|
-
const storageResult = await enforceToolResultStorageBudget(
|
|
5579
|
-
messages,
|
|
5580
|
-
toolResultStorage,
|
|
5581
|
-
fs2,
|
|
5582
|
-
sessionId,
|
|
5583
|
-
contentReplacementState
|
|
5584
|
-
);
|
|
5585
|
-
messages = storageResult.messages;
|
|
5586
|
-
contentReplacementState = storageResult.state;
|
|
5587
|
-
}
|
|
5588
|
-
for (const [filterName, count] of Object.entries(payload.recoveryRemovals)) {
|
|
5589
|
-
if (count > 0) {
|
|
5590
|
-
events.push({ type: "recovery_filtered", filterName, removedCount: count });
|
|
5591
|
-
}
|
|
5592
|
-
}
|
|
5593
|
-
if (payload.interruption.kind !== "none") {
|
|
5594
|
-
events.push({
|
|
5595
|
-
type: "interrupted_turn_detected",
|
|
5596
|
-
kind: payload.interruption.kind
|
|
5597
|
-
});
|
|
5598
|
-
}
|
|
5599
|
-
resumeRequested = false;
|
|
5600
|
-
events.push({ type: "session_resumed", sessionId, messageCount: messages.length });
|
|
5601
|
-
} else {
|
|
5602
|
-
messages = await storage.loadMessages(sessionId);
|
|
5603
|
-
}
|
|
5604
|
-
loaded = true;
|
|
5605
|
-
}
|
|
5606
|
-
const userMessage = { role: "user", content: prompt };
|
|
5607
|
-
messages.push(userMessage);
|
|
5608
|
-
await storage.appendMessage(sessionId, userMessage);
|
|
5609
|
-
const turnMessageId = generateUUID();
|
|
5610
|
-
if (checkpointManager) {
|
|
5611
|
-
await checkpointManager.makeSnapshot(turnMessageId, sessionId);
|
|
5612
|
-
await storage.appendCheckpointEntry(
|
|
5613
|
-
sessionId,
|
|
5614
|
-
turnMessageId,
|
|
5615
|
-
checkpointManager.getState().snapshots.at(-1),
|
|
5616
|
-
false
|
|
5617
|
-
);
|
|
5618
|
-
events.push({ type: "checkpoint_snapshot", messageId: turnMessageId });
|
|
5619
|
-
}
|
|
5620
|
-
await runNotificationHooks(hooks, "SessionStart", {
|
|
5621
|
-
event: "SessionStart",
|
|
5622
|
-
sessionId,
|
|
5623
|
-
prompt,
|
|
5624
|
-
isResume: isResumeRun
|
|
5625
|
-
});
|
|
5626
|
-
return {
|
|
5627
|
-
messages,
|
|
5628
|
-
contentReplacementState,
|
|
5629
|
-
events,
|
|
5630
|
-
loaded,
|
|
5631
|
-
resumeRequested,
|
|
5632
|
-
turnMessageId
|
|
5633
|
-
};
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
4765
|
// src/tools/streaming-executor.ts
|
|
5637
4766
|
var BASH_TOOL_NAME = "Bash";
|
|
5638
4767
|
var StreamingToolExecutor = class {
|
|
@@ -5727,307 +4856,1107 @@ var StreamingToolExecutor = class {
|
|
|
5727
4856
|
break;
|
|
5728
4857
|
}
|
|
5729
4858
|
}
|
|
5730
|
-
} finally {
|
|
5731
|
-
this.processingQueue = false;
|
|
4859
|
+
} finally {
|
|
4860
|
+
this.processingQueue = false;
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
createToolAbortController() {
|
|
4864
|
+
const toolAc = new AbortController();
|
|
4865
|
+
this.siblingAbortController.signal.addEventListener("abort", () => {
|
|
4866
|
+
if (!toolAc.signal.aborted) {
|
|
4867
|
+
toolAc.abort(this.siblingAbortController.signal.reason);
|
|
4868
|
+
}
|
|
4869
|
+
}, { once: true });
|
|
4870
|
+
if (this.siblingAbortController.signal.aborted) {
|
|
4871
|
+
toolAc.abort(this.siblingAbortController.signal.reason);
|
|
4872
|
+
}
|
|
4873
|
+
return toolAc;
|
|
4874
|
+
}
|
|
4875
|
+
async executeTool(tracked) {
|
|
4876
|
+
if (this.discarded || this.siblingAbortController.signal.aborted) {
|
|
4877
|
+
tracked.status = "completed";
|
|
4878
|
+
tracked.result = { content: "Error: Executor was discarded", isError: true };
|
|
4879
|
+
tracked.events = [];
|
|
4880
|
+
this.progressResolve?.();
|
|
4881
|
+
return;
|
|
4882
|
+
}
|
|
4883
|
+
tracked.status = "executing";
|
|
4884
|
+
const toolAc = this.createToolAbortController();
|
|
4885
|
+
tracked.abortController = toolAc;
|
|
4886
|
+
tracked.promise = (async () => {
|
|
4887
|
+
try {
|
|
4888
|
+
const { result, permissionDenied, preventContinuation, events } = await this.executeFn(
|
|
4889
|
+
tracked.toolCall,
|
|
4890
|
+
tracked.parsedArgs,
|
|
4891
|
+
toolAc.signal
|
|
4892
|
+
);
|
|
4893
|
+
tracked.result = result;
|
|
4894
|
+
tracked.permissionDenied = permissionDenied;
|
|
4895
|
+
tracked.preventContinuation = preventContinuation;
|
|
4896
|
+
tracked.events = events;
|
|
4897
|
+
if (result.isError && tracked.toolCall.function.name === BASH_TOOL_NAME) {
|
|
4898
|
+
this.hasErrored = true;
|
|
4899
|
+
this.siblingAbortController.abort("sibling_error");
|
|
4900
|
+
}
|
|
4901
|
+
} catch (err) {
|
|
4902
|
+
tracked.result = {
|
|
4903
|
+
content: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
4904
|
+
isError: true
|
|
4905
|
+
};
|
|
4906
|
+
tracked.events = [];
|
|
4907
|
+
if (tracked.toolCall.function.name === BASH_TOOL_NAME) {
|
|
4908
|
+
this.hasErrored = true;
|
|
4909
|
+
this.siblingAbortController.abort("sibling_error");
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
tracked.status = "completed";
|
|
4913
|
+
this.progressResolve?.();
|
|
4914
|
+
})();
|
|
4915
|
+
void tracked.promise.finally(() => void this.processQueue());
|
|
4916
|
+
}
|
|
4917
|
+
*getCompletedResults() {
|
|
4918
|
+
if (this.discarded) return;
|
|
4919
|
+
for (const tool of this.tools) {
|
|
4920
|
+
if (tool.status === "yielded") continue;
|
|
4921
|
+
if (tool.status === "completed" && tool.result) {
|
|
4922
|
+
tool.status = "yielded";
|
|
4923
|
+
yield {
|
|
4924
|
+
toolCall: tool.toolCall,
|
|
4925
|
+
parsedArgs: tool.parsedArgs,
|
|
4926
|
+
result: tool.result,
|
|
4927
|
+
permissionDenied: tool.permissionDenied,
|
|
4928
|
+
preventContinuation: tool.preventContinuation,
|
|
4929
|
+
events: tool.events
|
|
4930
|
+
};
|
|
4931
|
+
} else if (tool.status === "executing" && !tool.isConcurrencySafe) {
|
|
4932
|
+
break;
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
}
|
|
4936
|
+
async *getRemainingResults() {
|
|
4937
|
+
if (this.discarded) {
|
|
4938
|
+
for (const tool of this.tools) {
|
|
4939
|
+
if (tool.status === "yielded") continue;
|
|
4940
|
+
if (tool.status === "completed" && tool.result) {
|
|
4941
|
+
tool.status = "yielded";
|
|
4942
|
+
yield {
|
|
4943
|
+
toolCall: tool.toolCall,
|
|
4944
|
+
parsedArgs: tool.parsedArgs,
|
|
4945
|
+
result: tool.result,
|
|
4946
|
+
permissionDenied: tool.permissionDenied,
|
|
4947
|
+
preventContinuation: tool.preventContinuation,
|
|
4948
|
+
events: tool.events
|
|
4949
|
+
};
|
|
4950
|
+
continue;
|
|
4951
|
+
}
|
|
4952
|
+
tool.status = "yielded";
|
|
4953
|
+
yield {
|
|
4954
|
+
toolCall: tool.toolCall,
|
|
4955
|
+
parsedArgs: tool.parsedArgs,
|
|
4956
|
+
result: { content: "Error: Executor was discarded", isError: true },
|
|
4957
|
+
events: []
|
|
4958
|
+
};
|
|
4959
|
+
}
|
|
4960
|
+
return;
|
|
4961
|
+
}
|
|
4962
|
+
while (this.hasUnfinished()) {
|
|
4963
|
+
if (this.discarded) return;
|
|
4964
|
+
await this.processQueue();
|
|
4965
|
+
for (const result of this.getCompletedResults()) {
|
|
4966
|
+
yield result;
|
|
4967
|
+
}
|
|
4968
|
+
if (this.hasExecuting() && !this.hasCompleted()) {
|
|
4969
|
+
const executingPromises = this.tools.filter((t) => t.status === "executing" && t.promise).map((t) => t.promise);
|
|
4970
|
+
const progressPromise = new Promise((resolve) => {
|
|
4971
|
+
this.progressResolve = resolve;
|
|
4972
|
+
});
|
|
4973
|
+
if (executingPromises.length > 0) {
|
|
4974
|
+
await Promise.race([...executingPromises, progressPromise]);
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
for (const result of this.getCompletedResults()) {
|
|
4979
|
+
yield result;
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
hasUnfinished() {
|
|
4983
|
+
return this.tools.some(
|
|
4984
|
+
(t) => t.status === "queued" || t.status === "executing"
|
|
4985
|
+
);
|
|
4986
|
+
}
|
|
4987
|
+
hasExecuting() {
|
|
4988
|
+
return this.tools.some((t) => t.status === "executing");
|
|
4989
|
+
}
|
|
4990
|
+
hasCompleted() {
|
|
4991
|
+
return this.tools.some(
|
|
4992
|
+
(t) => t.status === "completed" && t.result !== void 0
|
|
4993
|
+
);
|
|
4994
|
+
}
|
|
4995
|
+
};
|
|
4996
|
+
|
|
4997
|
+
// src/retry/backoff.ts
|
|
4998
|
+
var DEFAULT_BASE_DELAY_MS = 500;
|
|
4999
|
+
function getRetryDelay(attempt, retryAfterHeader, maxDelayMs = 32e3, baseDelayMs = DEFAULT_BASE_DELAY_MS) {
|
|
5000
|
+
if (retryAfterHeader) {
|
|
5001
|
+
const seconds = parseInt(retryAfterHeader, 10);
|
|
5002
|
+
if (!isNaN(seconds)) {
|
|
5003
|
+
return seconds * 1e3;
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
const baseDelay = Math.min(
|
|
5007
|
+
baseDelayMs * Math.pow(2, attempt - 1),
|
|
5008
|
+
maxDelayMs
|
|
5009
|
+
);
|
|
5010
|
+
const jitter = Math.random() * 0.25 * baseDelay;
|
|
5011
|
+
return baseDelay + jitter;
|
|
5012
|
+
}
|
|
5013
|
+
function sleep(ms, signal) {
|
|
5014
|
+
return new Promise((resolve, reject) => {
|
|
5015
|
+
if (signal?.aborted) {
|
|
5016
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
5017
|
+
return;
|
|
5018
|
+
}
|
|
5019
|
+
const onAbort = () => {
|
|
5020
|
+
clearTimeout(timer);
|
|
5021
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
5022
|
+
};
|
|
5023
|
+
const timer = setTimeout(() => {
|
|
5024
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5025
|
+
resolve();
|
|
5026
|
+
}, ms);
|
|
5027
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5028
|
+
});
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// src/retry/engine.ts
|
|
5032
|
+
var FLOOR_OUTPUT_TOKENS = 3e3;
|
|
5033
|
+
var CannotRetryError = class extends Error {
|
|
5034
|
+
constructor(originalError, retryContext) {
|
|
5035
|
+
super(originalError instanceof Error ? originalError.message : String(originalError));
|
|
5036
|
+
this.originalError = originalError;
|
|
5037
|
+
this.retryContext = retryContext;
|
|
5038
|
+
this.name = "CannotRetryError";
|
|
5039
|
+
}
|
|
5040
|
+
originalError;
|
|
5041
|
+
retryContext;
|
|
5042
|
+
};
|
|
5043
|
+
async function* withRetry(operation, options) {
|
|
5044
|
+
const maxRetries = options.maxRetries ?? 10;
|
|
5045
|
+
const baseDelayMs = options.baseDelayMs ?? 500;
|
|
5046
|
+
const maxDelayMs = options.maxDelayMs ?? 32e3;
|
|
5047
|
+
const maxConsecutiveOverloaded = options.maxConsecutiveOverloaded ?? 3;
|
|
5048
|
+
const retryContext = {
|
|
5049
|
+
attempt: 0,
|
|
5050
|
+
model: options.model
|
|
5051
|
+
};
|
|
5052
|
+
let consecutiveOverloaded = 0;
|
|
5053
|
+
let fallbackUsed = false;
|
|
5054
|
+
let lastError;
|
|
5055
|
+
let totalAttempts = 0;
|
|
5056
|
+
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
|
|
5057
|
+
if (options.signal?.aborted) {
|
|
5058
|
+
throw new DOMException("Aborted", "AbortError");
|
|
5059
|
+
}
|
|
5060
|
+
totalAttempts++;
|
|
5061
|
+
retryContext.attempt = totalAttempts;
|
|
5062
|
+
try {
|
|
5063
|
+
const stream = operation(retryContext);
|
|
5064
|
+
const iterator = toAsyncIterator(stream);
|
|
5065
|
+
const first = await iterator.next();
|
|
5066
|
+
if (first.done) {
|
|
5067
|
+
throw new ChatStreamError("Provider returned empty stream", {
|
|
5068
|
+
status: 502,
|
|
5069
|
+
cause: new Error("empty_stream")
|
|
5070
|
+
});
|
|
5071
|
+
}
|
|
5072
|
+
return prependChunk(first.value, iterator);
|
|
5073
|
+
} catch (error) {
|
|
5074
|
+
lastError = error;
|
|
5075
|
+
const classified = classifyError(error);
|
|
5076
|
+
if (classified.isContextOverflow && classified.contextOverflowData) {
|
|
5077
|
+
const { inputTokens, contextLimit } = classified.contextOverflowData;
|
|
5078
|
+
const safetyBuffer = 1e3;
|
|
5079
|
+
const available = Math.max(0, contextLimit - inputTokens - safetyBuffer);
|
|
5080
|
+
if (available < FLOOR_OUTPUT_TOKENS) {
|
|
5081
|
+
throw new CannotRetryError(error, retryContext);
|
|
5082
|
+
}
|
|
5083
|
+
const minRequired = (options.thinkingBudget ?? 0) + 1;
|
|
5084
|
+
retryContext.maxTokensOverride = Math.max(
|
|
5085
|
+
FLOOR_OUTPUT_TOKENS,
|
|
5086
|
+
available,
|
|
5087
|
+
minRequired
|
|
5088
|
+
);
|
|
5089
|
+
continue;
|
|
5090
|
+
}
|
|
5091
|
+
if (classified.isOverloaded) {
|
|
5092
|
+
consecutiveOverloaded++;
|
|
5093
|
+
if (consecutiveOverloaded >= maxConsecutiveOverloaded && options.fallbackModel && !fallbackUsed) {
|
|
5094
|
+
const previousModel = retryContext.model;
|
|
5095
|
+
retryContext.model = options.fallbackModel;
|
|
5096
|
+
consecutiveOverloaded = 0;
|
|
5097
|
+
fallbackUsed = true;
|
|
5098
|
+
yield {
|
|
5099
|
+
type: "model_switch",
|
|
5100
|
+
from: previousModel,
|
|
5101
|
+
to: options.fallbackModel
|
|
5102
|
+
};
|
|
5103
|
+
yield {
|
|
5104
|
+
type: "retry_attempt",
|
|
5105
|
+
attempt: totalAttempts,
|
|
5106
|
+
maxRetries,
|
|
5107
|
+
delayMs: 0,
|
|
5108
|
+
error: new Error(
|
|
5109
|
+
`Model fallback: ${previousModel} \u2192 ${options.fallbackModel} after ${maxConsecutiveOverloaded} consecutive overloaded errors`
|
|
5110
|
+
)
|
|
5111
|
+
};
|
|
5112
|
+
attempt = Math.max(attempt - Math.floor(maxRetries / 2), 1);
|
|
5113
|
+
continue;
|
|
5114
|
+
}
|
|
5115
|
+
} else {
|
|
5116
|
+
consecutiveOverloaded = 0;
|
|
5117
|
+
}
|
|
5118
|
+
if (!isRetryable(classified, options)) {
|
|
5119
|
+
throw new CannotRetryError(error, retryContext);
|
|
5120
|
+
}
|
|
5121
|
+
if (totalAttempts > maxRetries) {
|
|
5122
|
+
const exhaustedError = error instanceof Error ? error : new Error(String(error));
|
|
5123
|
+
yield {
|
|
5124
|
+
type: "retry_exhausted",
|
|
5125
|
+
attempts: totalAttempts,
|
|
5126
|
+
error: exhaustedError
|
|
5127
|
+
};
|
|
5128
|
+
throw new CannotRetryError(error, retryContext);
|
|
5129
|
+
}
|
|
5130
|
+
const delayMs = getRetryDelay(
|
|
5131
|
+
attempt,
|
|
5132
|
+
classified.retryAfter,
|
|
5133
|
+
maxDelayMs,
|
|
5134
|
+
baseDelayMs
|
|
5135
|
+
);
|
|
5136
|
+
const retryError = error instanceof Error ? error : new Error(String(error));
|
|
5137
|
+
options.onRetry?.(totalAttempts, retryError, delayMs);
|
|
5138
|
+
yield {
|
|
5139
|
+
type: "retry_attempt",
|
|
5140
|
+
attempt: totalAttempts,
|
|
5141
|
+
maxRetries,
|
|
5142
|
+
delayMs,
|
|
5143
|
+
error: retryError
|
|
5144
|
+
};
|
|
5145
|
+
await sleep(delayMs, options.signal);
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
throw new CannotRetryError(lastError, retryContext);
|
|
5149
|
+
}
|
|
5150
|
+
function toAsyncIterator(iterable) {
|
|
5151
|
+
return iterable[Symbol.asyncIterator]();
|
|
5152
|
+
}
|
|
5153
|
+
async function* prependChunk(first, rest) {
|
|
5154
|
+
yield first;
|
|
5155
|
+
let next = await rest.next();
|
|
5156
|
+
while (!next.done) {
|
|
5157
|
+
yield next.value;
|
|
5158
|
+
next = await rest.next();
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
|
|
5162
|
+
// src/providers/cache-safe-params.ts
|
|
5163
|
+
var cacheSafeParamsMap = /* @__PURE__ */ new Map();
|
|
5164
|
+
function saveCacheSafeParams(params, sessionId = "_default") {
|
|
5165
|
+
if (params) {
|
|
5166
|
+
cacheSafeParamsMap.set(sessionId, params);
|
|
5167
|
+
} else {
|
|
5168
|
+
cacheSafeParamsMap.delete(sessionId);
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5171
|
+
function getLastCacheSafeParams(sessionId = "_default") {
|
|
5172
|
+
return cacheSafeParamsMap.get(sessionId) ?? null;
|
|
5173
|
+
}
|
|
5174
|
+
function createCacheSafeParams(opts) {
|
|
5175
|
+
return {
|
|
5176
|
+
systemPrompt: opts.systemPrompt,
|
|
5177
|
+
model: opts.model,
|
|
5178
|
+
tools: opts.tools,
|
|
5179
|
+
thinking: opts.thinking
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
|
|
5183
|
+
// src/memory/extraction.ts
|
|
5184
|
+
var MEMORY_TYPES = /* @__PURE__ */ new Set([
|
|
5185
|
+
"user",
|
|
5186
|
+
"project",
|
|
5187
|
+
"feedback",
|
|
5188
|
+
"reference"
|
|
5189
|
+
]);
|
|
5190
|
+
function summarizeRecentMessages(messages, maxMessages = 20) {
|
|
5191
|
+
const recent = messages.slice(-maxMessages);
|
|
5192
|
+
const lines = [];
|
|
5193
|
+
for (const msg of recent) {
|
|
5194
|
+
const role = msg.role.toUpperCase();
|
|
5195
|
+
const text = typeof msg.content === "string" ? msg.content : "(non-text content)";
|
|
5196
|
+
const truncated = text.length > 2e3 ? text.slice(0, 2e3) + "\u2026" : text;
|
|
5197
|
+
lines.push(`[${role}] ${truncated}`);
|
|
5198
|
+
}
|
|
5199
|
+
return lines.join("\n\n");
|
|
5200
|
+
}
|
|
5201
|
+
async function extractMemories(llmProvider, model, messages, provider) {
|
|
5202
|
+
const existingIndex = await provider.loadIndex();
|
|
5203
|
+
const summary = summarizeRecentMessages(messages);
|
|
5204
|
+
const prompt = buildExtractionPrompt(summary, existingIndex);
|
|
5205
|
+
const extractionMessages = [
|
|
5206
|
+
{ role: "user", content: prompt }
|
|
5207
|
+
];
|
|
5208
|
+
let responseText = "";
|
|
5209
|
+
for await (const chunk of llmProvider.chat({
|
|
5210
|
+
model,
|
|
5211
|
+
messages: extractionMessages,
|
|
5212
|
+
system: "You are a memory extraction assistant. Respond only with valid JSON.",
|
|
5213
|
+
max_tokens: 4096
|
|
5214
|
+
})) {
|
|
5215
|
+
for (const choice of chunk.choices) {
|
|
5216
|
+
if (choice.delta.content) {
|
|
5217
|
+
responseText += choice.delta.content;
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
const parsed = parseExtractionResponse(responseText);
|
|
5222
|
+
if (!parsed || parsed.memories.length === 0) {
|
|
5223
|
+
return { created: [], updated: [], deleted: [] };
|
|
5224
|
+
}
|
|
5225
|
+
const result = {
|
|
5226
|
+
created: [],
|
|
5227
|
+
updated: [],
|
|
5228
|
+
deleted: []
|
|
5229
|
+
};
|
|
5230
|
+
for (const action of parsed.memories) {
|
|
5231
|
+
if (!MEMORY_TYPES.has(action.type)) continue;
|
|
5232
|
+
if (action.action === "delete" && action.path) {
|
|
5233
|
+
await provider.removeEntry(action.path);
|
|
5234
|
+
result.deleted.push(action.path);
|
|
5235
|
+
} else if (action.action === "update" && action.path) {
|
|
5236
|
+
const entry = {
|
|
5237
|
+
name: action.name,
|
|
5238
|
+
description: action.description,
|
|
5239
|
+
type: action.type,
|
|
5240
|
+
content: action.content,
|
|
5241
|
+
path: action.path,
|
|
5242
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5243
|
+
};
|
|
5244
|
+
await provider.saveEntry(entry);
|
|
5245
|
+
result.updated.push(entry);
|
|
5246
|
+
} else if (action.action === "create") {
|
|
5247
|
+
const entry = {
|
|
5248
|
+
name: action.name,
|
|
5249
|
+
description: action.description,
|
|
5250
|
+
type: action.type,
|
|
5251
|
+
content: action.content,
|
|
5252
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5253
|
+
};
|
|
5254
|
+
await provider.saveEntry(entry);
|
|
5255
|
+
result.created.push(entry);
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
return result;
|
|
5259
|
+
}
|
|
5260
|
+
function parseExtractionResponse(text) {
|
|
5261
|
+
const trimmed = text.trim();
|
|
5262
|
+
let jsonStr = trimmed;
|
|
5263
|
+
if (jsonStr.startsWith("```")) {
|
|
5264
|
+
const firstNewline = jsonStr.indexOf("\n");
|
|
5265
|
+
jsonStr = jsonStr.slice(firstNewline + 1);
|
|
5266
|
+
const lastFence = jsonStr.lastIndexOf("```");
|
|
5267
|
+
if (lastFence !== -1) {
|
|
5268
|
+
jsonStr = jsonStr.slice(0, lastFence);
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
try {
|
|
5272
|
+
const parsed = JSON.parse(jsonStr);
|
|
5273
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.memories)) {
|
|
5274
|
+
return parsed;
|
|
5275
|
+
}
|
|
5276
|
+
return null;
|
|
5277
|
+
} catch {
|
|
5278
|
+
return null;
|
|
5279
|
+
}
|
|
5280
|
+
}
|
|
5281
|
+
|
|
5282
|
+
// src/tools/execution-pipeline.ts
|
|
5283
|
+
async function executeToolCall(tc, parsedArgs, ctx) {
|
|
5284
|
+
const {
|
|
5285
|
+
registry,
|
|
5286
|
+
toolCtx,
|
|
5287
|
+
permCtx,
|
|
5288
|
+
permHandler,
|
|
5289
|
+
denialTracker,
|
|
5290
|
+
hooks,
|
|
5291
|
+
sessionId,
|
|
5292
|
+
tracer,
|
|
5293
|
+
parentSpan,
|
|
5294
|
+
buildPermissionOpts
|
|
5295
|
+
} = ctx;
|
|
5296
|
+
let currentArgs = parsedArgs;
|
|
5297
|
+
const events = [];
|
|
5298
|
+
let preventContinuation = false;
|
|
5299
|
+
try {
|
|
5300
|
+
const toolDef = registry.get(tc.function.name);
|
|
5301
|
+
if (toolDef?.inputSchema) {
|
|
5302
|
+
const parsed = toolDef.inputSchema.safeParse(currentArgs);
|
|
5303
|
+
if (!parsed.success) {
|
|
5304
|
+
const { formatZodValidationError } = await import("./zod-VKURGPRT.js");
|
|
5305
|
+
return {
|
|
5306
|
+
toolCall: tc,
|
|
5307
|
+
parsedArgs: currentArgs,
|
|
5308
|
+
result: {
|
|
5309
|
+
content: formatZodValidationError(tc.function.name, parsed.error),
|
|
5310
|
+
isError: true
|
|
5311
|
+
},
|
|
5312
|
+
events
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
currentArgs = parsed.data;
|
|
5316
|
+
}
|
|
5317
|
+
if (toolDef?.validateInput) {
|
|
5318
|
+
const validationError = await toolDef.validateInput(currentArgs, toolCtx);
|
|
5319
|
+
if (validationError) {
|
|
5320
|
+
return {
|
|
5321
|
+
toolCall: tc,
|
|
5322
|
+
parsedArgs: currentArgs,
|
|
5323
|
+
result: {
|
|
5324
|
+
content: `Validation error for ${tc.function.name}: ${validationError}`,
|
|
5325
|
+
isError: true
|
|
5326
|
+
},
|
|
5327
|
+
events
|
|
5328
|
+
};
|
|
5329
|
+
}
|
|
5330
|
+
}
|
|
5331
|
+
if (hooks.length > 0) {
|
|
5332
|
+
const hookOutput = await runPreToolUseHooks(hooks, {
|
|
5333
|
+
event: "PreToolUse",
|
|
5334
|
+
toolName: tc.function.name,
|
|
5335
|
+
toolInput: currentArgs,
|
|
5336
|
+
toolUseId: tc.id,
|
|
5337
|
+
sessionId
|
|
5338
|
+
});
|
|
5339
|
+
if (hookOutput.decision === "deny") {
|
|
5340
|
+
const msg = hookOutput.message ?? "Blocked by hook.";
|
|
5341
|
+
return {
|
|
5342
|
+
toolCall: tc,
|
|
5343
|
+
parsedArgs: currentArgs,
|
|
5344
|
+
result: { content: `Hook denied: ${msg}`, isError: true },
|
|
5345
|
+
permissionDenied: true,
|
|
5346
|
+
events
|
|
5347
|
+
};
|
|
5348
|
+
}
|
|
5349
|
+
if (hookOutput.updatedInput) {
|
|
5350
|
+
currentArgs = hookOutput.updatedInput;
|
|
5351
|
+
if (permCtx && permCtx.workingDirectories.length > 0) {
|
|
5352
|
+
const hookFilePath = typeof currentArgs.file_path === "string" ? currentArgs.file_path : typeof currentArgs.path === "string" ? currentArgs.path : void 0;
|
|
5353
|
+
if (hookFilePath && !isPathInWorkingDirectories(hookFilePath, permCtx.workingDirectories)) {
|
|
5354
|
+
return {
|
|
5355
|
+
toolCall: tc,
|
|
5356
|
+
parsedArgs: currentArgs,
|
|
5357
|
+
result: {
|
|
5358
|
+
content: `Permission denied: Hook-modified path "${hookFilePath}" is outside working directories.`,
|
|
5359
|
+
isError: true
|
|
5360
|
+
},
|
|
5361
|
+
permissionDenied: true,
|
|
5362
|
+
events
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
if (hookOutput.preventContinuation) {
|
|
5368
|
+
preventContinuation = true;
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
if (permCtx) {
|
|
5372
|
+
const tool = registry.get(tc.function.name);
|
|
5373
|
+
if (tool) {
|
|
5374
|
+
const decision = await resolvePermission(
|
|
5375
|
+
tool,
|
|
5376
|
+
currentArgs,
|
|
5377
|
+
toolCtx,
|
|
5378
|
+
permCtx,
|
|
5379
|
+
buildPermissionOpts()
|
|
5380
|
+
);
|
|
5381
|
+
if (decision.behavior === "deny") {
|
|
5382
|
+
if (decision.reason !== "classifier") {
|
|
5383
|
+
denialTracker?.recordDenial();
|
|
5384
|
+
}
|
|
5385
|
+
events.push({
|
|
5386
|
+
type: "permission_denied",
|
|
5387
|
+
toolName: tc.function.name,
|
|
5388
|
+
input: currentArgs,
|
|
5389
|
+
message: decision.message
|
|
5390
|
+
});
|
|
5391
|
+
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
5392
|
+
event: "PermissionDenied",
|
|
5393
|
+
sessionId,
|
|
5394
|
+
toolName: tc.function.name,
|
|
5395
|
+
input: currentArgs,
|
|
5396
|
+
reason: decision.message
|
|
5397
|
+
});
|
|
5398
|
+
if (denialTracker?.shouldFallback().triggered) {
|
|
5399
|
+
const state = denialTracker.getState();
|
|
5400
|
+
events.push({
|
|
5401
|
+
type: "denial_limit_exceeded",
|
|
5402
|
+
consecutiveDenials: state.consecutiveDenials,
|
|
5403
|
+
totalDenials: state.totalDenials
|
|
5404
|
+
});
|
|
5405
|
+
preventContinuation = true;
|
|
5406
|
+
}
|
|
5407
|
+
return {
|
|
5408
|
+
toolCall: tc,
|
|
5409
|
+
parsedArgs: currentArgs,
|
|
5410
|
+
result: {
|
|
5411
|
+
content: `Permission denied: ${decision.message}`,
|
|
5412
|
+
isError: true
|
|
5413
|
+
},
|
|
5414
|
+
permissionDenied: true,
|
|
5415
|
+
preventContinuation: preventContinuation || void 0,
|
|
5416
|
+
events
|
|
5417
|
+
};
|
|
5418
|
+
}
|
|
5419
|
+
if (decision.behavior === "ask") {
|
|
5420
|
+
await runNotificationHooks(hooks, "PermissionRequest", {
|
|
5421
|
+
event: "PermissionRequest",
|
|
5422
|
+
sessionId,
|
|
5423
|
+
toolName: tc.function.name,
|
|
5424
|
+
input: currentArgs,
|
|
5425
|
+
mode: permCtx.mode ?? "default"
|
|
5426
|
+
});
|
|
5427
|
+
events.push({
|
|
5428
|
+
type: "permission_request",
|
|
5429
|
+
toolName: tc.function.name,
|
|
5430
|
+
input: currentArgs,
|
|
5431
|
+
message: decision.message
|
|
5432
|
+
});
|
|
5433
|
+
if (permHandler) {
|
|
5434
|
+
const signal = toolCtx.signal;
|
|
5435
|
+
if (signal?.aborted) {
|
|
5436
|
+
return {
|
|
5437
|
+
toolCall: tc,
|
|
5438
|
+
parsedArgs,
|
|
5439
|
+
result: { content: "Error: Session aborted", isError: true },
|
|
5440
|
+
permissionDenied: true,
|
|
5441
|
+
events
|
|
5442
|
+
};
|
|
5443
|
+
}
|
|
5444
|
+
const isReadOnly = resolveToolFlag(tool.isReadOnly, currentArgs);
|
|
5445
|
+
const isDestructive = resolveToolFlag(tool.isDestructive, currentArgs);
|
|
5446
|
+
const request = {
|
|
5447
|
+
toolName: tc.function.name,
|
|
5448
|
+
input: currentArgs,
|
|
5449
|
+
message: decision.message,
|
|
5450
|
+
suggestions: decision.suggestions,
|
|
5451
|
+
isReadOnly,
|
|
5452
|
+
isDestructive,
|
|
5453
|
+
signal
|
|
5454
|
+
};
|
|
5455
|
+
const response = await (signal ? Promise.race([
|
|
5456
|
+
permHandler(request),
|
|
5457
|
+
new Promise((_, reject) => {
|
|
5458
|
+
signal.addEventListener("abort", () => {
|
|
5459
|
+
reject(new DOMException("Permission prompt aborted", "AbortError"));
|
|
5460
|
+
}, { once: true });
|
|
5461
|
+
})
|
|
5462
|
+
]) : permHandler(request));
|
|
5463
|
+
if (!response.allow) {
|
|
5464
|
+
denialTracker?.recordDenial();
|
|
5465
|
+
const feedback = response.feedback ?? "User denied permission.";
|
|
5466
|
+
events.push({
|
|
5467
|
+
type: "permission_denied",
|
|
5468
|
+
toolName: tc.function.name,
|
|
5469
|
+
input: currentArgs,
|
|
5470
|
+
message: feedback
|
|
5471
|
+
});
|
|
5472
|
+
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
5473
|
+
event: "PermissionDenied",
|
|
5474
|
+
sessionId,
|
|
5475
|
+
toolName: tc.function.name,
|
|
5476
|
+
input: currentArgs,
|
|
5477
|
+
reason: feedback
|
|
5478
|
+
});
|
|
5479
|
+
if (denialTracker?.shouldFallback().triggered) {
|
|
5480
|
+
const state = denialTracker.getState();
|
|
5481
|
+
events.push({
|
|
5482
|
+
type: "denial_limit_exceeded",
|
|
5483
|
+
consecutiveDenials: state.consecutiveDenials,
|
|
5484
|
+
totalDenials: state.totalDenials
|
|
5485
|
+
});
|
|
5486
|
+
preventContinuation = true;
|
|
5487
|
+
}
|
|
5488
|
+
return {
|
|
5489
|
+
toolCall: tc,
|
|
5490
|
+
parsedArgs: currentArgs,
|
|
5491
|
+
result: {
|
|
5492
|
+
content: `Permission denied: ${feedback}`,
|
|
5493
|
+
isError: true
|
|
5494
|
+
},
|
|
5495
|
+
permissionDenied: true,
|
|
5496
|
+
preventContinuation: preventContinuation || void 0,
|
|
5497
|
+
events
|
|
5498
|
+
};
|
|
5499
|
+
}
|
|
5500
|
+
if (response.updatedInput) {
|
|
5501
|
+
currentArgs = response.updatedInput;
|
|
5502
|
+
}
|
|
5503
|
+
if (response.addRules) {
|
|
5504
|
+
permCtx.rules.push(...response.addRules);
|
|
5505
|
+
}
|
|
5506
|
+
} else {
|
|
5507
|
+
denialTracker?.recordDenial();
|
|
5508
|
+
events.push({
|
|
5509
|
+
type: "permission_denied",
|
|
5510
|
+
toolName: tc.function.name,
|
|
5511
|
+
input: currentArgs,
|
|
5512
|
+
message: "No permission handler configured."
|
|
5513
|
+
});
|
|
5514
|
+
await runNotificationHooks(hooks, "PermissionDenied", {
|
|
5515
|
+
event: "PermissionDenied",
|
|
5516
|
+
sessionId,
|
|
5517
|
+
toolName: tc.function.name,
|
|
5518
|
+
input: currentArgs,
|
|
5519
|
+
reason: "No permission handler configured."
|
|
5520
|
+
});
|
|
5521
|
+
if (denialTracker?.shouldFallback().triggered) {
|
|
5522
|
+
const state = denialTracker.getState();
|
|
5523
|
+
events.push({
|
|
5524
|
+
type: "denial_limit_exceeded",
|
|
5525
|
+
consecutiveDenials: state.consecutiveDenials,
|
|
5526
|
+
totalDenials: state.totalDenials
|
|
5527
|
+
});
|
|
5528
|
+
preventContinuation = true;
|
|
5529
|
+
}
|
|
5530
|
+
return {
|
|
5531
|
+
toolCall: tc,
|
|
5532
|
+
parsedArgs: currentArgs,
|
|
5533
|
+
result: {
|
|
5534
|
+
content: "Permission denied: No permission handler configured.",
|
|
5535
|
+
isError: true
|
|
5536
|
+
},
|
|
5537
|
+
permissionDenied: true,
|
|
5538
|
+
preventContinuation: preventContinuation || void 0,
|
|
5539
|
+
events
|
|
5540
|
+
};
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
5543
|
+
denialTracker?.recordSuccess();
|
|
5544
|
+
if (decision.behavior === "allow" && decision.updatedInput) {
|
|
5545
|
+
currentArgs = decision.updatedInput;
|
|
5546
|
+
}
|
|
5547
|
+
events.push({
|
|
5548
|
+
type: "permission_granted",
|
|
5549
|
+
toolName: tc.function.name,
|
|
5550
|
+
input: currentArgs
|
|
5551
|
+
});
|
|
5552
|
+
}
|
|
5732
5553
|
}
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5554
|
+
const toolSpan = tracer.startSpan("noumen.tool.execute", {
|
|
5555
|
+
parent: parentSpan,
|
|
5556
|
+
attributes: { "tool.name": tc.function.name, "tool.id": tc.id }
|
|
5557
|
+
});
|
|
5558
|
+
let result = await registry.execute(tc.function.name, currentArgs, toolCtx);
|
|
5559
|
+
let resultText = contentToString(result.content);
|
|
5560
|
+
if (ctx.maxResultChars && resultText.length > ctx.maxResultChars) {
|
|
5561
|
+
const truncated = resultText.slice(0, ctx.maxResultChars);
|
|
5562
|
+
const omitted = resultText.length - ctx.maxResultChars;
|
|
5563
|
+
resultText = truncated + `
|
|
5564
|
+
|
|
5565
|
+
[Result truncated: ${omitted} chars omitted]`;
|
|
5566
|
+
result = { ...result, content: resultText };
|
|
5743
5567
|
}
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5568
|
+
toolSpan.setStatus(
|
|
5569
|
+
result.isError ? SpanStatusCode.ERROR : SpanStatusCode.OK,
|
|
5570
|
+
result.isError ? resultText : void 0
|
|
5571
|
+
);
|
|
5572
|
+
toolSpan.end();
|
|
5573
|
+
if (hooks.length > 0) {
|
|
5574
|
+
const postOutput = await runPostToolUseHooks(hooks, {
|
|
5575
|
+
event: "PostToolUse",
|
|
5576
|
+
toolName: tc.function.name,
|
|
5577
|
+
toolInput: currentArgs,
|
|
5578
|
+
toolUseId: tc.id,
|
|
5579
|
+
toolOutput: resultText,
|
|
5580
|
+
isError: result.isError ?? false,
|
|
5581
|
+
sessionId
|
|
5582
|
+
});
|
|
5583
|
+
if (postOutput.updatedOutput !== void 0) {
|
|
5584
|
+
result = { ...result, content: postOutput.updatedOutput };
|
|
5585
|
+
}
|
|
5586
|
+
if (postOutput.preventContinuation) {
|
|
5587
|
+
preventContinuation = true;
|
|
5588
|
+
}
|
|
5589
|
+
if (result.isError) {
|
|
5590
|
+
const failOutput = await runPostToolUseFailureHooks(hooks, {
|
|
5591
|
+
event: "PostToolUseFailure",
|
|
5592
|
+
toolName: tc.function.name,
|
|
5593
|
+
toolInput: currentArgs,
|
|
5594
|
+
toolUseId: tc.id,
|
|
5595
|
+
toolOutput: contentToString(result.content),
|
|
5596
|
+
errorMessage: contentToString(result.content),
|
|
5597
|
+
sessionId
|
|
5598
|
+
});
|
|
5599
|
+
if (failOutput.updatedOutput !== void 0) {
|
|
5600
|
+
result = { ...result, content: failOutput.updatedOutput };
|
|
5601
|
+
}
|
|
5602
|
+
if (failOutput.preventContinuation) {
|
|
5603
|
+
preventContinuation = true;
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5753
5606
|
}
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5607
|
+
return {
|
|
5608
|
+
toolCall: tc,
|
|
5609
|
+
parsedArgs: currentArgs,
|
|
5610
|
+
result,
|
|
5611
|
+
preventContinuation: preventContinuation || void 0,
|
|
5612
|
+
events
|
|
5613
|
+
};
|
|
5614
|
+
} catch (execErr) {
|
|
5615
|
+
const msg = execErr instanceof Error ? execErr.message : String(execErr);
|
|
5616
|
+
const errorResult = { content: `Error executing tool: ${msg}`, isError: true };
|
|
5617
|
+
if (hooks.length > 0) {
|
|
5758
5618
|
try {
|
|
5759
|
-
const
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
if (
|
|
5769
|
-
|
|
5770
|
-
this.siblingAbortController.abort("sibling_error");
|
|
5619
|
+
const failOutput = await runPostToolUseFailureHooks(hooks, {
|
|
5620
|
+
event: "PostToolUseFailure",
|
|
5621
|
+
toolName: tc.function.name,
|
|
5622
|
+
toolInput: currentArgs,
|
|
5623
|
+
toolUseId: tc.id,
|
|
5624
|
+
toolOutput: errorResult.content,
|
|
5625
|
+
errorMessage: msg,
|
|
5626
|
+
sessionId
|
|
5627
|
+
});
|
|
5628
|
+
if (failOutput.updatedOutput !== void 0) {
|
|
5629
|
+
errorResult.content = failOutput.updatedOutput;
|
|
5771
5630
|
}
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
content: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
5775
|
-
isError: true
|
|
5776
|
-
};
|
|
5777
|
-
tracked.events = [];
|
|
5778
|
-
if (tracked.toolCall.function.name === BASH_TOOL_NAME) {
|
|
5779
|
-
this.hasErrored = true;
|
|
5780
|
-
this.siblingAbortController.abort("sibling_error");
|
|
5631
|
+
if (failOutput.preventContinuation) {
|
|
5632
|
+
preventContinuation = true;
|
|
5781
5633
|
}
|
|
5634
|
+
} catch {
|
|
5782
5635
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5636
|
+
}
|
|
5637
|
+
return {
|
|
5638
|
+
toolCall: tc,
|
|
5639
|
+
parsedArgs: currentArgs,
|
|
5640
|
+
result: errorResult,
|
|
5641
|
+
preventContinuation: preventContinuation || void 0,
|
|
5642
|
+
events
|
|
5643
|
+
};
|
|
5787
5644
|
}
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5645
|
+
}
|
|
5646
|
+
|
|
5647
|
+
// src/pipeline/prepare-messages.ts
|
|
5648
|
+
async function prepareMessagesForApi(messages, config, state) {
|
|
5649
|
+
const events = [];
|
|
5650
|
+
let canonical = messages;
|
|
5651
|
+
let contentReplacementState = state.contentReplacementState;
|
|
5652
|
+
let budgetState = state.budgetState;
|
|
5653
|
+
let microcompactTokensFreed = state.microcompactTokensFreed;
|
|
5654
|
+
if (config.toolResultStorage?.enabled && config.fs) {
|
|
5655
|
+
const storageResult = await enforceToolResultStorageBudget(
|
|
5656
|
+
canonical,
|
|
5657
|
+
config.toolResultStorage,
|
|
5658
|
+
config.fs,
|
|
5659
|
+
config.sessionId,
|
|
5660
|
+
contentReplacementState
|
|
5661
|
+
);
|
|
5662
|
+
if (storageResult.tokensFreed > 0) {
|
|
5663
|
+
canonical = storageResult.messages;
|
|
5664
|
+
contentReplacementState = storageResult.state;
|
|
5665
|
+
microcompactTokensFreed += storageResult.tokensFreed;
|
|
5805
5666
|
}
|
|
5806
5667
|
}
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
yield {
|
|
5814
|
-
toolCall: tool.toolCall,
|
|
5815
|
-
parsedArgs: tool.parsedArgs,
|
|
5816
|
-
result: tool.result,
|
|
5817
|
-
permissionDenied: tool.permissionDenied,
|
|
5818
|
-
preventContinuation: tool.preventContinuation,
|
|
5819
|
-
events: tool.events
|
|
5820
|
-
};
|
|
5821
|
-
continue;
|
|
5822
|
-
}
|
|
5823
|
-
tool.status = "yielded";
|
|
5824
|
-
yield {
|
|
5825
|
-
toolCall: tool.toolCall,
|
|
5826
|
-
parsedArgs: tool.parsedArgs,
|
|
5827
|
-
result: { content: "Error: Executor was discarded", isError: true },
|
|
5828
|
-
events: []
|
|
5829
|
-
};
|
|
5830
|
-
}
|
|
5831
|
-
return;
|
|
5668
|
+
if (config.microcompact?.enabled) {
|
|
5669
|
+
const mcResult = microcompactMessages(canonical, config.microcompact);
|
|
5670
|
+
if (mcResult.tokensFreed > 0) {
|
|
5671
|
+
canonical = mcResult.messages;
|
|
5672
|
+
microcompactTokensFreed += mcResult.tokensFreed;
|
|
5673
|
+
events.push({ type: "microcompact_complete", tokensFreed: mcResult.tokensFreed });
|
|
5832
5674
|
}
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5675
|
+
}
|
|
5676
|
+
let messagesForApi = canonical;
|
|
5677
|
+
if (config.toolResultBudget?.enabled) {
|
|
5678
|
+
const budgetResult = enforceToolResultBudget(
|
|
5679
|
+
[...canonical],
|
|
5680
|
+
config.toolResultBudget,
|
|
5681
|
+
budgetState
|
|
5682
|
+
);
|
|
5683
|
+
messagesForApi = budgetResult.messages;
|
|
5684
|
+
budgetState = budgetResult.state;
|
|
5685
|
+
microcompactTokensFreed += budgetResult.tokensFreed;
|
|
5686
|
+
for (const entry of budgetResult.truncatedEntries) {
|
|
5687
|
+
events.push({
|
|
5688
|
+
type: "tool_result_truncated",
|
|
5689
|
+
toolCallId: entry.toolCallId,
|
|
5690
|
+
originalChars: entry.originalChars,
|
|
5691
|
+
truncatedChars: entry.truncatedChars
|
|
5692
|
+
});
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
messagesForApi = normalizeMessagesForAPI(messagesForApi);
|
|
5696
|
+
if (config.debug) {
|
|
5697
|
+
assertValidMessageSequence(messagesForApi);
|
|
5698
|
+
}
|
|
5699
|
+
return {
|
|
5700
|
+
messagesForApi,
|
|
5701
|
+
canonicalMessages: canonical,
|
|
5702
|
+
state: {
|
|
5703
|
+
contentReplacementState,
|
|
5704
|
+
budgetState,
|
|
5705
|
+
microcompactTokensFreed
|
|
5706
|
+
},
|
|
5707
|
+
events
|
|
5708
|
+
};
|
|
5709
|
+
}
|
|
5710
|
+
|
|
5711
|
+
// src/pipeline/auto-compact-step.ts
|
|
5712
|
+
async function tryAutoCompactStep(messages, config, provider, model, state, hooks, sessionId, storage) {
|
|
5713
|
+
if (!canAutoCompact(state.autoCompactTracking) || !shouldAutoCompact(
|
|
5714
|
+
messages,
|
|
5715
|
+
config,
|
|
5716
|
+
state.lastUsage,
|
|
5717
|
+
state.anchorMessageIndex,
|
|
5718
|
+
state.microcompactTokensFreed,
|
|
5719
|
+
state.querySource
|
|
5720
|
+
)) {
|
|
5721
|
+
return { compacted: false, events: [] };
|
|
5722
|
+
}
|
|
5723
|
+
const events = [];
|
|
5724
|
+
await runNotificationHooks(hooks, "PreCompact", {
|
|
5725
|
+
event: "PreCompact",
|
|
5726
|
+
sessionId
|
|
5727
|
+
});
|
|
5728
|
+
events.push({ type: "compact_start" });
|
|
5729
|
+
try {
|
|
5730
|
+
const compactedMessages = await compactConversation(
|
|
5731
|
+
provider,
|
|
5732
|
+
model,
|
|
5733
|
+
messages,
|
|
5734
|
+
storage,
|
|
5735
|
+
sessionId,
|
|
5736
|
+
{
|
|
5737
|
+
tailMessagesToKeep: config.tailMessagesToKeep,
|
|
5738
|
+
stripBinaryContent: true,
|
|
5739
|
+
signal: state.signal,
|
|
5740
|
+
recentlyReadFiles: state.recentlyReadFiles && state.recentlyReadFiles.size > 0 ? state.recentlyReadFiles : void 0
|
|
5838
5741
|
}
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5742
|
+
);
|
|
5743
|
+
recordAutoCompactSuccess(state.autoCompactTracking);
|
|
5744
|
+
events.push({ type: "compact_complete" });
|
|
5745
|
+
await runNotificationHooks(hooks, "PostCompact", {
|
|
5746
|
+
event: "PostCompact",
|
|
5747
|
+
sessionId
|
|
5748
|
+
});
|
|
5749
|
+
return { compacted: true, messages: compactedMessages, events };
|
|
5750
|
+
} catch (compactErr) {
|
|
5751
|
+
recordAutoCompactFailure(state.autoCompactTracking);
|
|
5752
|
+
const error = compactErr instanceof Error ? compactErr : new Error(`Compaction failed: ${String(compactErr)}`);
|
|
5753
|
+
await runNotificationHooks(hooks, "Error", {
|
|
5754
|
+
event: "Error",
|
|
5755
|
+
sessionId,
|
|
5756
|
+
error
|
|
5757
|
+
});
|
|
5758
|
+
events.push({ type: "auto_compact_failed", error });
|
|
5759
|
+
return { compacted: false, events };
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
// src/pipeline/execute-tools-step.ts
|
|
5764
|
+
var FILE_TOOLS = /* @__PURE__ */ new Set(["ReadFile", "WriteFile", "EditFile"]);
|
|
5765
|
+
async function processToolResult(execResult, spillFn, messages, recentlyReadFiles, storage, sessionId) {
|
|
5766
|
+
const events = [];
|
|
5767
|
+
for (const evt of execResult.events ?? []) {
|
|
5768
|
+
events.push(evt);
|
|
5769
|
+
}
|
|
5770
|
+
if (!execResult.permissionDenied) {
|
|
5771
|
+
events.push({
|
|
5772
|
+
type: "tool_result",
|
|
5773
|
+
toolUseId: execResult.toolCall.id,
|
|
5774
|
+
toolName: execResult.toolCall.function.name,
|
|
5775
|
+
result: execResult.result
|
|
5776
|
+
});
|
|
5777
|
+
const gitOps = execResult.result.metadata?.gitOperations;
|
|
5778
|
+
if (gitOps) {
|
|
5779
|
+
for (const op of gitOps) {
|
|
5780
|
+
events.push({
|
|
5781
|
+
type: "git_operation",
|
|
5782
|
+
operation: op.type,
|
|
5783
|
+
details: op.details
|
|
5843
5784
|
});
|
|
5844
|
-
if (executingPromises.length > 0) {
|
|
5845
|
-
await Promise.race([...executingPromises, progressPromise]);
|
|
5846
|
-
}
|
|
5847
5785
|
}
|
|
5848
5786
|
}
|
|
5849
|
-
for (const result of this.getCompletedResults()) {
|
|
5850
|
-
yield result;
|
|
5851
|
-
}
|
|
5852
|
-
}
|
|
5853
|
-
hasUnfinished() {
|
|
5854
|
-
return this.tools.some(
|
|
5855
|
-
(t) => t.status === "queued" || t.status === "executing"
|
|
5856
|
-
);
|
|
5857
|
-
}
|
|
5858
|
-
hasExecuting() {
|
|
5859
|
-
return this.tools.some((t) => t.status === "executing");
|
|
5860
5787
|
}
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5788
|
+
let resultContent = execResult.result.content;
|
|
5789
|
+
let spillRecord;
|
|
5790
|
+
if (typeof resultContent === "string") {
|
|
5791
|
+
const spill = await spillFn(
|
|
5792
|
+
execResult.toolCall.id,
|
|
5793
|
+
execResult.toolCall.function.name,
|
|
5794
|
+
resultContent
|
|
5864
5795
|
);
|
|
5796
|
+
if (spill.spilled) {
|
|
5797
|
+
resultContent = spill.content;
|
|
5798
|
+
spillRecord = { toolUseId: execResult.toolCall.id, replacement: spill.content };
|
|
5799
|
+
}
|
|
5865
5800
|
}
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5801
|
+
const toolResultMsg = {
|
|
5802
|
+
role: "tool",
|
|
5803
|
+
tool_call_id: execResult.toolCall.id,
|
|
5804
|
+
content: resultContent,
|
|
5805
|
+
...execResult.result.isError ? { isError: true } : {}
|
|
5806
|
+
};
|
|
5807
|
+
messages.push(toolResultMsg);
|
|
5808
|
+
await storage.appendMessage(sessionId, toolResultMsg);
|
|
5809
|
+
let touchedPath;
|
|
5810
|
+
if (FILE_TOOLS.has(execResult.toolCall.function.name) && typeof execResult.parsedArgs.file_path === "string") {
|
|
5811
|
+
touchedPath = execResult.parsedArgs.file_path;
|
|
5812
|
+
if (execResult.toolCall.function.name === "ReadFile" && !execResult.result.isError) {
|
|
5813
|
+
const content = typeof execResult.result.content === "string" ? execResult.result.content : "";
|
|
5814
|
+
recentlyReadFiles.set(execResult.parsedArgs.file_path, content);
|
|
5875
5815
|
}
|
|
5876
5816
|
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5817
|
+
return {
|
|
5818
|
+
events,
|
|
5819
|
+
touchedPath,
|
|
5820
|
+
spillRecord,
|
|
5821
|
+
preventContinuation: !!execResult.preventContinuation
|
|
5822
|
+
};
|
|
5883
5823
|
}
|
|
5884
|
-
function
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5824
|
+
async function executeToolsStep(toolCalls, streamingExec, streamingResults, execCtx, registry, sessionId, messages, recentlyReadFiles, storage, spillFn) {
|
|
5825
|
+
const allEvents = [];
|
|
5826
|
+
const touchedFilePaths = [];
|
|
5827
|
+
const spilledRecords = [];
|
|
5828
|
+
let preventContinuation = false;
|
|
5829
|
+
const handleResult = async (execResult) => {
|
|
5830
|
+
const processed = await processToolResult(
|
|
5831
|
+
execResult,
|
|
5832
|
+
spillFn,
|
|
5833
|
+
messages,
|
|
5834
|
+
recentlyReadFiles,
|
|
5835
|
+
storage,
|
|
5836
|
+
sessionId
|
|
5837
|
+
);
|
|
5838
|
+
allEvents.push(...processed.events);
|
|
5839
|
+
if (processed.touchedPath) touchedFilePaths.push(processed.touchedPath);
|
|
5840
|
+
if (processed.spillRecord) spilledRecords.push(processed.spillRecord);
|
|
5841
|
+
if (processed.preventContinuation) preventContinuation = true;
|
|
5842
|
+
};
|
|
5843
|
+
if (streamingExec) {
|
|
5844
|
+
const allResults = [...streamingResults];
|
|
5845
|
+
for await (const result of streamingExec.getRemainingResults()) {
|
|
5846
|
+
allResults.push(result);
|
|
5889
5847
|
}
|
|
5890
|
-
const
|
|
5891
|
-
|
|
5892
|
-
|
|
5848
|
+
for (const execResult of allResults) {
|
|
5849
|
+
await handleResult(execResult);
|
|
5850
|
+
}
|
|
5851
|
+
} else {
|
|
5852
|
+
const executor = async (tc, parsedArgs) => {
|
|
5853
|
+
const pipelineResult = await executeToolCall(tc, parsedArgs, execCtx);
|
|
5854
|
+
if (pipelineResult.preventContinuation) preventContinuation = true;
|
|
5855
|
+
return pipelineResult;
|
|
5893
5856
|
};
|
|
5894
|
-
const
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5857
|
+
for await (const execResult of runToolsBatched(
|
|
5858
|
+
toolCalls,
|
|
5859
|
+
(name) => registry.get(name),
|
|
5860
|
+
executor
|
|
5861
|
+
)) {
|
|
5862
|
+
await handleResult(execResult);
|
|
5863
|
+
}
|
|
5864
|
+
}
|
|
5865
|
+
return { events: allEvents, touchedFilePaths, preventContinuation, spilledRecords };
|
|
5900
5866
|
}
|
|
5901
5867
|
|
|
5902
|
-
// src/
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
const
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
const first = await iterator.next();
|
|
5937
|
-
if (first.done) {
|
|
5938
|
-
throw new ChatStreamError("Provider returned empty stream", {
|
|
5939
|
-
status: 502,
|
|
5940
|
-
cause: new Error("empty_stream")
|
|
5941
|
-
});
|
|
5868
|
+
// src/pipeline/initialize-session.ts
|
|
5869
|
+
async function initializeSession(params) {
|
|
5870
|
+
const {
|
|
5871
|
+
storage,
|
|
5872
|
+
sessionId,
|
|
5873
|
+
hooks,
|
|
5874
|
+
prompt,
|
|
5875
|
+
isResumeRun,
|
|
5876
|
+
checkpointManager,
|
|
5877
|
+
costTracker,
|
|
5878
|
+
toolResultStorage,
|
|
5879
|
+
fs
|
|
5880
|
+
} = params;
|
|
5881
|
+
let { messages, contentReplacementState, loaded, resumeRequested } = params;
|
|
5882
|
+
const events = [];
|
|
5883
|
+
if (!loaded) {
|
|
5884
|
+
if (resumeRequested) {
|
|
5885
|
+
const payload = await restoreSession(storage, sessionId);
|
|
5886
|
+
messages = payload.messages;
|
|
5887
|
+
if (checkpointManager && payload.checkpointSnapshots.length > 0) {
|
|
5888
|
+
checkpointManager.restoreStateFromEntries(payload.checkpointSnapshots);
|
|
5889
|
+
}
|
|
5890
|
+
if (costTracker && payload.costState) {
|
|
5891
|
+
costTracker.restore(payload.costState);
|
|
5892
|
+
}
|
|
5893
|
+
if (payload.contentReplacements.length > 0) {
|
|
5894
|
+
contentReplacementState = reconstructContentReplacementState(
|
|
5895
|
+
payload.contentReplacements,
|
|
5896
|
+
messages
|
|
5897
|
+
);
|
|
5898
|
+
messages = applyPersistedReplacements(
|
|
5899
|
+
messages,
|
|
5900
|
+
contentReplacementState
|
|
5901
|
+
);
|
|
5942
5902
|
}
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
const available = Math.max(0, contextLimit - inputTokens - safetyBuffer);
|
|
5951
|
-
if (available < FLOOR_OUTPUT_TOKENS) {
|
|
5952
|
-
throw new CannotRetryError(error, retryContext);
|
|
5953
|
-
}
|
|
5954
|
-
const minRequired = (options.thinkingBudget ?? 0) + 1;
|
|
5955
|
-
retryContext.maxTokensOverride = Math.max(
|
|
5956
|
-
FLOOR_OUTPUT_TOKENS,
|
|
5957
|
-
available,
|
|
5958
|
-
minRequired
|
|
5903
|
+
if (toolResultStorage?.enabled && fs) {
|
|
5904
|
+
const storageResult = await enforceToolResultStorageBudget(
|
|
5905
|
+
messages,
|
|
5906
|
+
toolResultStorage,
|
|
5907
|
+
fs,
|
|
5908
|
+
sessionId,
|
|
5909
|
+
contentReplacementState
|
|
5959
5910
|
);
|
|
5960
|
-
|
|
5911
|
+
messages = storageResult.messages;
|
|
5912
|
+
contentReplacementState = storageResult.state;
|
|
5961
5913
|
}
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
const previousModel = retryContext.model;
|
|
5966
|
-
retryContext.model = options.fallbackModel;
|
|
5967
|
-
consecutiveOverloaded = 0;
|
|
5968
|
-
fallbackUsed = true;
|
|
5969
|
-
yield {
|
|
5970
|
-
type: "model_switch",
|
|
5971
|
-
from: previousModel,
|
|
5972
|
-
to: options.fallbackModel
|
|
5973
|
-
};
|
|
5974
|
-
yield {
|
|
5975
|
-
type: "retry_attempt",
|
|
5976
|
-
attempt: totalAttempts,
|
|
5977
|
-
maxRetries,
|
|
5978
|
-
delayMs: 0,
|
|
5979
|
-
error: new Error(
|
|
5980
|
-
`Model fallback: ${previousModel} \u2192 ${options.fallbackModel} after ${maxConsecutiveOverloaded} consecutive overloaded errors`
|
|
5981
|
-
)
|
|
5982
|
-
};
|
|
5983
|
-
attempt = Math.max(attempt - Math.floor(maxRetries / 2), 1);
|
|
5984
|
-
continue;
|
|
5914
|
+
for (const [filterName, count] of Object.entries(payload.recoveryRemovals)) {
|
|
5915
|
+
if (count > 0) {
|
|
5916
|
+
events.push({ type: "recovery_filtered", filterName, removedCount: count });
|
|
5985
5917
|
}
|
|
5986
|
-
} else {
|
|
5987
|
-
consecutiveOverloaded = 0;
|
|
5988
|
-
}
|
|
5989
|
-
if (!isRetryable(classified, options)) {
|
|
5990
|
-
throw new CannotRetryError(error, retryContext);
|
|
5991
5918
|
}
|
|
5992
|
-
if (
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
error: exhaustedError
|
|
5998
|
-
};
|
|
5999
|
-
throw new CannotRetryError(error, retryContext);
|
|
5919
|
+
if (payload.interruption.kind !== "none") {
|
|
5920
|
+
events.push({
|
|
5921
|
+
type: "interrupted_turn_detected",
|
|
5922
|
+
kind: payload.interruption.kind
|
|
5923
|
+
});
|
|
6000
5924
|
}
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
baseDelayMs
|
|
6006
|
-
);
|
|
6007
|
-
const retryError = error instanceof Error ? error : new Error(String(error));
|
|
6008
|
-
options.onRetry?.(totalAttempts, retryError, delayMs);
|
|
6009
|
-
yield {
|
|
6010
|
-
type: "retry_attempt",
|
|
6011
|
-
attempt: totalAttempts,
|
|
6012
|
-
maxRetries,
|
|
6013
|
-
delayMs,
|
|
6014
|
-
error: retryError
|
|
6015
|
-
};
|
|
6016
|
-
await sleep(delayMs, options.signal);
|
|
5925
|
+
resumeRequested = false;
|
|
5926
|
+
events.push({ type: "session_resumed", sessionId, messageCount: messages.length });
|
|
5927
|
+
} else {
|
|
5928
|
+
messages = await storage.loadMessages(sessionId);
|
|
6017
5929
|
}
|
|
5930
|
+
loaded = true;
|
|
6018
5931
|
}
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
5932
|
+
const userMessage = { role: "user", content: prompt };
|
|
5933
|
+
messages.push(userMessage);
|
|
5934
|
+
await storage.appendMessage(sessionId, userMessage);
|
|
5935
|
+
const turnMessageId = generateUUID();
|
|
5936
|
+
if (checkpointManager) {
|
|
5937
|
+
await checkpointManager.makeSnapshot(turnMessageId, sessionId);
|
|
5938
|
+
await storage.appendCheckpointEntry(
|
|
5939
|
+
sessionId,
|
|
5940
|
+
turnMessageId,
|
|
5941
|
+
checkpointManager.getState().snapshots.at(-1),
|
|
5942
|
+
false
|
|
5943
|
+
);
|
|
5944
|
+
events.push({ type: "checkpoint_snapshot", messageId: turnMessageId });
|
|
6030
5945
|
}
|
|
5946
|
+
await runNotificationHooks(hooks, "SessionStart", {
|
|
5947
|
+
event: "SessionStart",
|
|
5948
|
+
sessionId,
|
|
5949
|
+
prompt,
|
|
5950
|
+
isResume: isResumeRun
|
|
5951
|
+
});
|
|
5952
|
+
return {
|
|
5953
|
+
messages,
|
|
5954
|
+
contentReplacementState,
|
|
5955
|
+
events,
|
|
5956
|
+
loaded,
|
|
5957
|
+
resumeRequested,
|
|
5958
|
+
turnMessageId
|
|
5959
|
+
};
|
|
6031
5960
|
}
|
|
6032
5961
|
|
|
6033
5962
|
// src/pipeline/consume-stream.ts
|
|
@@ -6410,27 +6339,6 @@ async function tryReactiveCompactRecovery(input) {
|
|
|
6410
6339
|
return { recovered: false, events };
|
|
6411
6340
|
}
|
|
6412
6341
|
|
|
6413
|
-
// src/providers/cache-safe-params.ts
|
|
6414
|
-
var cacheSafeParamsMap = /* @__PURE__ */ new Map();
|
|
6415
|
-
function saveCacheSafeParams(params, sessionId = "_default") {
|
|
6416
|
-
if (params) {
|
|
6417
|
-
cacheSafeParamsMap.set(sessionId, params);
|
|
6418
|
-
} else {
|
|
6419
|
-
cacheSafeParamsMap.delete(sessionId);
|
|
6420
|
-
}
|
|
6421
|
-
}
|
|
6422
|
-
function getLastCacheSafeParams(sessionId = "_default") {
|
|
6423
|
-
return cacheSafeParamsMap.get(sessionId) ?? null;
|
|
6424
|
-
}
|
|
6425
|
-
function createCacheSafeParams(opts) {
|
|
6426
|
-
return {
|
|
6427
|
-
systemPrompt: opts.systemPrompt,
|
|
6428
|
-
model: opts.model,
|
|
6429
|
-
tools: opts.tools,
|
|
6430
|
-
thinking: opts.thinking
|
|
6431
|
-
};
|
|
6432
|
-
}
|
|
6433
|
-
|
|
6434
6342
|
// src/pipeline/provider-round.ts
|
|
6435
6343
|
var MAX_CONSECUTIVE_MALFORMED = 5;
|
|
6436
6344
|
async function* executeProviderRound(p) {
|
|
@@ -6992,112 +6900,13 @@ async function postToolStep(params) {
|
|
|
6992
6900
|
await runNotificationHooks(hooks, "TurnEnd", { event: "TurnEnd", sessionId });
|
|
6993
6901
|
return {
|
|
6994
6902
|
events,
|
|
6995
|
-
preventContinuation,
|
|
6996
|
-
shouldBreak: false,
|
|
6997
|
-
shouldContinue: true,
|
|
6998
|
-
hasAttemptedReactiveCompactReset: true,
|
|
6999
|
-
systemPrompt,
|
|
7000
|
-
toolDefs
|
|
7001
|
-
};
|
|
7002
|
-
}
|
|
7003
|
-
|
|
7004
|
-
// src/memory/extraction.ts
|
|
7005
|
-
var MEMORY_TYPES = /* @__PURE__ */ new Set([
|
|
7006
|
-
"user",
|
|
7007
|
-
"project",
|
|
7008
|
-
"feedback",
|
|
7009
|
-
"reference"
|
|
7010
|
-
]);
|
|
7011
|
-
function summarizeRecentMessages(messages, maxMessages = 20) {
|
|
7012
|
-
const recent = messages.slice(-maxMessages);
|
|
7013
|
-
const lines = [];
|
|
7014
|
-
for (const msg of recent) {
|
|
7015
|
-
const role = msg.role.toUpperCase();
|
|
7016
|
-
const text = typeof msg.content === "string" ? msg.content : "(non-text content)";
|
|
7017
|
-
const truncated = text.length > 2e3 ? text.slice(0, 2e3) + "\u2026" : text;
|
|
7018
|
-
lines.push(`[${role}] ${truncated}`);
|
|
7019
|
-
}
|
|
7020
|
-
return lines.join("\n\n");
|
|
7021
|
-
}
|
|
7022
|
-
async function extractMemories(llmProvider, model, messages, provider) {
|
|
7023
|
-
const existingIndex = await provider.loadIndex();
|
|
7024
|
-
const summary = summarizeRecentMessages(messages);
|
|
7025
|
-
const prompt = buildExtractionPrompt(summary, existingIndex);
|
|
7026
|
-
const extractionMessages = [
|
|
7027
|
-
{ role: "user", content: prompt }
|
|
7028
|
-
];
|
|
7029
|
-
let responseText = "";
|
|
7030
|
-
for await (const chunk of llmProvider.chat({
|
|
7031
|
-
model,
|
|
7032
|
-
messages: extractionMessages,
|
|
7033
|
-
system: "You are a memory extraction assistant. Respond only with valid JSON.",
|
|
7034
|
-
max_tokens: 4096
|
|
7035
|
-
})) {
|
|
7036
|
-
for (const choice of chunk.choices) {
|
|
7037
|
-
if (choice.delta.content) {
|
|
7038
|
-
responseText += choice.delta.content;
|
|
7039
|
-
}
|
|
7040
|
-
}
|
|
7041
|
-
}
|
|
7042
|
-
const parsed = parseExtractionResponse(responseText);
|
|
7043
|
-
if (!parsed || parsed.memories.length === 0) {
|
|
7044
|
-
return { created: [], updated: [], deleted: [] };
|
|
7045
|
-
}
|
|
7046
|
-
const result = {
|
|
7047
|
-
created: [],
|
|
7048
|
-
updated: [],
|
|
7049
|
-
deleted: []
|
|
7050
|
-
};
|
|
7051
|
-
for (const action of parsed.memories) {
|
|
7052
|
-
if (!MEMORY_TYPES.has(action.type)) continue;
|
|
7053
|
-
if (action.action === "delete" && action.path) {
|
|
7054
|
-
await provider.removeEntry(action.path);
|
|
7055
|
-
result.deleted.push(action.path);
|
|
7056
|
-
} else if (action.action === "update" && action.path) {
|
|
7057
|
-
const entry = {
|
|
7058
|
-
name: action.name,
|
|
7059
|
-
description: action.description,
|
|
7060
|
-
type: action.type,
|
|
7061
|
-
content: action.content,
|
|
7062
|
-
path: action.path,
|
|
7063
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7064
|
-
};
|
|
7065
|
-
await provider.saveEntry(entry);
|
|
7066
|
-
result.updated.push(entry);
|
|
7067
|
-
} else if (action.action === "create") {
|
|
7068
|
-
const entry = {
|
|
7069
|
-
name: action.name,
|
|
7070
|
-
description: action.description,
|
|
7071
|
-
type: action.type,
|
|
7072
|
-
content: action.content,
|
|
7073
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7074
|
-
};
|
|
7075
|
-
await provider.saveEntry(entry);
|
|
7076
|
-
result.created.push(entry);
|
|
7077
|
-
}
|
|
7078
|
-
}
|
|
7079
|
-
return result;
|
|
7080
|
-
}
|
|
7081
|
-
function parseExtractionResponse(text) {
|
|
7082
|
-
const trimmed = text.trim();
|
|
7083
|
-
let jsonStr = trimmed;
|
|
7084
|
-
if (jsonStr.startsWith("```")) {
|
|
7085
|
-
const firstNewline = jsonStr.indexOf("\n");
|
|
7086
|
-
jsonStr = jsonStr.slice(firstNewline + 1);
|
|
7087
|
-
const lastFence = jsonStr.lastIndexOf("```");
|
|
7088
|
-
if (lastFence !== -1) {
|
|
7089
|
-
jsonStr = jsonStr.slice(0, lastFence);
|
|
7090
|
-
}
|
|
7091
|
-
}
|
|
7092
|
-
try {
|
|
7093
|
-
const parsed = JSON.parse(jsonStr);
|
|
7094
|
-
if (parsed && typeof parsed === "object" && Array.isArray(parsed.memories)) {
|
|
7095
|
-
return parsed;
|
|
7096
|
-
}
|
|
7097
|
-
return null;
|
|
7098
|
-
} catch {
|
|
7099
|
-
return null;
|
|
7100
|
-
}
|
|
6903
|
+
preventContinuation,
|
|
6904
|
+
shouldBreak: false,
|
|
6905
|
+
shouldContinue: true,
|
|
6906
|
+
hasAttemptedReactiveCompactReset: true,
|
|
6907
|
+
systemPrompt,
|
|
6908
|
+
toolDefs
|
|
6909
|
+
};
|
|
7101
6910
|
}
|
|
7102
6911
|
|
|
7103
6912
|
// src/pipeline/finalize-turn.ts
|
|
@@ -7257,7 +7066,8 @@ var Thread = class {
|
|
|
7257
7066
|
this.permissionContext = {
|
|
7258
7067
|
mode: config.permissions.mode ?? "default",
|
|
7259
7068
|
rules: [...config.permissions.rules ?? []],
|
|
7260
|
-
workingDirectories: [...config.permissions.workingDirectories ?? []]
|
|
7069
|
+
workingDirectories: [...config.permissions.workingDirectories ?? []],
|
|
7070
|
+
dotDirNames: config.dotDirResolver?.config.names
|
|
7261
7071
|
};
|
|
7262
7072
|
this.permissionHandler = config.permissions.handler ?? null;
|
|
7263
7073
|
if (config.permissions.denialTracking) {
|
|
@@ -7389,7 +7199,8 @@ var Thread = class {
|
|
|
7389
7199
|
toolCtx.cwd = newCwd;
|
|
7390
7200
|
},
|
|
7391
7201
|
fileStateCache: this.fileStateCache ?? void 0,
|
|
7392
|
-
notifyHook: this.hooks.length > 0 ? (event, input) => runNotificationHooks(this.hooks, event, input) : void 0
|
|
7202
|
+
notifyHook: this.hooks.length > 0 ? (event, input) => runNotificationHooks(this.hooks, event, input) : void 0,
|
|
7203
|
+
dotDirResolver: this.config.dotDirResolver
|
|
7393
7204
|
};
|
|
7394
7205
|
const turnUsage = {
|
|
7395
7206
|
prompt_tokens: 0,
|
|
@@ -7812,7 +7623,7 @@ var Thread = class {
|
|
|
7812
7623
|
if (uuids.length === 0) return;
|
|
7813
7624
|
await this.storage.appendSnipBoundary(this.sessionId, uuids);
|
|
7814
7625
|
const entries = await this.storage.loadAllEntries(this.sessionId);
|
|
7815
|
-
const { applySnipRemovals: applySnipRemovals2 } = await import("./history-snip-
|
|
7626
|
+
const { applySnipRemovals: applySnipRemovals2 } = await import("./history-snip-HAWNAYKY.js");
|
|
7816
7627
|
let lastBoundaryIdx = -1;
|
|
7817
7628
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
7818
7629
|
if (entries[i].type === "compact-boundary") {
|
|
@@ -7869,16 +7680,16 @@ var Thread = class {
|
|
|
7869
7680
|
};
|
|
7870
7681
|
|
|
7871
7682
|
// src/skills/loader.ts
|
|
7872
|
-
async function loadSkills(
|
|
7683
|
+
async function loadSkills(fs, paths) {
|
|
7873
7684
|
const skills = [];
|
|
7874
7685
|
for (const skillPath of paths) {
|
|
7875
7686
|
try {
|
|
7876
|
-
const
|
|
7877
|
-
if (
|
|
7878
|
-
const skill = await loadSkillFile(
|
|
7687
|
+
const stat = await fs.stat(skillPath);
|
|
7688
|
+
if (stat.isFile) {
|
|
7689
|
+
const skill = await loadSkillFile(fs, skillPath);
|
|
7879
7690
|
if (skill) skills.push(skill);
|
|
7880
|
-
} else if (
|
|
7881
|
-
const dirSkills = await loadSkillsFromDir(
|
|
7691
|
+
} else if (stat.isDirectory) {
|
|
7692
|
+
const dirSkills = await loadSkillsFromDir(fs, skillPath);
|
|
7882
7693
|
skills.push(...dirSkills);
|
|
7883
7694
|
}
|
|
7884
7695
|
} catch {
|
|
@@ -7886,9 +7697,9 @@ async function loadSkills(fs2, paths) {
|
|
|
7886
7697
|
}
|
|
7887
7698
|
return skills;
|
|
7888
7699
|
}
|
|
7889
|
-
async function loadSkillFile(
|
|
7700
|
+
async function loadSkillFile(fs, filePath) {
|
|
7890
7701
|
try {
|
|
7891
|
-
const raw = await
|
|
7702
|
+
const raw = await fs.readFile(filePath);
|
|
7892
7703
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
7893
7704
|
const name = extractSkillName(filePath, body);
|
|
7894
7705
|
const fmDescription = frontmatter.description;
|
|
@@ -7911,17 +7722,17 @@ async function loadSkillFile(fs2, filePath) {
|
|
|
7911
7722
|
return null;
|
|
7912
7723
|
}
|
|
7913
7724
|
}
|
|
7914
|
-
async function loadSkillsFromDir(
|
|
7725
|
+
async function loadSkillsFromDir(fs, dirPath) {
|
|
7915
7726
|
const skills = [];
|
|
7916
7727
|
try {
|
|
7917
|
-
const entries = await
|
|
7728
|
+
const entries = await fs.readdir(dirPath);
|
|
7918
7729
|
for (const entry of entries) {
|
|
7919
7730
|
if (entry.isFile && (entry.name === "SKILL.md" || entry.name.endsWith(".md"))) {
|
|
7920
|
-
const skill = await loadSkillFile(
|
|
7731
|
+
const skill = await loadSkillFile(fs, entry.path);
|
|
7921
7732
|
if (skill) skills.push(skill);
|
|
7922
7733
|
} else if (entry.isDirectory) {
|
|
7923
7734
|
const skillMdPath = `${entry.path}/SKILL.md`;
|
|
7924
|
-
const skill = await loadSkillFile(
|
|
7735
|
+
const skill = await loadSkillFile(fs, skillMdPath);
|
|
7925
7736
|
if (skill) skills.push(skill);
|
|
7926
7737
|
}
|
|
7927
7738
|
}
|
|
@@ -7950,16 +7761,41 @@ function extractDescription(content) {
|
|
|
7950
7761
|
return void 0;
|
|
7951
7762
|
}
|
|
7952
7763
|
|
|
7764
|
+
// src/retry/types.ts
|
|
7765
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
7766
|
+
maxRetries: 10,
|
|
7767
|
+
baseDelayMs: 500,
|
|
7768
|
+
maxDelayMs: 32e3,
|
|
7769
|
+
retryableStatuses: [408, 409, 429, 500, 502, 503, 504, 529],
|
|
7770
|
+
maxConsecutiveOverloaded: 3
|
|
7771
|
+
};
|
|
7772
|
+
|
|
7953
7773
|
// src/prompt/context.ts
|
|
7954
7774
|
async function buildUserContext(opts) {
|
|
7955
|
-
|
|
7775
|
+
const ordered = [];
|
|
7776
|
+
if (opts.dotDirResolver) {
|
|
7777
|
+
if (opts.homeDir) {
|
|
7778
|
+
const homeDirs = opts.dotDirResolver.candidates(opts.homeDir).slice().reverse().map((d) => `${d}/skills`);
|
|
7779
|
+
const homeSkills = await loadSkills(opts.fs, homeDirs);
|
|
7780
|
+
ordered.push(...homeSkills);
|
|
7781
|
+
}
|
|
7782
|
+
if (opts.cwd) {
|
|
7783
|
+
const ancestors = walkAncestors2(opts.cwd);
|
|
7784
|
+
for (const ancestor of ancestors) {
|
|
7785
|
+
const ancestorDirs = opts.dotDirResolver.candidates(ancestor).slice().reverse().map((d) => `${d}/skills`);
|
|
7786
|
+
const ancestorSkills = await loadSkills(opts.fs, ancestorDirs);
|
|
7787
|
+
ordered.push(...ancestorSkills);
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
}
|
|
7956
7791
|
if (opts.skillsPaths && opts.skillsPaths.length > 0) {
|
|
7957
7792
|
const loaded = await loadSkills(opts.fs, opts.skillsPaths);
|
|
7958
|
-
|
|
7793
|
+
ordered.push(...loaded);
|
|
7959
7794
|
}
|
|
7960
7795
|
if (opts.inlineSkills) {
|
|
7961
|
-
|
|
7796
|
+
ordered.push(...opts.inlineSkills);
|
|
7962
7797
|
}
|
|
7798
|
+
const skills = dedupeSkillsByName(ordered);
|
|
7963
7799
|
const date = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
7964
7800
|
weekday: "long",
|
|
7965
7801
|
year: "numeric",
|
|
@@ -7968,15 +7804,23 @@ async function buildUserContext(opts) {
|
|
|
7968
7804
|
});
|
|
7969
7805
|
return { skills, date };
|
|
7970
7806
|
}
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7807
|
+
function dedupeSkillsByName(skills) {
|
|
7808
|
+
const byName = /* @__PURE__ */ new Map();
|
|
7809
|
+
for (const skill of skills) {
|
|
7810
|
+
byName.set(skill.name, skill);
|
|
7811
|
+
}
|
|
7812
|
+
return Array.from(byName.values());
|
|
7813
|
+
}
|
|
7814
|
+
function walkAncestors2(cwd) {
|
|
7815
|
+
const normalized = cwd.endsWith("/") && cwd.length > 1 ? cwd.slice(0, -1) : cwd;
|
|
7816
|
+
const parts = normalized.split("/");
|
|
7817
|
+
const dirs = [];
|
|
7818
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
7819
|
+
const dir = parts.slice(0, i).join("/") || "/";
|
|
7820
|
+
dirs.push(dir);
|
|
7821
|
+
}
|
|
7822
|
+
return dirs;
|
|
7823
|
+
}
|
|
7980
7824
|
|
|
7981
7825
|
// src/diagnostics.ts
|
|
7982
7826
|
async function timedCheck(fn, timeoutMs) {
|
|
@@ -8010,9 +7854,9 @@ async function checkProviderHealth(provider, model, timeoutMs) {
|
|
|
8010
7854
|
return { ok: false, latencyMs: 0, error: formatDiagError(err), model };
|
|
8011
7855
|
}
|
|
8012
7856
|
}
|
|
8013
|
-
async function checkVirtualFs(
|
|
7857
|
+
async function checkVirtualFs(fs, timeoutMs) {
|
|
8014
7858
|
try {
|
|
8015
|
-
const { latencyMs } = await timedCheck(async () =>
|
|
7859
|
+
const { latencyMs } = await timedCheck(async () => fs.exists("/"), timeoutMs);
|
|
8016
7860
|
return { ok: true, latencyMs };
|
|
8017
7861
|
} catch (err) {
|
|
8018
7862
|
return { ok: false, latencyMs: 0, error: formatDiagError(err) };
|
|
@@ -8034,9 +7878,9 @@ async function checkVirtualComputer(computer, timeoutMs) {
|
|
|
8034
7878
|
}
|
|
8035
7879
|
async function checkSandboxRuntime() {
|
|
8036
7880
|
try {
|
|
8037
|
-
const { SandboxManager
|
|
8038
|
-
const supported =
|
|
8039
|
-
const deps =
|
|
7881
|
+
const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
|
|
7882
|
+
const supported = SandboxManager.isSupportedPlatform();
|
|
7883
|
+
const deps = SandboxManager.checkDependencies();
|
|
8040
7884
|
const hasErrors = deps.errors.length > 0;
|
|
8041
7885
|
if (supported && !hasErrors) {
|
|
8042
7886
|
return {
|
|
@@ -8101,11 +7945,16 @@ function resolveAgentConfig(input) {
|
|
|
8101
7945
|
} else if (typeof input.retry === "object") {
|
|
8102
7946
|
retryConfig = input.retry;
|
|
8103
7947
|
}
|
|
7948
|
+
const dotDirs = input.dotDirs ?? DEFAULT_DOT_DIRS;
|
|
7949
|
+
const dotDirResolver = createDotDirResolver(dotDirs);
|
|
8104
7950
|
let projectContextConfig;
|
|
8105
7951
|
if (input.projectContext === true) {
|
|
8106
|
-
projectContextConfig = { cwd: effectiveCwd };
|
|
7952
|
+
projectContextConfig = { cwd: effectiveCwd, dotDirs };
|
|
8107
7953
|
} else if (typeof input.projectContext === "object") {
|
|
8108
|
-
projectContextConfig =
|
|
7954
|
+
projectContextConfig = {
|
|
7955
|
+
...input.projectContext,
|
|
7956
|
+
dotDirs: input.projectContext.dotDirs ?? dotDirs
|
|
7957
|
+
};
|
|
8109
7958
|
}
|
|
8110
7959
|
const mcpServerConfigs = input.mcpServers && Object.keys(input.mcpServers).length > 0 ? input.mcpServers : void 0;
|
|
8111
7960
|
const lspConfigs = input.lsp && Object.keys(input.lsp).length > 0 ? input.lsp : void 0;
|
|
@@ -8114,7 +7963,9 @@ function resolveAgentConfig(input) {
|
|
|
8114
7963
|
retryConfig,
|
|
8115
7964
|
projectContextConfig,
|
|
8116
7965
|
mcpServerConfigs,
|
|
8117
|
-
lspConfigs
|
|
7966
|
+
lspConfigs,
|
|
7967
|
+
dotDirs,
|
|
7968
|
+
dotDirResolver
|
|
8118
7969
|
};
|
|
8119
7970
|
}
|
|
8120
7971
|
|
|
@@ -8169,6 +8020,7 @@ var Agent = class {
|
|
|
8169
8020
|
toolSearchEnabled;
|
|
8170
8021
|
projectContextConfig;
|
|
8171
8022
|
resolvedProjectContext = null;
|
|
8023
|
+
dotDirResolver;
|
|
8172
8024
|
checkpointManager = null;
|
|
8173
8025
|
promptCachingConfig;
|
|
8174
8026
|
fileStateCacheConfig;
|
|
@@ -8197,13 +8049,15 @@ var Agent = class {
|
|
|
8197
8049
|
retry: opts.options?.retry,
|
|
8198
8050
|
projectContext: opts.options?.projectContext,
|
|
8199
8051
|
mcpServers: opts.options?.mcpServers,
|
|
8200
|
-
lsp: opts.options?.lsp
|
|
8052
|
+
lsp: opts.options?.lsp,
|
|
8053
|
+
dotDirs: opts.options?.dotDirs
|
|
8201
8054
|
});
|
|
8202
|
-
const resolvedSandbox = opts.sandbox
|
|
8055
|
+
const resolvedSandbox = opts.sandbox;
|
|
8203
8056
|
this.sandbox = resolvedSandbox;
|
|
8204
8057
|
this.fs = resolvedSandbox.fs;
|
|
8205
8058
|
this.computer = resolvedSandbox.computer;
|
|
8206
|
-
this.
|
|
8059
|
+
this.dotDirResolver = resolved.dotDirResolver;
|
|
8060
|
+
this.sessionDir = opts.options?.sessionDir ?? `${resolved.dotDirResolver.writePath(resolved.effectiveCwd)}/sessions`;
|
|
8207
8061
|
this.skills = opts.options?.skills ?? [];
|
|
8208
8062
|
this.skillsPaths = opts.options?.skillsPaths ?? [];
|
|
8209
8063
|
this.tools = opts.options?.tools ?? [];
|
|
@@ -8219,7 +8073,7 @@ var Agent = class {
|
|
|
8219
8073
|
this.enableSubagents = opts.options?.enableSubagents ?? false;
|
|
8220
8074
|
this.enableTasks = opts.options?.enableTasks ?? false;
|
|
8221
8075
|
if (this.enableTasks) {
|
|
8222
|
-
const tasksDir = opts.options?.tasksDir ??
|
|
8076
|
+
const tasksDir = opts.options?.tasksDir ?? `${this.dotDirResolver.writePath(resolved.effectiveCwd)}/tasks`;
|
|
8223
8077
|
this.taskStore = new TaskStore(this.fs, tasksDir);
|
|
8224
8078
|
}
|
|
8225
8079
|
this.enablePlanMode = opts.options?.enablePlanMode ?? false;
|
|
@@ -8250,7 +8104,10 @@ var Agent = class {
|
|
|
8250
8104
|
this.projectContextConfig = resolved.projectContextConfig;
|
|
8251
8105
|
this.promptCachingConfig = opts.options?.promptCaching;
|
|
8252
8106
|
this.fileStateCacheConfig = opts.options?.fileStateCache;
|
|
8253
|
-
this.toolResultStorageConfig = opts.options?.toolResultStorage
|
|
8107
|
+
this.toolResultStorageConfig = opts.options?.toolResultStorage ? {
|
|
8108
|
+
...opts.options.toolResultStorage,
|
|
8109
|
+
storageDir: opts.options.toolResultStorage.storageDir ?? `${this.dotDirResolver.writePath(resolved.effectiveCwd)}/tool-results`
|
|
8110
|
+
} : void 0;
|
|
8254
8111
|
this.historySnipConfig = opts.options?.historySnip;
|
|
8255
8112
|
this.outputFormat = opts.options?.outputFormat;
|
|
8256
8113
|
this.structuredOutputMode = opts.options?.structuredOutputMode;
|
|
@@ -8263,7 +8120,10 @@ var Agent = class {
|
|
|
8263
8120
|
if (opts.options?.checkpoint?.enabled) {
|
|
8264
8121
|
this.checkpointManager = new FileCheckpointManager(
|
|
8265
8122
|
this.fs,
|
|
8266
|
-
|
|
8123
|
+
{
|
|
8124
|
+
...opts.options.checkpoint,
|
|
8125
|
+
backupDir: opts.options.checkpoint.backupDir ?? `${this.dotDirResolver.writePath(resolved.effectiveCwd)}/checkpoints`
|
|
8126
|
+
}
|
|
8267
8127
|
);
|
|
8268
8128
|
}
|
|
8269
8129
|
}
|
|
@@ -8271,8 +8131,8 @@ var Agent = class {
|
|
|
8271
8131
|
if (this.resolvedProvider) return this.resolvedProvider;
|
|
8272
8132
|
if (!this.providerPromise) {
|
|
8273
8133
|
this.providerPromise = (async () => {
|
|
8274
|
-
const { resolveProvider
|
|
8275
|
-
return
|
|
8134
|
+
const { resolveProvider } = await import("./resolve-AGQZFMKD.js");
|
|
8135
|
+
return resolveProvider(this.providerInput, { model: this.model });
|
|
8276
8136
|
})();
|
|
8277
8137
|
}
|
|
8278
8138
|
this.resolvedProvider = await this.providerPromise;
|
|
@@ -8288,10 +8148,14 @@ var Agent = class {
|
|
|
8288
8148
|
}
|
|
8289
8149
|
async getSkills() {
|
|
8290
8150
|
if (this.resolvedSkills) return this.resolvedSkills;
|
|
8151
|
+
const homeDir = process.env.HOME ?? process.env.USERPROFILE;
|
|
8291
8152
|
const ctx = await buildUserContext({
|
|
8292
8153
|
fs: this.fs,
|
|
8293
8154
|
skillsPaths: this.skillsPaths,
|
|
8294
|
-
inlineSkills: this.skills
|
|
8155
|
+
inlineSkills: this.skills,
|
|
8156
|
+
dotDirResolver: this.dotDirResolver,
|
|
8157
|
+
cwd: this.cwd,
|
|
8158
|
+
homeDir
|
|
8295
8159
|
});
|
|
8296
8160
|
this.resolvedSkills = ctx.skills;
|
|
8297
8161
|
return this.resolvedSkills;
|
|
@@ -8350,7 +8214,8 @@ var Agent = class {
|
|
|
8350
8214
|
historySnip: this.historySnipConfig,
|
|
8351
8215
|
promptCachingEnabled: this.promptCachingConfig?.enabled ?? false,
|
|
8352
8216
|
skipCacheWrite: true,
|
|
8353
|
-
projectContext: this.resolvedProjectContext ?? void 0
|
|
8217
|
+
projectContext: this.resolvedProjectContext ?? void 0,
|
|
8218
|
+
dotDirResolver: this.dotDirResolver
|
|
8354
8219
|
},
|
|
8355
8220
|
{ cwd: parentCwd }
|
|
8356
8221
|
);
|
|
@@ -8412,6 +8277,7 @@ var Agent = class {
|
|
|
8412
8277
|
promptCachingEnabled: this.promptCachingConfig?.enabled ?? false,
|
|
8413
8278
|
mcpToolNames: this.mcpToolNames.size > 0 ? this.mcpToolNames : void 0,
|
|
8414
8279
|
projectContext: this.resolvedProjectContext ?? void 0,
|
|
8280
|
+
dotDirResolver: this.dotDirResolver,
|
|
8415
8281
|
outputFormat: this.outputFormat,
|
|
8416
8282
|
structuredOutputMode: this.structuredOutputMode
|
|
8417
8283
|
},
|
|
@@ -8626,7 +8492,7 @@ var Agent = class {
|
|
|
8626
8492
|
if (this.mcpServerConfigs && !this.mcpManager) {
|
|
8627
8493
|
tasks.push(
|
|
8628
8494
|
(async () => {
|
|
8629
|
-
const { McpClientManager: McpMgr } = await import("./client-
|
|
8495
|
+
const { McpClientManager: McpMgr } = await import("./client-JJFLE6RT.js");
|
|
8630
8496
|
this.mcpManager = new McpMgr(this.mcpServerConfigs, {
|
|
8631
8497
|
tokenStorage: this.mcpTokenStorage,
|
|
8632
8498
|
onAuthorizationUrl: this.mcpOnAuthorizationUrl
|
|
@@ -8636,7 +8502,7 @@ var Agent = class {
|
|
|
8636
8502
|
this.mcpToolNames = new Set(this.mcpTools.map((t) => t.name));
|
|
8637
8503
|
const needsAuth = this.mcpManager.getServersNeedingAuth();
|
|
8638
8504
|
if (needsAuth.length > 0) {
|
|
8639
|
-
const { createMcpAuthTool } = await import("./mcp-auth-
|
|
8505
|
+
const { createMcpAuthTool } = await import("./mcp-auth-NOIQPF7W.js");
|
|
8640
8506
|
this.mcpAuthTools = needsAuth.map(
|
|
8641
8507
|
(name) => createMcpAuthTool(name, this.mcpManager)
|
|
8642
8508
|
);
|
|
@@ -8647,8 +8513,8 @@ var Agent = class {
|
|
|
8647
8513
|
if (this.lspConfigs && !this.lspManager) {
|
|
8648
8514
|
tasks.push(
|
|
8649
8515
|
(async () => {
|
|
8650
|
-
const { LspServerManager } = await import("./manager-
|
|
8651
|
-
const { lspTool } = await import("./lsp-
|
|
8516
|
+
const { LspServerManager } = await import("./manager-Z5EQ7YYV.js");
|
|
8517
|
+
const { lspTool } = await import("./lsp-3APWNKB2.js");
|
|
8652
8518
|
const rootUri = `file://${this.cwd}`;
|
|
8653
8519
|
this.lspManager = new LspServerManager(this.lspConfigs, rootUri);
|
|
8654
8520
|
this.lspToolRef = lspTool;
|
|
@@ -8684,30 +8550,20 @@ var Agent = class {
|
|
|
8684
8550
|
// ---------------------------------------------------------------------------
|
|
8685
8551
|
// Sandbox index — local host file mapping sessionId → sandboxId so we can
|
|
8686
8552
|
// reconnect to auto-created containers on resume without accessing the
|
|
8687
|
-
// (potentially unreachable) sandbox filesystem.
|
|
8553
|
+
// (potentially unreachable) sandbox filesystem. The implementation is
|
|
8554
|
+
// quarantined in `session/sandbox-index.ts` and lazy-loaded so that
|
|
8555
|
+
// `node:fs/promises` + `node:path.resolve(cwd, ...)` don't appear in the
|
|
8556
|
+
// Agent's static import graph — bundler dependency tracers (Next.js NFT,
|
|
8557
|
+
// serverless-webpack, …) flag top-level dynamic path resolution as
|
|
8558
|
+
// "whole project was traced unintentionally".
|
|
8688
8559
|
// ---------------------------------------------------------------------------
|
|
8689
|
-
get sandboxIndexPath() {
|
|
8690
|
-
return nodePath.resolve(this.cwd, this.sessionDir, ".sandbox-index.json");
|
|
8691
|
-
}
|
|
8692
8560
|
async loadSandboxId(sessionId) {
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
const index = JSON.parse(content);
|
|
8696
|
-
return index[sessionId];
|
|
8697
|
-
} catch {
|
|
8698
|
-
return void 0;
|
|
8699
|
-
}
|
|
8561
|
+
const { loadSandboxId } = await import("./sandbox-index-ODNREIFA.js");
|
|
8562
|
+
return loadSandboxId(this.cwd, this.sessionDir, sessionId);
|
|
8700
8563
|
}
|
|
8701
8564
|
async storeSandboxId(sessionId, sandboxId) {
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
const content = await nodeFs.readFile(this.sandboxIndexPath, "utf-8");
|
|
8705
|
-
index = JSON.parse(content);
|
|
8706
|
-
} catch {
|
|
8707
|
-
}
|
|
8708
|
-
index[sessionId] = sandboxId;
|
|
8709
|
-
await nodeFs.mkdir(nodePath.dirname(this.sandboxIndexPath), { recursive: true });
|
|
8710
|
-
await nodeFs.writeFile(this.sandboxIndexPath, JSON.stringify(index, null, 2));
|
|
8565
|
+
const { storeSandboxId } = await import("./sandbox-index-ODNREIFA.js");
|
|
8566
|
+
return storeSandboxId(this.cwd, this.sessionDir, sessionId, sandboxId);
|
|
8711
8567
|
}
|
|
8712
8568
|
/**
|
|
8713
8569
|
* Disconnect all MCP clients. Call when done with this Agent instance.
|
|
@@ -8741,648 +8597,7 @@ var Agent = class {
|
|
|
8741
8597
|
}
|
|
8742
8598
|
};
|
|
8743
8599
|
|
|
8744
|
-
// src/presets.ts
|
|
8745
|
-
function codingAgent(opts) {
|
|
8746
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
8747
|
-
return new Agent({
|
|
8748
|
-
provider: opts.provider,
|
|
8749
|
-
sandbox: opts.sandbox ?? UnsandboxedLocal({ cwd }),
|
|
8750
|
-
options: {
|
|
8751
|
-
cwd,
|
|
8752
|
-
model: opts.model,
|
|
8753
|
-
systemPrompt: opts.systemPrompt,
|
|
8754
|
-
permissions: { mode: "default" },
|
|
8755
|
-
autoCompact: true,
|
|
8756
|
-
enableSubagents: true,
|
|
8757
|
-
enableTasks: true,
|
|
8758
|
-
enablePlanMode: true,
|
|
8759
|
-
projectContext: { cwd },
|
|
8760
|
-
costTracking: { enabled: true },
|
|
8761
|
-
retry: true,
|
|
8762
|
-
hooks: opts.hooks,
|
|
8763
|
-
mcpServers: opts.mcpServers,
|
|
8764
|
-
autoTitle: opts.autoTitle
|
|
8765
|
-
}
|
|
8766
|
-
});
|
|
8767
|
-
}
|
|
8768
|
-
function planningAgent(opts) {
|
|
8769
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
8770
|
-
return new Agent({
|
|
8771
|
-
provider: opts.provider,
|
|
8772
|
-
sandbox: opts.sandbox ?? UnsandboxedLocal({ cwd }),
|
|
8773
|
-
options: {
|
|
8774
|
-
cwd,
|
|
8775
|
-
model: opts.model,
|
|
8776
|
-
systemPrompt: opts.systemPrompt,
|
|
8777
|
-
permissions: { mode: "plan" },
|
|
8778
|
-
autoCompact: true,
|
|
8779
|
-
enableSubagents: false,
|
|
8780
|
-
enableTasks: false,
|
|
8781
|
-
enablePlanMode: true,
|
|
8782
|
-
projectContext: { cwd },
|
|
8783
|
-
costTracking: { enabled: true },
|
|
8784
|
-
retry: true,
|
|
8785
|
-
hooks: opts.hooks,
|
|
8786
|
-
mcpServers: opts.mcpServers,
|
|
8787
|
-
autoTitle: opts.autoTitle
|
|
8788
|
-
}
|
|
8789
|
-
});
|
|
8790
|
-
}
|
|
8791
|
-
function reviewAgent(opts) {
|
|
8792
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
8793
|
-
return new Agent({
|
|
8794
|
-
provider: opts.provider,
|
|
8795
|
-
sandbox: opts.sandbox ?? UnsandboxedLocal({ cwd }),
|
|
8796
|
-
options: {
|
|
8797
|
-
cwd,
|
|
8798
|
-
model: opts.model,
|
|
8799
|
-
systemPrompt: opts.systemPrompt,
|
|
8800
|
-
permissions: { mode: "plan" },
|
|
8801
|
-
autoCompact: true,
|
|
8802
|
-
enableSubagents: false,
|
|
8803
|
-
enableTasks: false,
|
|
8804
|
-
enablePlanMode: true,
|
|
8805
|
-
projectContext: { cwd },
|
|
8806
|
-
costTracking: { enabled: true },
|
|
8807
|
-
retry: true,
|
|
8808
|
-
hooks: opts.hooks,
|
|
8809
|
-
mcpServers: opts.mcpServers,
|
|
8810
|
-
autoTitle: opts.autoTitle,
|
|
8811
|
-
webSearch: {
|
|
8812
|
-
search: async (query) => {
|
|
8813
|
-
try {
|
|
8814
|
-
const res = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`);
|
|
8815
|
-
if (!res.ok) return [];
|
|
8816
|
-
return [{ title: query, url: res.url, snippet: `Search results for: ${query}` }];
|
|
8817
|
-
} catch {
|
|
8818
|
-
return [];
|
|
8819
|
-
}
|
|
8820
|
-
}
|
|
8821
|
-
}
|
|
8822
|
-
}
|
|
8823
|
-
});
|
|
8824
|
-
}
|
|
8825
|
-
|
|
8826
|
-
// src/swarm/mailbox.ts
|
|
8827
|
-
var Mailbox = class {
|
|
8828
|
-
messages = [];
|
|
8829
|
-
listeners = /* @__PURE__ */ new Map();
|
|
8830
|
-
/**
|
|
8831
|
-
* Send a message from one member to another.
|
|
8832
|
-
*/
|
|
8833
|
-
send(from, to, content) {
|
|
8834
|
-
const msg = {
|
|
8835
|
-
from,
|
|
8836
|
-
to,
|
|
8837
|
-
content,
|
|
8838
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8839
|
-
};
|
|
8840
|
-
this.messages.push(msg);
|
|
8841
|
-
const handlers = this.listeners.get(to);
|
|
8842
|
-
if (handlers) {
|
|
8843
|
-
for (const handler of handlers) {
|
|
8844
|
-
handler(msg);
|
|
8845
|
-
}
|
|
8846
|
-
}
|
|
8847
|
-
}
|
|
8848
|
-
/**
|
|
8849
|
-
* Broadcast a message to all members except the sender.
|
|
8850
|
-
*/
|
|
8851
|
-
broadcast(from, content, memberNames) {
|
|
8852
|
-
for (const name of memberNames) {
|
|
8853
|
-
if (name !== from) {
|
|
8854
|
-
this.send(from, name, content);
|
|
8855
|
-
}
|
|
8856
|
-
}
|
|
8857
|
-
}
|
|
8858
|
-
/**
|
|
8859
|
-
* Get all messages sent to a specific member.
|
|
8860
|
-
*/
|
|
8861
|
-
getMessagesFor(memberName) {
|
|
8862
|
-
return this.messages.filter((m) => m.to === memberName);
|
|
8863
|
-
}
|
|
8864
|
-
/**
|
|
8865
|
-
* Get all unread messages for a member since the last check.
|
|
8866
|
-
*/
|
|
8867
|
-
getNewMessagesFor(memberName, since) {
|
|
8868
|
-
return this.messages.filter(
|
|
8869
|
-
(m) => m.to === memberName && m.timestamp > since
|
|
8870
|
-
);
|
|
8871
|
-
}
|
|
8872
|
-
/**
|
|
8873
|
-
* Register a listener for incoming messages to a member.
|
|
8874
|
-
*/
|
|
8875
|
-
onMessage(memberName, handler) {
|
|
8876
|
-
const existing = this.listeners.get(memberName) ?? [];
|
|
8877
|
-
existing.push(handler);
|
|
8878
|
-
this.listeners.set(memberName, existing);
|
|
8879
|
-
return () => {
|
|
8880
|
-
const handlers = this.listeners.get(memberName);
|
|
8881
|
-
if (handlers) {
|
|
8882
|
-
const idx = handlers.indexOf(handler);
|
|
8883
|
-
if (idx !== -1) handlers.splice(idx, 1);
|
|
8884
|
-
}
|
|
8885
|
-
};
|
|
8886
|
-
}
|
|
8887
|
-
/**
|
|
8888
|
-
* Get all messages in the mailbox.
|
|
8889
|
-
*/
|
|
8890
|
-
getAllMessages() {
|
|
8891
|
-
return [...this.messages];
|
|
8892
|
-
}
|
|
8893
|
-
};
|
|
8894
|
-
|
|
8895
|
-
// src/swarm/manager.ts
|
|
8896
|
-
var SwarmManager = class {
|
|
8897
|
-
members = /* @__PURE__ */ new Map();
|
|
8898
|
-
backend;
|
|
8899
|
-
mailbox = new Mailbox();
|
|
8900
|
-
config;
|
|
8901
|
-
runningTasks = /* @__PURE__ */ new Map();
|
|
8902
|
-
eventHandlers = [];
|
|
8903
|
-
constructor(backend, config) {
|
|
8904
|
-
this.backend = backend;
|
|
8905
|
-
this.config = config ?? {};
|
|
8906
|
-
}
|
|
8907
|
-
/**
|
|
8908
|
-
* Register a handler for swarm lifecycle events.
|
|
8909
|
-
*/
|
|
8910
|
-
onEvent(handler) {
|
|
8911
|
-
this.eventHandlers.push(handler);
|
|
8912
|
-
return () => {
|
|
8913
|
-
const idx = this.eventHandlers.indexOf(handler);
|
|
8914
|
-
if (idx !== -1) this.eventHandlers.splice(idx, 1);
|
|
8915
|
-
};
|
|
8916
|
-
}
|
|
8917
|
-
emit(event) {
|
|
8918
|
-
for (const handler of this.eventHandlers) {
|
|
8919
|
-
handler(event);
|
|
8920
|
-
}
|
|
8921
|
-
}
|
|
8922
|
-
/**
|
|
8923
|
-
* Spawn a new swarm member. Returns the member ID.
|
|
8924
|
-
*/
|
|
8925
|
-
async spawn(config) {
|
|
8926
|
-
const id = generateUUID();
|
|
8927
|
-
const member = {
|
|
8928
|
-
id,
|
|
8929
|
-
name: config.name,
|
|
8930
|
-
status: "pending"
|
|
8931
|
-
};
|
|
8932
|
-
this.members.set(id, member);
|
|
8933
|
-
const maxConcurrent = this.config.maxConcurrent ?? 4;
|
|
8934
|
-
const running = Array.from(this.members.values()).filter(
|
|
8935
|
-
(m) => m.status === "running"
|
|
8936
|
-
).length;
|
|
8937
|
-
if (running >= maxConcurrent) {
|
|
8938
|
-
await this.waitForSlot(maxConcurrent);
|
|
8939
|
-
}
|
|
8940
|
-
member.status = "running";
|
|
8941
|
-
this.emit({
|
|
8942
|
-
type: "swarm_member_start",
|
|
8943
|
-
memberId: id,
|
|
8944
|
-
memberName: config.name
|
|
8945
|
-
});
|
|
8946
|
-
const task = this.runMember(config, member);
|
|
8947
|
-
this.runningTasks.set(id, task);
|
|
8948
|
-
return id;
|
|
8949
|
-
}
|
|
8950
|
-
async runMember(config, member) {
|
|
8951
|
-
try {
|
|
8952
|
-
const gen = this.backend.spawn(config, member);
|
|
8953
|
-
let next = await gen.next();
|
|
8954
|
-
while (!next.done) {
|
|
8955
|
-
next = await gen.next();
|
|
8956
|
-
}
|
|
8957
|
-
member.result = next.value;
|
|
8958
|
-
member.status = "completed";
|
|
8959
|
-
this.emit({
|
|
8960
|
-
type: "swarm_member_complete",
|
|
8961
|
-
memberId: member.id,
|
|
8962
|
-
memberName: member.name,
|
|
8963
|
-
content: member.result
|
|
8964
|
-
});
|
|
8965
|
-
} catch (err) {
|
|
8966
|
-
member.error = err instanceof Error ? err : new Error(String(err));
|
|
8967
|
-
member.status = "failed";
|
|
8968
|
-
this.emit({
|
|
8969
|
-
type: "swarm_member_failed",
|
|
8970
|
-
memberId: member.id,
|
|
8971
|
-
memberName: member.name,
|
|
8972
|
-
error: member.error
|
|
8973
|
-
});
|
|
8974
|
-
} finally {
|
|
8975
|
-
this.runningTasks.delete(member.id);
|
|
8976
|
-
}
|
|
8977
|
-
}
|
|
8978
|
-
/**
|
|
8979
|
-
* Spawn multiple members concurrently. Returns their IDs.
|
|
8980
|
-
*/
|
|
8981
|
-
async spawnAll(configs) {
|
|
8982
|
-
const ids = [];
|
|
8983
|
-
for (const config of configs) {
|
|
8984
|
-
ids.push(await this.spawn(config));
|
|
8985
|
-
}
|
|
8986
|
-
return ids;
|
|
8987
|
-
}
|
|
8988
|
-
/**
|
|
8989
|
-
* Send a message between swarm members.
|
|
8990
|
-
*/
|
|
8991
|
-
sendMessage(from, to, content) {
|
|
8992
|
-
this.mailbox.send(from, to, content);
|
|
8993
|
-
this.emit({
|
|
8994
|
-
type: "swarm_message",
|
|
8995
|
-
memberId: to,
|
|
8996
|
-
memberName: this.getMemberName(to),
|
|
8997
|
-
content
|
|
8998
|
-
});
|
|
8999
|
-
}
|
|
9000
|
-
/**
|
|
9001
|
-
* Kill a running member.
|
|
9002
|
-
*/
|
|
9003
|
-
async kill(memberId) {
|
|
9004
|
-
const member = this.members.get(memberId);
|
|
9005
|
-
if (!member || member.status !== "running") return;
|
|
9006
|
-
await this.backend.kill(memberId);
|
|
9007
|
-
member.status = "killed";
|
|
9008
|
-
this.runningTasks.delete(memberId);
|
|
9009
|
-
}
|
|
9010
|
-
/**
|
|
9011
|
-
* Wait for all running members to complete.
|
|
9012
|
-
*/
|
|
9013
|
-
async waitForAll() {
|
|
9014
|
-
while (this.runningTasks.size > 0) {
|
|
9015
|
-
await Promise.race(this.runningTasks.values());
|
|
9016
|
-
}
|
|
9017
|
-
}
|
|
9018
|
-
/**
|
|
9019
|
-
* Get current swarm status.
|
|
9020
|
-
*/
|
|
9021
|
-
getStatus() {
|
|
9022
|
-
return {
|
|
9023
|
-
members: Array.from(this.members.values()),
|
|
9024
|
-
messages: this.mailbox.getAllMessages()
|
|
9025
|
-
};
|
|
9026
|
-
}
|
|
9027
|
-
/**
|
|
9028
|
-
* Get a specific member.
|
|
9029
|
-
*/
|
|
9030
|
-
getMember(id) {
|
|
9031
|
-
return this.members.get(id);
|
|
9032
|
-
}
|
|
9033
|
-
/**
|
|
9034
|
-
* Get the mailbox for direct access.
|
|
9035
|
-
*/
|
|
9036
|
-
getMailbox() {
|
|
9037
|
-
return this.mailbox;
|
|
9038
|
-
}
|
|
9039
|
-
getMemberName(id) {
|
|
9040
|
-
return this.members.get(id)?.name ?? id;
|
|
9041
|
-
}
|
|
9042
|
-
async waitForSlot(maxConcurrent) {
|
|
9043
|
-
while (true) {
|
|
9044
|
-
const running = Array.from(this.members.values()).filter(
|
|
9045
|
-
(m) => m.status === "running"
|
|
9046
|
-
).length;
|
|
9047
|
-
if (running < maxConcurrent) return;
|
|
9048
|
-
if (this.runningTasks.size > 0) {
|
|
9049
|
-
await Promise.race(this.runningTasks.values());
|
|
9050
|
-
} else {
|
|
9051
|
-
break;
|
|
9052
|
-
}
|
|
9053
|
-
}
|
|
9054
|
-
}
|
|
9055
|
-
};
|
|
9056
|
-
|
|
9057
|
-
// src/swarm/backends/in-process.ts
|
|
9058
|
-
var InProcessBackend = class {
|
|
9059
|
-
threadConfig;
|
|
9060
|
-
abortControllers = /* @__PURE__ */ new Map();
|
|
9061
|
-
constructor(threadConfig) {
|
|
9062
|
-
this.threadConfig = threadConfig;
|
|
9063
|
-
}
|
|
9064
|
-
async *spawn(config, member) {
|
|
9065
|
-
const ac = new AbortController();
|
|
9066
|
-
this.abortControllers.set(member.id, ac);
|
|
9067
|
-
const childTools = config.allowedTools ? (this.threadConfig.tools ?? []).filter(
|
|
9068
|
-
(t) => config.allowedTools.includes(t.name)
|
|
9069
|
-
) : this.threadConfig.tools;
|
|
9070
|
-
const thread = new Thread(
|
|
9071
|
-
{
|
|
9072
|
-
...this.threadConfig,
|
|
9073
|
-
tools: childTools,
|
|
9074
|
-
systemPrompt: config.systemPrompt,
|
|
9075
|
-
model: config.model
|
|
9076
|
-
},
|
|
9077
|
-
{ cwd: this.threadConfig.cwd }
|
|
9078
|
-
);
|
|
9079
|
-
member.sessionId = thread.sessionId;
|
|
9080
|
-
let resultText = "";
|
|
9081
|
-
for await (const event of thread.run(config.prompt, {
|
|
9082
|
-
signal: ac.signal
|
|
9083
|
-
})) {
|
|
9084
|
-
yield event;
|
|
9085
|
-
if (event.type === "message_complete" && event.message.content) {
|
|
9086
|
-
resultText += event.message.content;
|
|
9087
|
-
}
|
|
9088
|
-
}
|
|
9089
|
-
this.abortControllers.delete(member.id);
|
|
9090
|
-
return resultText;
|
|
9091
|
-
}
|
|
9092
|
-
async kill(memberId) {
|
|
9093
|
-
const ac = this.abortControllers.get(memberId);
|
|
9094
|
-
if (ac) {
|
|
9095
|
-
ac.abort();
|
|
9096
|
-
this.abortControllers.delete(memberId);
|
|
9097
|
-
}
|
|
9098
|
-
}
|
|
9099
|
-
};
|
|
9100
|
-
|
|
9101
|
-
// src/permissions/updates.ts
|
|
9102
|
-
function applyPermissionUpdate(ctx, update) {
|
|
9103
|
-
switch (update.type) {
|
|
9104
|
-
case "addRules":
|
|
9105
|
-
ctx.rules.push(...update.rules);
|
|
9106
|
-
break;
|
|
9107
|
-
case "removeRules":
|
|
9108
|
-
ctx.rules = ctx.rules.filter((r) => {
|
|
9109
|
-
if (r.toolName !== update.toolName) return true;
|
|
9110
|
-
if (update.behavior && r.behavior !== update.behavior) return true;
|
|
9111
|
-
return false;
|
|
9112
|
-
});
|
|
9113
|
-
break;
|
|
9114
|
-
case "setMode":
|
|
9115
|
-
ctx.mode = update.mode;
|
|
9116
|
-
break;
|
|
9117
|
-
case "addDirectories":
|
|
9118
|
-
for (const dir of update.directories) {
|
|
9119
|
-
if (!ctx.workingDirectories.includes(dir)) {
|
|
9120
|
-
ctx.workingDirectories.push(dir);
|
|
9121
|
-
}
|
|
9122
|
-
}
|
|
9123
|
-
break;
|
|
9124
|
-
case "removeDirectories":
|
|
9125
|
-
ctx.workingDirectories = ctx.workingDirectories.filter(
|
|
9126
|
-
(d) => !update.directories.includes(d)
|
|
9127
|
-
);
|
|
9128
|
-
break;
|
|
9129
|
-
}
|
|
9130
|
-
return ctx;
|
|
9131
|
-
}
|
|
9132
|
-
function applyPermissionUpdates(ctx, updates) {
|
|
9133
|
-
for (const update of updates) {
|
|
9134
|
-
applyPermissionUpdate(ctx, update);
|
|
9135
|
-
}
|
|
9136
|
-
return ctx;
|
|
9137
|
-
}
|
|
9138
|
-
|
|
9139
|
-
// src/tracing/otel.ts
|
|
9140
|
-
var otelApi = null;
|
|
9141
|
-
var otelLoadFailed = false;
|
|
9142
|
-
async function loadOTelApi() {
|
|
9143
|
-
if (otelApi) return otelApi;
|
|
9144
|
-
if (otelLoadFailed) return null;
|
|
9145
|
-
try {
|
|
9146
|
-
otelApi = await import("@opentelemetry/api");
|
|
9147
|
-
return otelApi;
|
|
9148
|
-
} catch {
|
|
9149
|
-
otelLoadFailed = true;
|
|
9150
|
-
return null;
|
|
9151
|
-
}
|
|
9152
|
-
}
|
|
9153
|
-
var OTelSpan = class {
|
|
9154
|
-
name;
|
|
9155
|
-
inner;
|
|
9156
|
-
constructor(name, inner) {
|
|
9157
|
-
this.name = name;
|
|
9158
|
-
this.inner = inner;
|
|
9159
|
-
}
|
|
9160
|
-
setAttribute(key, value) {
|
|
9161
|
-
this.inner.setAttribute(key, value);
|
|
9162
|
-
}
|
|
9163
|
-
addEvent(name, attributes) {
|
|
9164
|
-
this.inner.addEvent(name, attributes);
|
|
9165
|
-
}
|
|
9166
|
-
setStatus(code, message) {
|
|
9167
|
-
const otelCode = code === SpanStatusCode.ERROR ? 2 : 1;
|
|
9168
|
-
this.inner.setStatus({ code: otelCode, message });
|
|
9169
|
-
}
|
|
9170
|
-
end() {
|
|
9171
|
-
this.inner.end();
|
|
9172
|
-
}
|
|
9173
|
-
/** Access the underlying OTEL span for advanced use cases. */
|
|
9174
|
-
getInnerSpan() {
|
|
9175
|
-
return this.inner;
|
|
9176
|
-
}
|
|
9177
|
-
};
|
|
9178
|
-
var OTelTracer = class _OTelTracer {
|
|
9179
|
-
otelTracer;
|
|
9180
|
-
api;
|
|
9181
|
-
constructor(api, otelTracer) {
|
|
9182
|
-
this.api = api;
|
|
9183
|
-
this.otelTracer = otelTracer;
|
|
9184
|
-
}
|
|
9185
|
-
/**
|
|
9186
|
-
* Create an `OTelTracer`. Falls back to `NoopTracer` if
|
|
9187
|
-
* `@opentelemetry/api` is not available at runtime.
|
|
9188
|
-
*/
|
|
9189
|
-
static async create(serviceName = "noumen", version) {
|
|
9190
|
-
const api = await loadOTelApi();
|
|
9191
|
-
if (!api) return new NoopTracer();
|
|
9192
|
-
const tracer = api.trace.getTracer(serviceName, version);
|
|
9193
|
-
return new _OTelTracer(api, tracer);
|
|
9194
|
-
}
|
|
9195
|
-
startSpan(name, options) {
|
|
9196
|
-
const parentCtx = options?.parent instanceof OTelSpan ? this.api.trace.setSpan(this.api.context.active(), options.parent.getInnerSpan()) : this.api.context.active();
|
|
9197
|
-
const otelSpan = this.otelTracer.startSpan(
|
|
9198
|
-
name,
|
|
9199
|
-
options?.attributes ? { attributes: options.attributes } : void 0,
|
|
9200
|
-
parentCtx
|
|
9201
|
-
);
|
|
9202
|
-
return new OTelSpan(name, otelSpan);
|
|
9203
|
-
}
|
|
9204
|
-
};
|
|
9205
|
-
|
|
9206
|
-
// src/memory/file-provider.ts
|
|
9207
|
-
var INDEX_NAME = "MEMORY.md";
|
|
9208
|
-
var DEFAULT_MAX_LINES = 200;
|
|
9209
|
-
var DEFAULT_MAX_BYTES2 = 25e3;
|
|
9210
|
-
var MEMORY_TYPES2 = /* @__PURE__ */ new Set([
|
|
9211
|
-
"user",
|
|
9212
|
-
"project",
|
|
9213
|
-
"feedback",
|
|
9214
|
-
"reference"
|
|
9215
|
-
]);
|
|
9216
|
-
function parseFrontmatter2(raw) {
|
|
9217
|
-
const trimmed = raw.trimStart();
|
|
9218
|
-
if (!trimmed.startsWith("---")) {
|
|
9219
|
-
return { rest: raw };
|
|
9220
|
-
}
|
|
9221
|
-
const endIdx = trimmed.indexOf("---", 3);
|
|
9222
|
-
if (endIdx === -1) {
|
|
9223
|
-
return { rest: raw };
|
|
9224
|
-
}
|
|
9225
|
-
const fmBlock = trimmed.slice(3, endIdx).trim();
|
|
9226
|
-
const rest = trimmed.slice(endIdx + 3).trim();
|
|
9227
|
-
let name;
|
|
9228
|
-
let description;
|
|
9229
|
-
let type;
|
|
9230
|
-
for (const line of fmBlock.split("\n")) {
|
|
9231
|
-
const colonIdx = line.indexOf(":");
|
|
9232
|
-
if (colonIdx === -1) continue;
|
|
9233
|
-
const key = line.slice(0, colonIdx).trim();
|
|
9234
|
-
const value = line.slice(colonIdx + 1).trim();
|
|
9235
|
-
if (key === "name") name = value;
|
|
9236
|
-
else if (key === "description") description = value;
|
|
9237
|
-
else if (key === "type" && MEMORY_TYPES2.has(value)) type = value;
|
|
9238
|
-
}
|
|
9239
|
-
return { name, description, type, rest };
|
|
9240
|
-
}
|
|
9241
|
-
function serializeEntry(entry) {
|
|
9242
|
-
const lines = [
|
|
9243
|
-
"---",
|
|
9244
|
-
`name: ${entry.name}`,
|
|
9245
|
-
`description: ${entry.description}`,
|
|
9246
|
-
`type: ${entry.type}`,
|
|
9247
|
-
"---",
|
|
9248
|
-
"",
|
|
9249
|
-
entry.content
|
|
9250
|
-
];
|
|
9251
|
-
return lines.join("\n");
|
|
9252
|
-
}
|
|
9253
|
-
function slugify(name) {
|
|
9254
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 60);
|
|
9255
|
-
}
|
|
9256
|
-
function truncateIndex(raw, maxLines = DEFAULT_MAX_LINES, maxBytes = DEFAULT_MAX_BYTES2) {
|
|
9257
|
-
const trimmed = raw.trim();
|
|
9258
|
-
const contentLines = trimmed.split("\n");
|
|
9259
|
-
const lineCount = contentLines.length;
|
|
9260
|
-
const byteCount = trimmed.length;
|
|
9261
|
-
const wasLineTruncated = lineCount > maxLines;
|
|
9262
|
-
const wasByteTruncated = byteCount > maxBytes;
|
|
9263
|
-
if (!wasLineTruncated && !wasByteTruncated) {
|
|
9264
|
-
return { content: trimmed, lineCount, byteCount, wasLineTruncated, wasByteTruncated };
|
|
9265
|
-
}
|
|
9266
|
-
let truncated = wasLineTruncated ? contentLines.slice(0, maxLines).join("\n") : trimmed;
|
|
9267
|
-
if (truncated.length > maxBytes) {
|
|
9268
|
-
const cutAt = truncated.lastIndexOf("\n", maxBytes);
|
|
9269
|
-
truncated = truncated.slice(0, cutAt > 0 ? cutAt : maxBytes);
|
|
9270
|
-
}
|
|
9271
|
-
const reason = wasByteTruncated && !wasLineTruncated ? `${byteCount} bytes (limit: ${maxBytes})` : wasLineTruncated && !wasByteTruncated ? `${lineCount} lines (limit: ${maxLines})` : `${lineCount} lines and ${byteCount} bytes`;
|
|
9272
|
-
return {
|
|
9273
|
-
content: truncated + `
|
|
9274
|
-
|
|
9275
|
-
> WARNING: ${INDEX_NAME} is ${reason}. Only part of it was loaded. Keep index entries to one line under ~200 chars; move detail into topic files.`,
|
|
9276
|
-
lineCount,
|
|
9277
|
-
byteCount,
|
|
9278
|
-
wasLineTruncated,
|
|
9279
|
-
wasByteTruncated
|
|
9280
|
-
};
|
|
9281
|
-
}
|
|
9282
|
-
var FileMemoryProvider = class {
|
|
9283
|
-
fs;
|
|
9284
|
-
dir;
|
|
9285
|
-
maxIndexLines;
|
|
9286
|
-
constructor(fs2, memoryDir, maxIndexLines = DEFAULT_MAX_LINES) {
|
|
9287
|
-
this.fs = fs2;
|
|
9288
|
-
this.dir = memoryDir.endsWith("/") ? memoryDir : memoryDir + "/";
|
|
9289
|
-
this.maxIndexLines = maxIndexLines;
|
|
9290
|
-
}
|
|
9291
|
-
indexPath() {
|
|
9292
|
-
return this.dir + INDEX_NAME;
|
|
9293
|
-
}
|
|
9294
|
-
async ensureDir() {
|
|
9295
|
-
const exists = await this.fs.exists(this.dir);
|
|
9296
|
-
if (!exists) {
|
|
9297
|
-
await this.fs.mkdir(this.dir, { recursive: true });
|
|
9298
|
-
}
|
|
9299
|
-
}
|
|
9300
|
-
async loadIndex() {
|
|
9301
|
-
try {
|
|
9302
|
-
const raw = await this.fs.readFile(this.indexPath());
|
|
9303
|
-
return truncateIndex(raw, this.maxIndexLines).content;
|
|
9304
|
-
} catch {
|
|
9305
|
-
return "";
|
|
9306
|
-
}
|
|
9307
|
-
}
|
|
9308
|
-
async loadEntry(path2) {
|
|
9309
|
-
const fullPath = path2.startsWith(this.dir) ? path2 : this.dir + path2;
|
|
9310
|
-
try {
|
|
9311
|
-
const raw = await this.fs.readFile(fullPath);
|
|
9312
|
-
const fm = parseFrontmatter2(raw);
|
|
9313
|
-
const stat2 = await this.fs.stat(fullPath).catch(() => null);
|
|
9314
|
-
return {
|
|
9315
|
-
name: fm.name ?? pathToName(path2),
|
|
9316
|
-
description: fm.description ?? "",
|
|
9317
|
-
type: fm.type ?? "project",
|
|
9318
|
-
content: fm.rest,
|
|
9319
|
-
path: path2.startsWith(this.dir) ? path2.slice(this.dir.length) : path2,
|
|
9320
|
-
updatedAt: stat2?.modifiedAt?.toISOString()
|
|
9321
|
-
};
|
|
9322
|
-
} catch {
|
|
9323
|
-
return null;
|
|
9324
|
-
}
|
|
9325
|
-
}
|
|
9326
|
-
async saveEntry(entry) {
|
|
9327
|
-
await this.ensureDir();
|
|
9328
|
-
const relativePath = entry.path ?? slugify(entry.name) + ".md";
|
|
9329
|
-
const fullPath = this.dir + relativePath;
|
|
9330
|
-
const content = serializeEntry({ ...entry, path: relativePath });
|
|
9331
|
-
await this.fs.writeFile(fullPath, content);
|
|
9332
|
-
await this.rebuildIndex();
|
|
9333
|
-
}
|
|
9334
|
-
async removeEntry(path2) {
|
|
9335
|
-
const fullPath = path2.startsWith(this.dir) ? path2 : this.dir + path2;
|
|
9336
|
-
try {
|
|
9337
|
-
await this.fs.deleteFile(fullPath);
|
|
9338
|
-
} catch {
|
|
9339
|
-
}
|
|
9340
|
-
await this.rebuildIndex();
|
|
9341
|
-
}
|
|
9342
|
-
async listEntries() {
|
|
9343
|
-
try {
|
|
9344
|
-
const files = await this.fs.readdir(this.dir);
|
|
9345
|
-
const entries = [];
|
|
9346
|
-
for (const file of files) {
|
|
9347
|
-
if (!file.isFile || !file.name.endsWith(".md") || file.name === INDEX_NAME) continue;
|
|
9348
|
-
const entry = await this.loadEntry(file.name);
|
|
9349
|
-
if (entry) entries.push(entry);
|
|
9350
|
-
}
|
|
9351
|
-
return entries;
|
|
9352
|
-
} catch {
|
|
9353
|
-
return [];
|
|
9354
|
-
}
|
|
9355
|
-
}
|
|
9356
|
-
async search(query) {
|
|
9357
|
-
const entries = await this.listEntries();
|
|
9358
|
-
const lower = query.toLowerCase();
|
|
9359
|
-
return entries.filter(
|
|
9360
|
-
(e) => e.name.toLowerCase().includes(lower) || e.description.toLowerCase().includes(lower) || e.content.toLowerCase().includes(lower)
|
|
9361
|
-
);
|
|
9362
|
-
}
|
|
9363
|
-
async rebuildIndex() {
|
|
9364
|
-
const entries = await this.listEntries();
|
|
9365
|
-
const lines = [];
|
|
9366
|
-
for (const entry of entries) {
|
|
9367
|
-
const relativePath = entry.path ?? slugify(entry.name) + ".md";
|
|
9368
|
-
const desc = entry.description ? ` \u2014 ${entry.description}` : "";
|
|
9369
|
-
lines.push(`- [${entry.name}](${relativePath})${desc}`);
|
|
9370
|
-
}
|
|
9371
|
-
await this.ensureDir();
|
|
9372
|
-
await this.fs.writeFile(this.indexPath(), lines.join("\n") + "\n");
|
|
9373
|
-
}
|
|
9374
|
-
};
|
|
9375
|
-
function pathToName(p) {
|
|
9376
|
-
const base = p.split("/").pop() ?? p;
|
|
9377
|
-
return base.replace(/\.md$/i, "").replace(/[_-]/g, " ");
|
|
9378
|
-
}
|
|
9379
|
-
|
|
9380
8600
|
export {
|
|
9381
|
-
LocalFs,
|
|
9382
|
-
LocalComputer,
|
|
9383
|
-
SandboxedLocalComputer,
|
|
9384
|
-
UnsandboxedLocal,
|
|
9385
|
-
LocalSandbox,
|
|
9386
8601
|
DEFAULT_AUTO_TITLE_MAX_INPUT_CHARS,
|
|
9387
8602
|
DEFAULT_AUTO_TITLE_SYSTEM_PROMPT,
|
|
9388
8603
|
extractTitleSeedText,
|
|
@@ -9444,6 +8659,10 @@ export {
|
|
|
9444
8659
|
tokenCountWithEstimation,
|
|
9445
8660
|
groupMessagesByTurn,
|
|
9446
8661
|
truncateHeadForPTLRetry,
|
|
8662
|
+
registerContextWindows,
|
|
8663
|
+
getContextWindowForModel,
|
|
8664
|
+
getEffectiveContextWindow,
|
|
8665
|
+
getAutoCompactThreshold,
|
|
9447
8666
|
classifyError,
|
|
9448
8667
|
isRetryable,
|
|
9449
8668
|
compactConversation,
|
|
@@ -9488,17 +8707,6 @@ export {
|
|
|
9488
8707
|
Thread,
|
|
9489
8708
|
loadSkills,
|
|
9490
8709
|
DEFAULT_RETRY_CONFIG,
|
|
9491
|
-
Agent
|
|
9492
|
-
codingAgent,
|
|
9493
|
-
planningAgent,
|
|
9494
|
-
reviewAgent,
|
|
9495
|
-
Mailbox,
|
|
9496
|
-
SwarmManager,
|
|
9497
|
-
InProcessBackend,
|
|
9498
|
-
applyPermissionUpdate,
|
|
9499
|
-
applyPermissionUpdates,
|
|
9500
|
-
OTelTracer,
|
|
9501
|
-
truncateIndex,
|
|
9502
|
-
FileMemoryProvider
|
|
8710
|
+
Agent
|
|
9503
8711
|
};
|
|
9504
|
-
//# sourceMappingURL=chunk-
|
|
8712
|
+
//# sourceMappingURL=chunk-5HY4IYNT.js.map
|