@wrongstack/core 0.275.1 → 0.276.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-D9JkPvJ0.d.ts → agent-bridge-D7A-eu3C.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CArSFKFl.d.ts → agent-subagent-runner-CEuw4ATz.d.ts} +16 -10
- package/dist/{brain-DCkB5_e7.d.ts → brain-BLOyN5ZP.d.ts} +127 -1
- package/dist/{compactor-CzSvxM1g.d.ts → compactor-DcBpaJsI.d.ts} +1 -1
- package/dist/{config-BzFRKkg7.d.ts → config-Bf5mj-ad.d.ts} +20 -2
- package/dist/{context-BrLe8pJy.d.ts → context-CLnUMW5g.d.ts} +40 -2
- package/dist/coordination/index.d.ts +43 -24
- package/dist/coordination/index.js +849 -648
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +1636 -845
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +218 -49
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +7 -7
- package/dist/extension/index.js.map +1 -1
- package/dist/{global-mailbox-CXkugtNQ.d.ts → global-mailbox-Iqfkgmwu.d.ts} +3 -3
- package/dist/{goal-store-DUwdbdoY.d.ts → goal-store-DGb6b5Ed.d.ts} +1 -1
- package/dist/hq/index.d.ts +6 -6
- package/dist/hq/index.js +178 -75
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-CtlizLTK.d.ts → index-Cn0NOshr.d.ts} +10 -5
- package/dist/{index-neOCEy6q.d.ts → index-L4RZN9jJ.d.ts} +2 -2
- package/dist/index.d.ts +56 -48
- package/dist/index.js +2789 -1546
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +26 -7
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +20 -12
- package/dist/kernel/index.js +55 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mailbox-types-_7gaY0Rl.d.ts → mailbox-types-DTl7bRH3.d.ts} +3 -1
- package/dist/{mcp-servers-MLL6bMlv.d.ts → mcp-servers-CuZGf9fI.d.ts} +4 -4
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +223 -139
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-CrkcxQ-g.d.ts → models-registry-8XOdxWQu.d.ts} +16 -1
- package/dist/{multi-agent-coordinator-Dc_HuG9p.d.ts → multi-agent-coordinator-CiRtKVTk.d.ts} +8 -1
- package/dist/{null-fleet-bus-BMZwMin7.d.ts → null-fleet-bus-d9G-bVy9.d.ts} +26 -22
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-uVK4BatM.d.ts → path-resolver-BhIb6mtd.d.ts} +8 -3
- package/dist/{permission-CJR1qfOi.d.ts → permission-BCbQDR2s.d.ts} +1 -1
- package/dist/{permission-policy-DLVKKk4w.d.ts → permission-policy-C0ikndX_.d.ts} +2 -18
- package/dist/{pipeline-BYR-Vdau.d.ts → pipeline-Dl6XbfE7.d.ts} +10 -6
- package/dist/{provider-model-resolve-iREK_1lG.d.ts → provider-model-resolve-B70epO19.d.ts} +3 -3
- package/dist/{provider-runner-i7SQXZuC.d.ts → provider-runner-DZ808MSM.d.ts} +3 -3
- package/dist/{retry-policy-BmY5ooh3.d.ts → retry-policy-Dt3_z8Aj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +19 -10
- package/dist/sdd/index.js +411 -240
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-C9leEMzr.d.ts → secret-vault-BUJ2d1gB.d.ts} +1 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +30 -6
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-qjpee9BF.d.ts → selector-BCkWgdwy.d.ts} +1 -1
- package/dist/{session-event-bridge-m7y--I-H.d.ts → session-event-bridge-CMvIO59_.d.ts} +1 -1
- package/dist/{session-reader-BjLH4V9n.d.ts → session-reader-C8aiChUu.d.ts} +1 -1
- package/dist/skills/index.js +1 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +68 -30
- package/dist/storage/index.js +839 -528
- package/dist/storage/index.js.map +1 -1
- package/dist/{strategy-compactor-C2bmlWYg.d.ts → strategy-compactor-DI1OHVbB.d.ts} +10 -10
- package/dist/{todos-checkpoint-oDS9IBNS.d.ts → todos-checkpoint-Ddd2CGr0.d.ts} +56 -9
- package/dist/{tool-executor-D4YdaJ-M.d.ts → tool-executor-Bmd5Ygoo.d.ts} +45 -10
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -20
- package/dist/types/index.js +331 -98
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +16 -3
- package/dist/utils/index.js +159 -83
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-A1Efnvs0.d.ts → worktree-manager-DBdl_5rs.d.ts} +4 -1
- package/instructions/agents/shadow-agent.md +3 -3
- package/instructions/coordination/director-preamble.md +3 -3
- package/instructions/modes/research-web.md +4 -4
- package/package.json +1 -1
- package/skills/research-web/SKILL.md +26 -26
- package/skills/research-web/SKILL.save.md +1 -1
package/dist/storage/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as syncFs from 'fs';
|
|
1
2
|
import { createReadStream, readFileSync, statSync } from 'fs';
|
|
2
3
|
import * as fsp2 from 'fs/promises';
|
|
3
4
|
import * as path2 from 'path';
|
|
@@ -8,123 +9,107 @@ import { hostname } from 'os';
|
|
|
8
9
|
import { fileURLToPath } from 'url';
|
|
9
10
|
|
|
10
11
|
// src/storage/session-store.ts
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (typeof content === "string") {
|
|
17
|
-
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
18
|
-
} else {
|
|
19
|
-
await fsp2.writeFile(tmp, content, { flag: "wx" });
|
|
20
|
-
}
|
|
21
|
-
try {
|
|
22
|
-
const fh = await fsp2.open(tmp, "r+");
|
|
23
|
-
try {
|
|
24
|
-
await fh.sync();
|
|
25
|
-
} finally {
|
|
26
|
-
await fh.close();
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
let mode;
|
|
31
|
-
try {
|
|
32
|
-
const stat10 = await fsp2.stat(targetPath);
|
|
33
|
-
mode = stat10.mode & 511;
|
|
34
|
-
} catch {
|
|
35
|
-
mode = opts.mode;
|
|
36
|
-
}
|
|
37
|
-
if (mode !== void 0) {
|
|
38
|
-
await fsp2.chmod(tmp, mode);
|
|
39
|
-
}
|
|
40
|
-
await renameWithRetry(tmp, targetPath);
|
|
41
|
-
if (mode !== void 0 && process.platform === "win32") {
|
|
42
|
-
try {
|
|
43
|
-
await fsp2.chmod(targetPath, mode);
|
|
44
|
-
} catch {
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
} catch (err) {
|
|
48
|
-
try {
|
|
49
|
-
await fsp2.unlink(tmp);
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
52
|
-
throw err;
|
|
53
|
-
}
|
|
12
|
+
|
|
13
|
+
// src/utils/term.ts
|
|
14
|
+
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
15
|
+
function isStdoutTTY() {
|
|
16
|
+
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
54
17
|
}
|
|
55
|
-
|
|
56
|
-
|
|
18
|
+
|
|
19
|
+
// src/utils/color.ts
|
|
20
|
+
var isColorTty = () => {
|
|
21
|
+
if (envFlag(process.env.NO_COLOR)) return false;
|
|
22
|
+
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
23
|
+
return isStdoutTTY();
|
|
24
|
+
};
|
|
25
|
+
function envFlag(value) {
|
|
26
|
+
if (value === void 0) return false;
|
|
27
|
+
if (value.trim() === "") return false;
|
|
28
|
+
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
57
29
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
30
|
+
var COLOR = isColorTty();
|
|
31
|
+
var wrap = (open8, close) => (s) => COLOR ? `\x1B[${open8}m${s}\x1B[${close}m` : s;
|
|
32
|
+
var color = {
|
|
33
|
+
reset: wrap("0", "0"),
|
|
34
|
+
bold: wrap("1", "22"),
|
|
35
|
+
dim: wrap("2", "22"),
|
|
36
|
+
italic: wrap("3", "23"),
|
|
37
|
+
underline: wrap("4", "24"),
|
|
38
|
+
red: wrap("31", "39"),
|
|
39
|
+
green: wrap("32", "39"),
|
|
40
|
+
yellow: wrap("33", "39"),
|
|
41
|
+
blue: wrap("34", "39"),
|
|
42
|
+
magenta: wrap("35", "39"),
|
|
43
|
+
cyan: wrap("36", "39"),
|
|
44
|
+
gray: wrap("90", "39"),
|
|
45
|
+
amber: wrap("38;5;214", "39"),
|
|
46
|
+
pink: wrap("38;5;205", "39"),
|
|
47
|
+
bgRed: wrap("41", "49"),
|
|
48
|
+
bgGreen: wrap("42", "49")
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/utils/deep-merge.ts
|
|
52
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
53
|
+
"__proto__",
|
|
54
|
+
"constructor",
|
|
55
|
+
"prototype",
|
|
56
|
+
"__defineGetter__",
|
|
57
|
+
"__defineSetter__",
|
|
58
|
+
"__lookupGetter__",
|
|
59
|
+
"__lookupSetter__"
|
|
60
|
+
]);
|
|
61
|
+
function isPrimitiveArray(a) {
|
|
62
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
63
|
+
}
|
|
64
|
+
function deepMerge(base, patch, options = {}) {
|
|
65
|
+
const {
|
|
66
|
+
conflictResolution = "prefer-patch",
|
|
67
|
+
arrayMode = "replace",
|
|
68
|
+
protectProto = true,
|
|
69
|
+
onNonPrimitiveArrayReplace
|
|
70
|
+
} = options;
|
|
71
|
+
if (typeof base !== "object" || base === null) {
|
|
72
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
92
73
|
}
|
|
93
|
-
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
await fsp2.unlink(lockPath);
|
|
102
|
-
} catch {
|
|
74
|
+
if (typeof patch !== "object" || patch === null) {
|
|
75
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
78
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
79
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
103
80
|
}
|
|
81
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
104
82
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
async function renameWithRetry(from, to) {
|
|
108
|
-
if (process.platform !== "win32") {
|
|
109
|
-
await fsp2.rename(from, to);
|
|
110
|
-
return;
|
|
83
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
84
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
111
85
|
}
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
86
|
+
const baseObj = base;
|
|
87
|
+
const patchObj = patch;
|
|
88
|
+
const out = { ...baseObj };
|
|
89
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
90
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
91
|
+
const existing = out[k];
|
|
92
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
93
|
+
out[k] = deepMerge(existing, v, options);
|
|
94
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
95
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
96
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
123
97
|
}
|
|
124
|
-
|
|
98
|
+
out[k] = deepMerge(existing, v, options);
|
|
99
|
+
} else if (v !== void 0) {
|
|
100
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
101
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
102
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
103
|
+
}
|
|
104
|
+
out[k] = v;
|
|
125
105
|
}
|
|
126
106
|
}
|
|
127
|
-
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/utils/error.ts
|
|
111
|
+
function toErrorMessage(err) {
|
|
112
|
+
return err instanceof Error ? err.message : String(err);
|
|
128
113
|
}
|
|
129
114
|
|
|
130
115
|
// src/utils/expect-defined.ts
|
|
@@ -229,108 +214,6 @@ function isEmptyMessage(msg) {
|
|
|
229
214
|
return msg.content.length === 0;
|
|
230
215
|
}
|
|
231
216
|
|
|
232
|
-
// src/utils/term.ts
|
|
233
|
-
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
234
|
-
function isStdoutTTY() {
|
|
235
|
-
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// src/utils/color.ts
|
|
239
|
-
var isColorTty = () => {
|
|
240
|
-
if (envFlag(process.env.NO_COLOR)) return false;
|
|
241
|
-
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
242
|
-
return isStdoutTTY();
|
|
243
|
-
};
|
|
244
|
-
function envFlag(value) {
|
|
245
|
-
if (value === void 0) return false;
|
|
246
|
-
if (value.trim() === "") return false;
|
|
247
|
-
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
248
|
-
}
|
|
249
|
-
var COLOR = isColorTty();
|
|
250
|
-
var wrap = (open8, close) => (s) => COLOR ? `\x1B[${open8}m${s}\x1B[${close}m` : s;
|
|
251
|
-
var color = {
|
|
252
|
-
reset: wrap("0", "0"),
|
|
253
|
-
bold: wrap("1", "22"),
|
|
254
|
-
dim: wrap("2", "22"),
|
|
255
|
-
italic: wrap("3", "23"),
|
|
256
|
-
underline: wrap("4", "24"),
|
|
257
|
-
red: wrap("31", "39"),
|
|
258
|
-
green: wrap("32", "39"),
|
|
259
|
-
yellow: wrap("33", "39"),
|
|
260
|
-
blue: wrap("34", "39"),
|
|
261
|
-
magenta: wrap("35", "39"),
|
|
262
|
-
cyan: wrap("36", "39"),
|
|
263
|
-
gray: wrap("90", "39"),
|
|
264
|
-
amber: wrap("38;5;214", "39"),
|
|
265
|
-
pink: wrap("38;5;205", "39"),
|
|
266
|
-
bgRed: wrap("41", "49"),
|
|
267
|
-
bgGreen: wrap("42", "49")
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// src/utils/deep-merge.ts
|
|
271
|
-
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
272
|
-
"__proto__",
|
|
273
|
-
"constructor",
|
|
274
|
-
"prototype",
|
|
275
|
-
"__defineGetter__",
|
|
276
|
-
"__defineSetter__",
|
|
277
|
-
"__lookupGetter__",
|
|
278
|
-
"__lookupSetter__"
|
|
279
|
-
]);
|
|
280
|
-
function isPrimitiveArray(a) {
|
|
281
|
-
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
282
|
-
}
|
|
283
|
-
function deepMerge(base, patch, options = {}) {
|
|
284
|
-
const {
|
|
285
|
-
conflictResolution = "prefer-patch",
|
|
286
|
-
arrayMode = "replace",
|
|
287
|
-
protectProto = true,
|
|
288
|
-
onNonPrimitiveArrayReplace
|
|
289
|
-
} = options;
|
|
290
|
-
if (typeof base !== "object" || base === null) {
|
|
291
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
292
|
-
}
|
|
293
|
-
if (typeof patch !== "object" || patch === null) {
|
|
294
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
295
|
-
}
|
|
296
|
-
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
297
|
-
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
298
|
-
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
299
|
-
}
|
|
300
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
301
|
-
}
|
|
302
|
-
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
303
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
304
|
-
}
|
|
305
|
-
const baseObj = base;
|
|
306
|
-
const patchObj = patch;
|
|
307
|
-
const out = { ...baseObj };
|
|
308
|
-
for (const [k, v] of Object.entries(patchObj)) {
|
|
309
|
-
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
310
|
-
const existing = out[k];
|
|
311
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
312
|
-
out[k] = deepMerge(existing, v, options);
|
|
313
|
-
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
314
|
-
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
315
|
-
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
316
|
-
}
|
|
317
|
-
out[k] = deepMerge(existing, v, options);
|
|
318
|
-
} else if (v !== void 0) {
|
|
319
|
-
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
320
|
-
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
321
|
-
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
322
|
-
}
|
|
323
|
-
out[k] = v;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return out;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// src/utils/error.ts
|
|
330
|
-
function toErrorMessage(err) {
|
|
331
|
-
return err instanceof Error ? err.message : String(err);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
217
|
// src/utils/regex-guard.ts
|
|
335
218
|
var MAX_PATTERN_LEN = 512;
|
|
336
219
|
var DANGEROUS_PATTERNS = [
|
|
@@ -381,6 +264,25 @@ function safeParse(input, maxBytes = 5e6) {
|
|
|
381
264
|
};
|
|
382
265
|
}
|
|
383
266
|
}
|
|
267
|
+
function sessionScopedPath(dir, sessionId, suffix) {
|
|
268
|
+
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
269
|
+
throw invalid(sessionId);
|
|
270
|
+
}
|
|
271
|
+
const resolved = path2.resolve(dir, `${sessionId}${suffix}`);
|
|
272
|
+
const rel = path2.relative(path2.resolve(dir), resolved);
|
|
273
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
274
|
+
throw invalid(sessionId);
|
|
275
|
+
}
|
|
276
|
+
return resolved;
|
|
277
|
+
}
|
|
278
|
+
function invalid(sessionId) {
|
|
279
|
+
return new FsError({
|
|
280
|
+
message: `Invalid sessionId: ${sessionId}`,
|
|
281
|
+
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
282
|
+
path: sessionId,
|
|
283
|
+
context: { reason: "path_traversal" }
|
|
284
|
+
});
|
|
285
|
+
}
|
|
384
286
|
|
|
385
287
|
// src/utils/slug.ts
|
|
386
288
|
function slugify(name, fallback = "prompt", maxLen = 64) {
|
|
@@ -476,6 +378,222 @@ function resolveWstackPaths(opts) {
|
|
|
476
378
|
};
|
|
477
379
|
}
|
|
478
380
|
|
|
381
|
+
// src/types/errors.ts
|
|
382
|
+
var ERROR_CODES = {
|
|
383
|
+
// Config
|
|
384
|
+
CONFIG_INVALID: "CONFIG_INVALID",
|
|
385
|
+
// Session
|
|
386
|
+
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
387
|
+
SESSION_CORRUPTED: "SESSION_CORRUPTED",
|
|
388
|
+
SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
|
|
389
|
+
// File system
|
|
390
|
+
FS_READ_FAILED: "FS_READ_FAILED",
|
|
391
|
+
FS_DELETE_FAILED: "FS_DELETE_FAILED",
|
|
392
|
+
FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
|
|
393
|
+
// General
|
|
394
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
395
|
+
UNKNOWN: "UNKNOWN"
|
|
396
|
+
};
|
|
397
|
+
var WrongStackError = class extends Error {
|
|
398
|
+
code;
|
|
399
|
+
subsystem;
|
|
400
|
+
severity;
|
|
401
|
+
recoverable;
|
|
402
|
+
context;
|
|
403
|
+
constructor(opts) {
|
|
404
|
+
super(opts.message, { cause: opts.cause });
|
|
405
|
+
this.name = "WrongStackError";
|
|
406
|
+
this.code = opts.code;
|
|
407
|
+
this.subsystem = opts.subsystem;
|
|
408
|
+
this.severity = opts.severity ?? "error";
|
|
409
|
+
this.recoverable = opts.recoverable ?? false;
|
|
410
|
+
this.context = opts.context;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Render a one-line user-facing description.
|
|
414
|
+
* Subclasses should override for domain-specific formatting.
|
|
415
|
+
*/
|
|
416
|
+
describe() {
|
|
417
|
+
const ctx = this.context ? ` ${formatContext(this.context)}` : "";
|
|
418
|
+
return `${this.code}: ${this.message}${ctx}`;
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
function formatContext(ctx) {
|
|
422
|
+
const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
|
|
423
|
+
return parts.length > 0 ? `[${parts.join(" ")}]` : "";
|
|
424
|
+
}
|
|
425
|
+
var ConfigError = class extends WrongStackError {
|
|
426
|
+
constructor(opts) {
|
|
427
|
+
super({
|
|
428
|
+
message: opts.message,
|
|
429
|
+
code: opts.code,
|
|
430
|
+
subsystem: "config",
|
|
431
|
+
severity: "fatal",
|
|
432
|
+
recoverable: false,
|
|
433
|
+
context: opts.context,
|
|
434
|
+
cause: opts.cause
|
|
435
|
+
});
|
|
436
|
+
this.name = "ConfigError";
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
var SessionError = class extends WrongStackError {
|
|
440
|
+
sessionId;
|
|
441
|
+
constructor(opts) {
|
|
442
|
+
super({
|
|
443
|
+
message: opts.message,
|
|
444
|
+
code: opts.code,
|
|
445
|
+
subsystem: "session",
|
|
446
|
+
severity: opts.code === ERROR_CODES.SESSION_WRITE_FAILED ? "error" : "warning",
|
|
447
|
+
recoverable: opts.code !== ERROR_CODES.SESSION_CORRUPTED,
|
|
448
|
+
context: { sessionId: opts.sessionId, ...opts.context },
|
|
449
|
+
cause: opts.cause
|
|
450
|
+
});
|
|
451
|
+
this.name = "SessionError";
|
|
452
|
+
this.sessionId = opts.sessionId;
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
var FsError = class extends WrongStackError {
|
|
456
|
+
path;
|
|
457
|
+
constructor(opts) {
|
|
458
|
+
super({
|
|
459
|
+
message: opts.message,
|
|
460
|
+
code: opts.code,
|
|
461
|
+
subsystem: "fs",
|
|
462
|
+
severity: "error",
|
|
463
|
+
recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
|
|
464
|
+
context: { path: opts.path, ...opts.context },
|
|
465
|
+
cause: opts.cause
|
|
466
|
+
});
|
|
467
|
+
this.name = "FsError";
|
|
468
|
+
this.path = opts.path;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// src/utils/atomic-write.ts
|
|
473
|
+
async function atomicWrite(targetPath, content, opts = {}) {
|
|
474
|
+
const dir = path2.dirname(targetPath);
|
|
475
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
476
|
+
const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
477
|
+
try {
|
|
478
|
+
if (typeof content === "string") {
|
|
479
|
+
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
480
|
+
} else {
|
|
481
|
+
await fsp2.writeFile(tmp, content, { flag: "wx" });
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const fh = await fsp2.open(tmp, "r+");
|
|
485
|
+
try {
|
|
486
|
+
await fh.sync();
|
|
487
|
+
} finally {
|
|
488
|
+
await fh.close();
|
|
489
|
+
}
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
492
|
+
let mode;
|
|
493
|
+
try {
|
|
494
|
+
const stat11 = await fsp2.stat(targetPath);
|
|
495
|
+
mode = stat11.mode & 511;
|
|
496
|
+
} catch {
|
|
497
|
+
mode = opts.mode;
|
|
498
|
+
}
|
|
499
|
+
if (mode !== void 0) {
|
|
500
|
+
await fsp2.chmod(tmp, mode);
|
|
501
|
+
}
|
|
502
|
+
await renameWithRetry(tmp, targetPath);
|
|
503
|
+
if (mode !== void 0 && process.platform === "win32") {
|
|
504
|
+
try {
|
|
505
|
+
await fsp2.chmod(targetPath, mode);
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
} catch (err) {
|
|
510
|
+
try {
|
|
511
|
+
await fsp2.unlink(tmp);
|
|
512
|
+
} catch {
|
|
513
|
+
}
|
|
514
|
+
throw err;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async function ensureDir(dir) {
|
|
518
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
519
|
+
}
|
|
520
|
+
async function withFileLock(targetPath, fn, opts = {}) {
|
|
521
|
+
const dir = path2.dirname(targetPath);
|
|
522
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
523
|
+
const lockPath = path2.join(dir, `.${path2.basename(targetPath)}.lock`);
|
|
524
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
525
|
+
const staleMs = opts.staleMs ?? 3e4;
|
|
526
|
+
const started = Date.now();
|
|
527
|
+
let handle;
|
|
528
|
+
for (; ; ) {
|
|
529
|
+
try {
|
|
530
|
+
handle = await fsp2.open(lockPath, "wx");
|
|
531
|
+
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
532
|
+
break;
|
|
533
|
+
} catch (err) {
|
|
534
|
+
const code = err.code;
|
|
535
|
+
if (code === "ENOENT") {
|
|
536
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
if (code !== "EEXIST") throw err;
|
|
540
|
+
try {
|
|
541
|
+
const stat11 = await fsp2.stat(lockPath);
|
|
542
|
+
if (Date.now() - stat11.mtimeMs > staleMs) {
|
|
543
|
+
await fsp2.unlink(lockPath);
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
} catch {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (Date.now() - started >= timeoutMs) {
|
|
550
|
+
throw new FsError({
|
|
551
|
+
message: `Timed out waiting for file lock: ${targetPath}`,
|
|
552
|
+
code: "FS_ATOMIC_WRITE_FAILED",
|
|
553
|
+
path: targetPath,
|
|
554
|
+
context: { timeoutMs }
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
await new Promise((resolve8) => setTimeout(resolve8, 25));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
return await fn();
|
|
562
|
+
} finally {
|
|
563
|
+
try {
|
|
564
|
+
await handle?.close();
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
try {
|
|
568
|
+
await fsp2.unlink(lockPath);
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
574
|
+
async function renameWithRetry(from, to) {
|
|
575
|
+
if (process.platform !== "win32") {
|
|
576
|
+
await fsp2.rename(from, to);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
const delays = [10, 25, 60, 120, 250];
|
|
580
|
+
let lastErr;
|
|
581
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
582
|
+
try {
|
|
583
|
+
await fsp2.rename(from, to);
|
|
584
|
+
return;
|
|
585
|
+
} catch (err) {
|
|
586
|
+
lastErr = err;
|
|
587
|
+
const code = err?.code;
|
|
588
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
589
|
+
throw err;
|
|
590
|
+
}
|
|
591
|
+
await new Promise((resolve8) => setTimeout(resolve8, delays[i]));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
throw lastErr;
|
|
595
|
+
}
|
|
596
|
+
|
|
479
597
|
// src/storage/session-helpers.ts
|
|
480
598
|
function userInputTitle(content) {
|
|
481
599
|
const text = typeof content === "string" ? content : content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
@@ -860,6 +978,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
860
978
|
promptPreview
|
|
861
979
|
});
|
|
862
980
|
this.events?.emit("checkpoint.written", {
|
|
981
|
+
sessionId: this.id,
|
|
863
982
|
promptIndex,
|
|
864
983
|
promptPreview,
|
|
865
984
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1029,6 +1148,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1029
1148
|
revertedFiles: []
|
|
1030
1149
|
});
|
|
1031
1150
|
this.events?.emit("session.rewound", {
|
|
1151
|
+
sessionId: this.id,
|
|
1032
1152
|
toPromptIndex: targetPromptIndex,
|
|
1033
1153
|
revertedFiles: [],
|
|
1034
1154
|
removedEvents: removedCount
|
|
@@ -1068,7 +1188,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1068
1188
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1069
1189
|
context
|
|
1070
1190
|
});
|
|
1071
|
-
this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1191
|
+
this.events?.emit("in_flight.started", { sessionId: this.id, context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1072
1192
|
}
|
|
1073
1193
|
/**
|
|
1074
1194
|
* Close the in-flight marker. Idempotent in spirit
|
|
@@ -1083,18 +1203,18 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1083
1203
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1084
1204
|
reason
|
|
1085
1205
|
});
|
|
1086
|
-
this.events?.emit("in_flight.ended", { reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1206
|
+
this.events?.emit("in_flight.ended", { sessionId: this.id, reason, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1087
1207
|
}
|
|
1088
1208
|
};
|
|
1209
|
+
|
|
1210
|
+
// src/storage/session-id.ts
|
|
1089
1211
|
function sanitizeModel(model) {
|
|
1090
1212
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
1091
1213
|
}
|
|
1092
|
-
function generateSessionId(startedAt,
|
|
1214
|
+
function generateSessionId(startedAt, _model) {
|
|
1093
1215
|
const date = startedAt.slice(0, 10);
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
1097
|
-
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
1216
|
+
const seedTime = Number.isNaN(Date.parse(startedAt)) ? Date.now() : Date.parse(startedAt);
|
|
1217
|
+
return `${date}/sess_${ulid(seedTime)}`;
|
|
1098
1218
|
}
|
|
1099
1219
|
|
|
1100
1220
|
// src/storage/session-summary.ts
|
|
@@ -1229,7 +1349,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1229
1349
|
}
|
|
1230
1350
|
/** Join session ID to its absolute path within the store directory. */
|
|
1231
1351
|
sessionPath(id, ext) {
|
|
1232
|
-
return
|
|
1352
|
+
return sessionScopedPath(this.dir, id, ext);
|
|
1233
1353
|
}
|
|
1234
1354
|
shardManifestPath(shardKey) {
|
|
1235
1355
|
return shardKey ? path2.join(this.dir, shardKey, "_manifest.json") : path2.join(this.dir, "_manifest.json");
|
|
@@ -1247,15 +1367,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1247
1367
|
* subdirectory so sessions group naturally by day.
|
|
1248
1368
|
*/
|
|
1249
1369
|
async ensureShardDir(id) {
|
|
1250
|
-
const dirPath = path2.dirname(
|
|
1370
|
+
const dirPath = path2.dirname(sessionScopedPath(this.dir, id, ""));
|
|
1251
1371
|
await ensureDir(dirPath);
|
|
1252
1372
|
return dirPath;
|
|
1253
1373
|
}
|
|
1254
1374
|
async create(meta) {
|
|
1255
1375
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1256
|
-
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt
|
|
1376
|
+
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt);
|
|
1257
1377
|
const shardDir = await this.ensureShardDir(id);
|
|
1258
|
-
const file =
|
|
1378
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
1259
1379
|
const t0 = Date.now();
|
|
1260
1380
|
let handle;
|
|
1261
1381
|
try {
|
|
@@ -1337,6 +1457,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1337
1457
|
}
|
|
1338
1458
|
}
|
|
1339
1459
|
async load(id) {
|
|
1460
|
+
return this.loadInternal(id, { full: true });
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Fast-path loader that skips message reconstruction and adjacency repair.
|
|
1464
|
+
*
|
|
1465
|
+
* Use this for callers that only need the raw event stream + session
|
|
1466
|
+
* metadata — e.g. session listers, analytics, audit, and the TUI's
|
|
1467
|
+
* "events only" views. It avoids the message array build and
|
|
1468
|
+
* repairToolUseAdjacency cost on large session files (a long agent
|
|
1469
|
+
* run can have 50k+ events; rebuilding messages is O(events) and
|
|
1470
|
+
* allocates per-block, so skipping it is a meaningful win).
|
|
1471
|
+
*
|
|
1472
|
+
* The returned data.messages is an empty array; data.toolCallEnds
|
|
1473
|
+
* is computed from the raw events. usage is the sum across all
|
|
1474
|
+
* llm_response events — same as full load().
|
|
1475
|
+
*/
|
|
1476
|
+
async loadEventsOnly(id) {
|
|
1477
|
+
return this.loadInternal(id, { full: false });
|
|
1478
|
+
}
|
|
1479
|
+
async loadInternal(id, mode) {
|
|
1340
1480
|
const file = this.sessionPath(id, ".jsonl");
|
|
1341
1481
|
const t0 = Date.now();
|
|
1342
1482
|
let outcome = "success";
|
|
@@ -1344,93 +1484,113 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1344
1484
|
let cacheHit = false;
|
|
1345
1485
|
try {
|
|
1346
1486
|
const s = await fsp2.stat(file);
|
|
1347
|
-
const
|
|
1487
|
+
const stat11 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
1348
1488
|
const cached = this._loadCache.get(id);
|
|
1349
|
-
if (cached && cached.mtimeMs ===
|
|
1489
|
+
if (cached && cached.mtimeMs === stat11.mtimeMs && cached.size === stat11.size) {
|
|
1350
1490
|
cacheHit = true;
|
|
1351
1491
|
this._loadCache.delete(id);
|
|
1352
1492
|
this._loadCache.set(id, cached);
|
|
1353
|
-
return cached.data;
|
|
1493
|
+
if (mode.full) return cached.data;
|
|
1494
|
+
return { ...cached.data, messages: [] };
|
|
1354
1495
|
}
|
|
1355
|
-
const raw = await fsp2.readFile(file, "utf8");
|
|
1356
|
-
const lines = raw.split("\n").filter((l) => l.trim());
|
|
1357
1496
|
const events = [];
|
|
1358
1497
|
let sessionStartEvent;
|
|
1359
1498
|
let sessionEndEvent;
|
|
1360
1499
|
let sessionModel;
|
|
1361
1500
|
let sessionProvider;
|
|
1362
1501
|
let sessionPendingToolUses;
|
|
1363
|
-
const messages = [];
|
|
1364
|
-
const openToolUses = /* @__PURE__ */ new Set();
|
|
1502
|
+
const messages = mode.full ? [] : void 0;
|
|
1503
|
+
const openToolUses = mode.full ? /* @__PURE__ */ new Set() : void 0;
|
|
1365
1504
|
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
if (ev.type === "user_input") {
|
|
1382
|
-
openToolUses.clear();
|
|
1383
|
-
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
1384
|
-
} else if (ev.type === "llm_response") {
|
|
1385
|
-
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
1386
|
-
for (const b of ev.content) {
|
|
1387
|
-
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
1505
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
1506
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
1507
|
+
try {
|
|
1508
|
+
for await (const line of rl) {
|
|
1509
|
+
if (!line.trim()) continue;
|
|
1510
|
+
try {
|
|
1511
|
+
const parsed = JSON.parse(line);
|
|
1512
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
1513
|
+
const ev = parsed;
|
|
1514
|
+
events.push(ev);
|
|
1515
|
+
if (ev.type === "session_start" && !sessionStartEvent) {
|
|
1516
|
+
sessionStartEvent = ev;
|
|
1517
|
+
sessionModel = ev.model;
|
|
1518
|
+
sessionProvider = ev.provider;
|
|
1388
1519
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
1393
|
-
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
1394
|
-
};
|
|
1395
|
-
} else if (ev.type === "tool_result") {
|
|
1396
|
-
if (!openToolUses.has(ev.id)) {
|
|
1397
|
-
this.events?.emit("session.damaged", {
|
|
1398
|
-
sessionId: id,
|
|
1399
|
-
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
1400
|
-
});
|
|
1401
|
-
continue;
|
|
1520
|
+
if (ev.type === "session_end") {
|
|
1521
|
+
sessionEndEvent = ev;
|
|
1522
|
+
sessionPendingToolUses = ev.pendingToolUses;
|
|
1402
1523
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1524
|
+
if (mode.full && messages !== void 0 && openToolUses !== void 0) {
|
|
1525
|
+
if (ev.type === "user_input") {
|
|
1526
|
+
openToolUses.clear();
|
|
1527
|
+
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
1528
|
+
} else if (ev.type === "llm_response") {
|
|
1529
|
+
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
1530
|
+
for (const b of ev.content) {
|
|
1531
|
+
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
1532
|
+
}
|
|
1533
|
+
usage = {
|
|
1534
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
1535
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
1536
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
1537
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
1538
|
+
};
|
|
1539
|
+
} else if (ev.type === "tool_result") {
|
|
1540
|
+
if (!openToolUses.has(ev.id)) {
|
|
1541
|
+
this.events?.emit("session.damaged", {
|
|
1542
|
+
sessionId: id,
|
|
1543
|
+
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
1544
|
+
});
|
|
1545
|
+
continue;
|
|
1546
|
+
}
|
|
1547
|
+
openToolUses.delete(ev.id);
|
|
1548
|
+
const resultBlock = {
|
|
1549
|
+
type: "tool_result",
|
|
1550
|
+
tool_use_id: ev.id,
|
|
1551
|
+
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
1552
|
+
is_error: ev.isError
|
|
1553
|
+
};
|
|
1554
|
+
const last = messages[messages.length - 1];
|
|
1555
|
+
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
1556
|
+
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
1557
|
+
last.content.push(resultBlock);
|
|
1558
|
+
} else {
|
|
1559
|
+
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
} else if (ev.type === "llm_response") {
|
|
1563
|
+
usage = {
|
|
1564
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
1565
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
1566
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
1567
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
1568
|
+
};
|
|
1416
1569
|
}
|
|
1417
1570
|
}
|
|
1571
|
+
} catch {
|
|
1418
1572
|
}
|
|
1419
|
-
} catch {
|
|
1420
1573
|
}
|
|
1574
|
+
} finally {
|
|
1575
|
+
rl.close();
|
|
1576
|
+
stream.close();
|
|
1421
1577
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1578
|
+
let finalMessages = [];
|
|
1579
|
+
if (mode.full && messages !== void 0 && openToolUses !== void 0) {
|
|
1580
|
+
if (openToolUses.size > 0) {
|
|
1581
|
+
this.events?.emit("session.damaged", {
|
|
1582
|
+
sessionId: id,
|
|
1583
|
+
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
const repaired = repairToolUseAdjacency(messages);
|
|
1587
|
+
if (repaired.report.changed) {
|
|
1588
|
+
this.events?.emit("session.damaged", {
|
|
1589
|
+
sessionId: id,
|
|
1590
|
+
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
finalMessages = repaired.messages;
|
|
1434
1594
|
}
|
|
1435
1595
|
const meta = {
|
|
1436
1596
|
id,
|
|
@@ -1441,27 +1601,29 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1441
1601
|
pendingToolUses: sessionPendingToolUses
|
|
1442
1602
|
};
|
|
1443
1603
|
const toolCallEnds = extractToolCallEnds(events);
|
|
1444
|
-
const data = { metadata: meta, events, messages:
|
|
1445
|
-
if (
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1604
|
+
const data = { metadata: meta, events, messages: finalMessages, usage, toolCallEnds };
|
|
1605
|
+
if (mode.full) {
|
|
1606
|
+
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
1607
|
+
const oldest = this._loadCache.keys().next().value;
|
|
1608
|
+
if (oldest !== void 0) {
|
|
1609
|
+
this._loadCache.delete(oldest);
|
|
1610
|
+
}
|
|
1449
1611
|
}
|
|
1612
|
+
this._loadCache.set(id, { mtimeMs: stat11.mtimeMs, size: stat11.size, data });
|
|
1450
1613
|
}
|
|
1451
|
-
this._loadCache.set(id, { mtimeMs: stat10.mtimeMs, size: stat10.size, data });
|
|
1452
1614
|
return data;
|
|
1453
1615
|
} catch (err) {
|
|
1454
1616
|
outcome = "failure";
|
|
1455
1617
|
errorMsg = toErrorMessage(err);
|
|
1456
1618
|
throw err;
|
|
1457
1619
|
} finally {
|
|
1458
|
-
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
1620
|
+
this.emitRead(id, file, mode.full ? "load" : "load_events_only", outcome, Date.now() - t0, errorMsg);
|
|
1459
1621
|
if (cacheHit) {
|
|
1460
1622
|
this.events?.emit("storage.cache_hit", {
|
|
1461
1623
|
sessionId: id,
|
|
1462
1624
|
store: "session",
|
|
1463
1625
|
filePath: file,
|
|
1464
|
-
operation: "load",
|
|
1626
|
+
operation: mode.full ? "load" : "load_events_only",
|
|
1465
1627
|
durationMs: Date.now() - t0
|
|
1466
1628
|
});
|
|
1467
1629
|
}
|
|
@@ -1489,14 +1651,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1489
1651
|
const limit = opts?.limit;
|
|
1490
1652
|
const signal = opts?.signal;
|
|
1491
1653
|
const out = [];
|
|
1492
|
-
let
|
|
1654
|
+
let stat11;
|
|
1493
1655
|
try {
|
|
1494
|
-
|
|
1656
|
+
stat11 = await fsp2.stat(file);
|
|
1495
1657
|
} catch (err) {
|
|
1496
1658
|
if (err.code === "ENOENT") return [];
|
|
1497
1659
|
throw err;
|
|
1498
1660
|
}
|
|
1499
|
-
if (
|
|
1661
|
+
if (stat11.size === 0) return [];
|
|
1500
1662
|
let fh;
|
|
1501
1663
|
try {
|
|
1502
1664
|
fh = await fsp2.open(file, "r");
|
|
@@ -1609,14 +1771,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1609
1771
|
async appendToIndex(summary) {
|
|
1610
1772
|
try {
|
|
1611
1773
|
await ensureDir(this.dir);
|
|
1612
|
-
|
|
1613
|
-
await
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
this.indexAppendCount
|
|
1774
|
+
let shouldCompact = false;
|
|
1775
|
+
await withFileLock(this.indexFile, async () => {
|
|
1776
|
+
const line = JSON.stringify(summary) + "\n";
|
|
1777
|
+
await fsp2.appendFile(this.indexFile, line, "utf8");
|
|
1778
|
+
this._indexCache = null;
|
|
1779
|
+
this.invalidateShardManifestBySessionId(summary.id);
|
|
1780
|
+
this.indexAppendCount++;
|
|
1781
|
+
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
1782
|
+
shouldCompact = true;
|
|
1783
|
+
this.indexAppendCount = 0;
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
if (shouldCompact) {
|
|
1787
|
+
await withFileLock(this.indexFile, () => this.compactIndexInner());
|
|
1620
1788
|
}
|
|
1621
1789
|
} catch {
|
|
1622
1790
|
}
|
|
@@ -1625,30 +1793,28 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1625
1793
|
async writeTombstone(id) {
|
|
1626
1794
|
try {
|
|
1627
1795
|
await ensureDir(this.dir);
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1796
|
+
await withFileLock(this.indexFile, async () => {
|
|
1797
|
+
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
1798
|
+
await fsp2.appendFile(this.indexFile, line, "utf8");
|
|
1799
|
+
this._indexCache = null;
|
|
1800
|
+
this.invalidateShardManifestBySessionId(id);
|
|
1801
|
+
this.indexAppendCount++;
|
|
1802
|
+
});
|
|
1633
1803
|
} catch {
|
|
1634
1804
|
}
|
|
1635
1805
|
}
|
|
1636
1806
|
/**
|
|
1637
1807
|
* Compact the index: read all entries, drop tombstones, deduplicate
|
|
1638
|
-
* (keep latest per session), and rewrite.
|
|
1808
|
+
* (keep latest per session), and rewrite atomically. Acquires the index
|
|
1809
|
+
* file lock so a concurrent append (this process or another wstack in the
|
|
1810
|
+
* same project) can't be overwritten by the rewrite.
|
|
1639
1811
|
*/
|
|
1640
1812
|
async compactIndex() {
|
|
1641
1813
|
const t0 = Date.now();
|
|
1642
1814
|
let outcome = "success";
|
|
1643
1815
|
let errorMsg;
|
|
1644
1816
|
try {
|
|
1645
|
-
|
|
1646
|
-
if (entries.length === 0) return;
|
|
1647
|
-
const tmp = `${this.indexFile}.compact.tmp`;
|
|
1648
|
-
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
1649
|
-
await fsp2.writeFile(tmp, lines, "utf8");
|
|
1650
|
-
await fsp2.rename(tmp, this.indexFile);
|
|
1651
|
-
this._indexCache = null;
|
|
1817
|
+
await withFileLock(this.indexFile, () => this.compactIndexInner());
|
|
1652
1818
|
} catch (err) {
|
|
1653
1819
|
outcome = "failure";
|
|
1654
1820
|
errorMsg = toErrorMessage(err);
|
|
@@ -1656,21 +1822,34 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1656
1822
|
this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
|
|
1657
1823
|
}
|
|
1658
1824
|
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Lock-free compaction body. The caller MUST already hold the index file
|
|
1827
|
+
* lock (via withFileLock(this.indexFile, ...)). Uses atomicWrite for the
|
|
1828
|
+
* rewrite so the temp file gets a random suffix (no collision between two
|
|
1829
|
+
* compactions) and the Windows transient-EPERM rename retry.
|
|
1830
|
+
*/
|
|
1831
|
+
async compactIndexInner() {
|
|
1832
|
+
const entries = await this.readIndex();
|
|
1833
|
+
if (entries.length === 0) return;
|
|
1834
|
+
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
1835
|
+
await atomicWrite(this.indexFile, lines, { mode: 384 });
|
|
1836
|
+
this._indexCache = null;
|
|
1837
|
+
}
|
|
1659
1838
|
/**
|
|
1660
1839
|
* Read the index file and return deduplicated session summaries.
|
|
1661
1840
|
* Entries with a matching tombstone are filtered out.
|
|
1662
1841
|
* Returns empty array when the index doesn't exist or is corrupt.
|
|
1663
1842
|
*/
|
|
1664
1843
|
async readIndex() {
|
|
1665
|
-
let
|
|
1844
|
+
let stat11;
|
|
1666
1845
|
try {
|
|
1667
1846
|
const s = await fsp2.stat(this.indexFile);
|
|
1668
|
-
|
|
1847
|
+
stat11 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
1669
1848
|
} catch {
|
|
1670
1849
|
this._indexCache = null;
|
|
1671
1850
|
return [];
|
|
1672
1851
|
}
|
|
1673
|
-
if (this._indexCache !== null && this._indexCache.mtimeMs ===
|
|
1852
|
+
if (this._indexCache !== null && this._indexCache.mtimeMs === stat11.mtimeMs && this._indexCache.size === stat11.size) {
|
|
1674
1853
|
return [...this._indexCache.summaries];
|
|
1675
1854
|
}
|
|
1676
1855
|
let raw;
|
|
@@ -1703,7 +1882,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1703
1882
|
if (a.startedAt > b.startedAt) return -1;
|
|
1704
1883
|
return a.id.localeCompare(b.id);
|
|
1705
1884
|
});
|
|
1706
|
-
this._indexCache = { ...
|
|
1885
|
+
this._indexCache = { ...stat11, summaries };
|
|
1707
1886
|
return [...summaries];
|
|
1708
1887
|
}
|
|
1709
1888
|
/**
|
|
@@ -1714,11 +1893,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1714
1893
|
const ids = await this.collectSessionIds(this.dir);
|
|
1715
1894
|
const summaries = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
1716
1895
|
const valid = summaries.filter((s) => s !== null);
|
|
1717
|
-
const tmp = `${this.indexFile}.tmp`;
|
|
1718
1896
|
const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
1719
|
-
await
|
|
1720
|
-
|
|
1721
|
-
|
|
1897
|
+
await withFileLock(this.indexFile, async () => {
|
|
1898
|
+
await atomicWrite(this.indexFile, lines, { mode: 384 });
|
|
1899
|
+
this._indexCache = null;
|
|
1900
|
+
});
|
|
1722
1901
|
return valid.length;
|
|
1723
1902
|
}
|
|
1724
1903
|
async listFromDirectoryScan(limit) {
|
|
@@ -1870,8 +2049,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1870
2049
|
if (fromManifest) return fromManifest;
|
|
1871
2050
|
try {
|
|
1872
2051
|
const full = this.sessionPath(id, ".jsonl");
|
|
1873
|
-
const
|
|
1874
|
-
const summary = await this.summarize(id,
|
|
2052
|
+
const stat11 = await fsp2.stat(full);
|
|
2053
|
+
const summary = await this.summarize(id, stat11.mtime.toISOString());
|
|
1875
2054
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
1876
2055
|
const msg = toErrorMessage(err);
|
|
1877
2056
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
@@ -1914,18 +2093,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1914
2093
|
async summaryHeaderFor(ref) {
|
|
1915
2094
|
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1916
2095
|
try {
|
|
1917
|
-
const
|
|
1918
|
-
if (!
|
|
2096
|
+
const stat11 = await fsp2.stat(ref.filePath);
|
|
2097
|
+
if (!stat11.isFile()) {
|
|
1919
2098
|
return {
|
|
1920
2099
|
id: ref.id,
|
|
1921
2100
|
title: "(damaged)",
|
|
1922
|
-
startedAt:
|
|
2101
|
+
startedAt: stat11.mtime.toISOString(),
|
|
1923
2102
|
model: "unknown",
|
|
1924
2103
|
provider: "unknown",
|
|
1925
2104
|
tokenTotal: 0
|
|
1926
2105
|
};
|
|
1927
2106
|
}
|
|
1928
|
-
mtime =
|
|
2107
|
+
mtime = stat11.mtime.toISOString();
|
|
1929
2108
|
} catch {
|
|
1930
2109
|
return null;
|
|
1931
2110
|
}
|
|
@@ -1973,14 +2152,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1973
2152
|
async deleteSession(id) {
|
|
1974
2153
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
1975
2154
|
const summaryPath = this.sessionPath(id, ".summary.json");
|
|
1976
|
-
const shardDir = path2.dirname(
|
|
2155
|
+
const shardDir = path2.dirname(jsonlPath);
|
|
1977
2156
|
const base = path2.basename(id);
|
|
1978
2157
|
const sessDir = path2.join(shardDir, base);
|
|
1979
2158
|
const deletions = [
|
|
1980
2159
|
fsp2.unlink(jsonlPath),
|
|
1981
2160
|
fsp2.unlink(summaryPath),
|
|
1982
|
-
fsp2.unlink(
|
|
1983
|
-
fsp2.unlink(
|
|
2161
|
+
fsp2.unlink(sessionScopedPath(this.dir, id, ".plan.json")),
|
|
2162
|
+
fsp2.unlink(sessionScopedPath(this.dir, id, ".tasks.json")),
|
|
2163
|
+
fsp2.unlink(sessionScopedPath(this.dir, id, ".todos.json"))
|
|
1984
2164
|
];
|
|
1985
2165
|
const results = await Promise.allSettled(deletions);
|
|
1986
2166
|
for (const r of results) {
|
|
@@ -2025,8 +2205,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
2025
2205
|
const pruneFile = async (dir, name, prefix) => {
|
|
2026
2206
|
const jsonlPath = path2.join(dir, name);
|
|
2027
2207
|
try {
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2208
|
+
const stat11 = await fsp2.stat(jsonlPath);
|
|
2209
|
+
if (stat11.mtimeMs >= cutoff) return;
|
|
2030
2210
|
} catch {
|
|
2031
2211
|
return;
|
|
2032
2212
|
}
|
|
@@ -2565,9 +2745,80 @@ function isMemoryType(s) {
|
|
|
2565
2745
|
function isPriority(s) {
|
|
2566
2746
|
return s === "critical" || s === "high" || s === "medium" || s === "low";
|
|
2567
2747
|
}
|
|
2748
|
+
function buildInvertedIndex(entries) {
|
|
2749
|
+
const wordMap = /* @__PURE__ */ new Map();
|
|
2750
|
+
const tagMap = /* @__PURE__ */ new Map();
|
|
2751
|
+
const indexed = new Array(entries.length);
|
|
2752
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2753
|
+
const e = entries[i];
|
|
2754
|
+
const words = e.text.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
|
|
2755
|
+
const tags = (e.tags ?? []).map((t) => t.toLowerCase());
|
|
2756
|
+
indexed[i] = { entry: e, words, tags };
|
|
2757
|
+
for (const w of words) {
|
|
2758
|
+
const arr = wordMap.get(w);
|
|
2759
|
+
if (arr) arr.push(i);
|
|
2760
|
+
else wordMap.set(w, [i]);
|
|
2761
|
+
}
|
|
2762
|
+
for (const t of tags) {
|
|
2763
|
+
const arr = tagMap.get(t);
|
|
2764
|
+
if (arr) arr.push(i);
|
|
2765
|
+
else tagMap.set(t, [i]);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
return { wordMap, tagMap, entries: indexed, mtimeMs: 0 };
|
|
2769
|
+
}
|
|
2770
|
+
var MIN_SUBSTRING_NEEDLE_LEN = 3;
|
|
2771
|
+
function searchIndex(index, query, limit) {
|
|
2772
|
+
const needles = query.toLowerCase().split(/\s+/).filter((n) => n.length > 0);
|
|
2773
|
+
if (needles.length === 0) return [];
|
|
2774
|
+
const scores = /* @__PURE__ */ new Map();
|
|
2775
|
+
for (const n of needles) {
|
|
2776
|
+
let matched = false;
|
|
2777
|
+
const wordExact = index.wordMap.get(n);
|
|
2778
|
+
if (wordExact) {
|
|
2779
|
+
for (const idx of wordExact) scores.set(idx, (scores.get(idx) ?? 0) + 1);
|
|
2780
|
+
matched = true;
|
|
2781
|
+
}
|
|
2782
|
+
const tagExact = index.tagMap.get(n);
|
|
2783
|
+
if (tagExact) {
|
|
2784
|
+
for (const idx of tagExact) scores.set(idx, (scores.get(idx) ?? 0) + 2);
|
|
2785
|
+
matched = true;
|
|
2786
|
+
}
|
|
2787
|
+
if (matched || n.length < MIN_SUBSTRING_NEEDLE_LEN) continue;
|
|
2788
|
+
for (const [word, indices] of index.wordMap) {
|
|
2789
|
+
if (word.includes(n) || n.includes(word)) {
|
|
2790
|
+
for (const idx of indices) {
|
|
2791
|
+
scores.set(idx, (scores.get(idx) ?? 0) + 1);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
for (const [tag, indices] of index.tagMap) {
|
|
2796
|
+
if (tag.includes(n) || n.includes(tag)) {
|
|
2797
|
+
for (const idx of indices) {
|
|
2798
|
+
scores.set(idx, (scores.get(idx) ?? 0) + 2);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
if (scores.size === 0) return [];
|
|
2804
|
+
const scored = Array.from(scores.entries());
|
|
2805
|
+
scored.sort((a, b) => b[1] - a[1]);
|
|
2806
|
+
const result = [];
|
|
2807
|
+
const max = limit ? Math.min(limit, scored.length) : scored.length;
|
|
2808
|
+
for (let i = 0; i < max; i++) {
|
|
2809
|
+
result.push(index.entries[scored[i][0]].entry);
|
|
2810
|
+
}
|
|
2811
|
+
return result;
|
|
2812
|
+
}
|
|
2568
2813
|
var FileMemoryBackend = class {
|
|
2569
2814
|
kind = "file";
|
|
2570
2815
|
files;
|
|
2816
|
+
/** Cache of parsed entries per file path. */
|
|
2817
|
+
entryCache = /* @__PURE__ */ new Map();
|
|
2818
|
+
/** Inverted index per file path. */
|
|
2819
|
+
indexCache = /* @__PURE__ */ new Map();
|
|
2820
|
+
/** File mtime cache for invalidation. */
|
|
2821
|
+
mtimeCache = /* @__PURE__ */ new Map();
|
|
2571
2822
|
constructor(opts) {
|
|
2572
2823
|
this.files = {
|
|
2573
2824
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
@@ -2578,6 +2829,53 @@ var FileMemoryBackend = class {
|
|
|
2578
2829
|
resolveFile(filePath, scope) {
|
|
2579
2830
|
return filePath || this.files[scope];
|
|
2580
2831
|
}
|
|
2832
|
+
async getMtime(file) {
|
|
2833
|
+
try {
|
|
2834
|
+
const stat11 = await fsp2.stat(file);
|
|
2835
|
+
return stat11.mtimeMs;
|
|
2836
|
+
} catch {
|
|
2837
|
+
return 0;
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
invalidateCache(file) {
|
|
2841
|
+
this.entryCache.delete(file);
|
|
2842
|
+
this.indexCache.delete(file);
|
|
2843
|
+
this.mtimeCache.delete(file);
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Load (and cache) the parsed entries for a file. Callers that have already
|
|
2847
|
+
* stat'd the file this tick (e.g. `getIndex`) can pass the known `mtime` to
|
|
2848
|
+
* avoid a redundant `fs.stat` — otherwise it's fetched here.
|
|
2849
|
+
*/
|
|
2850
|
+
async loadEntries(file, scope, mtime) {
|
|
2851
|
+
const resolvedMtime = mtime ?? await this.getMtime(file);
|
|
2852
|
+
const cachedMtime = this.mtimeCache.get(file);
|
|
2853
|
+
if (cachedMtime === resolvedMtime && this.entryCache.has(file)) {
|
|
2854
|
+
return this.entryCache.get(file);
|
|
2855
|
+
}
|
|
2856
|
+
const raw = await this.readAll(scope, file);
|
|
2857
|
+
if (!raw.trim()) {
|
|
2858
|
+
this.entryCache.set(file, []);
|
|
2859
|
+
this.mtimeCache.set(file, resolvedMtime);
|
|
2860
|
+
return [];
|
|
2861
|
+
}
|
|
2862
|
+
const entries = parseEntries(raw, scope);
|
|
2863
|
+
this.entryCache.set(file, entries);
|
|
2864
|
+
this.mtimeCache.set(file, resolvedMtime);
|
|
2865
|
+
return entries;
|
|
2866
|
+
}
|
|
2867
|
+
async getIndex(file, scope) {
|
|
2868
|
+
const mtime = await this.getMtime(file);
|
|
2869
|
+
const cached = this.indexCache.get(file);
|
|
2870
|
+
if (cached && cached.mtimeMs === mtime) {
|
|
2871
|
+
return cached;
|
|
2872
|
+
}
|
|
2873
|
+
const entries = await this.loadEntries(file, scope, mtime);
|
|
2874
|
+
const index = buildInvertedIndex(entries);
|
|
2875
|
+
index.mtimeMs = mtime;
|
|
2876
|
+
this.indexCache.set(file, index);
|
|
2877
|
+
return index;
|
|
2878
|
+
}
|
|
2581
2879
|
async remember(scope, entry, filePath) {
|
|
2582
2880
|
const file = this.resolveFile(filePath, scope);
|
|
2583
2881
|
await ensureDir(path2.dirname(file));
|
|
@@ -2594,6 +2892,7 @@ var FileMemoryBackend = class {
|
|
|
2594
2892
|
const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# Agent Memory
|
|
2595
2893
|
${line}`;
|
|
2596
2894
|
await atomicWrite(file, next);
|
|
2895
|
+
this.invalidateCache(file);
|
|
2597
2896
|
}
|
|
2598
2897
|
async forget(scope, query, filePath) {
|
|
2599
2898
|
const file = this.resolveFile(filePath, scope);
|
|
@@ -2630,6 +2929,7 @@ ${line}`;
|
|
|
2630
2929
|
await atomicWrite(file, lines.join("\n"));
|
|
2631
2930
|
}
|
|
2632
2931
|
}
|
|
2932
|
+
this.invalidateCache(file);
|
|
2633
2933
|
return removed;
|
|
2634
2934
|
});
|
|
2635
2935
|
}
|
|
@@ -2642,30 +2942,19 @@ ${line}`;
|
|
|
2642
2942
|
}
|
|
2643
2943
|
}
|
|
2644
2944
|
async list(scope, filePath, limit) {
|
|
2645
|
-
const
|
|
2646
|
-
|
|
2647
|
-
const entries = parseEntries(raw, scope);
|
|
2945
|
+
const file = this.resolveFile(filePath, scope);
|
|
2946
|
+
const entries = await this.loadEntries(file, scope);
|
|
2648
2947
|
return limit ? entries.slice(0, limit) : entries;
|
|
2649
2948
|
}
|
|
2650
2949
|
async search(scope, query, filePath, limit) {
|
|
2651
|
-
const
|
|
2652
|
-
const
|
|
2653
|
-
|
|
2654
|
-
const words = e.text.toLowerCase().split(/\s+/);
|
|
2655
|
-
let score = 0;
|
|
2656
|
-
for (const n of needle) {
|
|
2657
|
-
if (words.some((w) => w.includes(n))) score += 1;
|
|
2658
|
-
if (e.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
|
|
2659
|
-
}
|
|
2660
|
-
return { entry: e, score };
|
|
2661
|
-
});
|
|
2662
|
-
scored.sort((a, b) => b.score - a.score);
|
|
2663
|
-
const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
|
|
2664
|
-
return limit ? matched.slice(0, limit) : matched;
|
|
2950
|
+
const file = this.resolveFile(filePath, scope);
|
|
2951
|
+
const index = await this.getIndex(file, scope);
|
|
2952
|
+
return searchIndex(index, query, limit);
|
|
2665
2953
|
}
|
|
2666
2954
|
async clear(scope, filePath) {
|
|
2667
2955
|
const file = this.resolveFile(filePath, scope);
|
|
2668
2956
|
await atomicWrite(file, "");
|
|
2957
|
+
this.invalidateCache(file);
|
|
2669
2958
|
}
|
|
2670
2959
|
async consolidate(scope, filePath) {
|
|
2671
2960
|
const file = this.resolveFile(filePath, scope);
|
|
@@ -2700,6 +2989,7 @@ ${line}`;
|
|
|
2700
2989
|
} catch {
|
|
2701
2990
|
return 0;
|
|
2702
2991
|
}
|
|
2992
|
+
this.invalidateCache(file);
|
|
2703
2993
|
return removed;
|
|
2704
2994
|
}
|
|
2705
2995
|
};
|
|
@@ -2766,9 +3056,10 @@ var DefaultMemoryStore = class {
|
|
|
2766
3056
|
/** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
|
|
2767
3057
|
_scoreCache = /* @__PURE__ */ new Map();
|
|
2768
3058
|
/**
|
|
2769
|
-
* Per-entry cached lowercase strings —
|
|
2770
|
-
*
|
|
2771
|
-
*
|
|
3059
|
+
* Per-entry cached lowercase strings — lazily allocated once and reused
|
|
3060
|
+
* across scoreRelevant() calls so repeated scoring of the same entries skips
|
|
3061
|
+
* re-lowercasing. Keyed by entry object identity; cleared (set to null) on
|
|
3062
|
+
* every mutation (remember/forget/consolidate/clear) via invalidateScoreCaches().
|
|
2772
3063
|
*/
|
|
2773
3064
|
_cachedLower = null;
|
|
2774
3065
|
constructor(opts) {
|
|
@@ -2918,7 +3209,7 @@ ${body.trim()}`);
|
|
|
2918
3209
|
const t0 = Date.now();
|
|
2919
3210
|
try {
|
|
2920
3211
|
await this.backend.remember(scope, entry, filePath);
|
|
2921
|
-
this.
|
|
3212
|
+
this.invalidateScoreCaches();
|
|
2922
3213
|
const dur = Date.now() - t0;
|
|
2923
3214
|
this.events?.emit("storage.write", {
|
|
2924
3215
|
sessionId: "~memory~",
|
|
@@ -2969,6 +3260,15 @@ ${body.trim()}`);
|
|
|
2969
3260
|
});
|
|
2970
3261
|
});
|
|
2971
3262
|
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Drop the relevance caches after a mutation. Both the per-context score
|
|
3265
|
+
* cache and the per-entry lowercase cache are keyed on entry objects that a
|
|
3266
|
+
* mutation invalidates, so they must be cleared together when entries change.
|
|
3267
|
+
*/
|
|
3268
|
+
invalidateScoreCaches() {
|
|
3269
|
+
this._scoreCache.clear();
|
|
3270
|
+
this._cachedLower = null;
|
|
3271
|
+
}
|
|
2972
3272
|
/**
|
|
2973
3273
|
* Score and rank memories by relevance to the current context.
|
|
2974
3274
|
* Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
|
|
@@ -2986,18 +3286,19 @@ ${body.trim()}`);
|
|
|
2986
3286
|
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
2987
3287
|
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
2988
3288
|
const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
|
|
2989
|
-
this._cachedLower
|
|
3289
|
+
this._cachedLower ??= /* @__PURE__ */ new WeakMap();
|
|
3290
|
+
const lowerCache = this._cachedLower;
|
|
2990
3291
|
const scored = [];
|
|
2991
3292
|
for (const entry of all) {
|
|
2992
3293
|
let score = 0;
|
|
2993
3294
|
const reasons = [];
|
|
2994
|
-
let cachedLower =
|
|
3295
|
+
let cachedLower = lowerCache.get(entry);
|
|
2995
3296
|
if (!cachedLower) {
|
|
2996
3297
|
cachedLower = {
|
|
2997
3298
|
textLower: entry.text.toLowerCase(),
|
|
2998
3299
|
tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
|
|
2999
3300
|
};
|
|
3000
|
-
|
|
3301
|
+
lowerCache.set(entry, cachedLower);
|
|
3001
3302
|
}
|
|
3002
3303
|
const { textLower, tagsLower } = cachedLower;
|
|
3003
3304
|
let taskHits = 0;
|
|
@@ -3095,7 +3396,7 @@ ${body.trim()}`);
|
|
|
3095
3396
|
let removed = 0;
|
|
3096
3397
|
try {
|
|
3097
3398
|
removed = await this.backend.forget(scope, query, filePath);
|
|
3098
|
-
this.
|
|
3399
|
+
this.invalidateScoreCaches();
|
|
3099
3400
|
const dur = Date.now() - t0;
|
|
3100
3401
|
this.events?.emit("storage.write", {
|
|
3101
3402
|
sessionId: "~memory~",
|
|
@@ -3139,7 +3440,7 @@ ${body.trim()}`);
|
|
|
3139
3440
|
let removed = 0;
|
|
3140
3441
|
try {
|
|
3141
3442
|
removed = await this.backend.consolidate(scope, filePath);
|
|
3142
|
-
this.
|
|
3443
|
+
this.invalidateScoreCaches();
|
|
3143
3444
|
const dur = Date.now() - t0;
|
|
3144
3445
|
this.events?.emit("storage.write", {
|
|
3145
3446
|
sessionId: "~memory~",
|
|
@@ -3181,7 +3482,7 @@ ${body.trim()}`);
|
|
|
3181
3482
|
const t0 = Date.now();
|
|
3182
3483
|
try {
|
|
3183
3484
|
await this.backend.clear(scope, filePath);
|
|
3184
|
-
this.
|
|
3485
|
+
this.invalidateScoreCaches();
|
|
3185
3486
|
const dur = Date.now() - t0;
|
|
3186
3487
|
this.events?.emit("storage.write", {
|
|
3187
3488
|
sessionId: "~memory~",
|
|
@@ -3248,6 +3549,7 @@ ${body.trim()}`);
|
|
|
3248
3549
|
})
|
|
3249
3550
|
)
|
|
3250
3551
|
);
|
|
3552
|
+
this.invalidateScoreCaches();
|
|
3251
3553
|
}
|
|
3252
3554
|
/**
|
|
3253
3555
|
* Return a new MemoryStore proxy that carries `traceId` on every storage
|
|
@@ -3775,97 +4077,6 @@ var SessionMemoryConsolidator = class {
|
|
|
3775
4077
|
};
|
|
3776
4078
|
};
|
|
3777
4079
|
|
|
3778
|
-
// src/types/errors.ts
|
|
3779
|
-
var ERROR_CODES = {
|
|
3780
|
-
// Config
|
|
3781
|
-
CONFIG_INVALID: "CONFIG_INVALID",
|
|
3782
|
-
// Session
|
|
3783
|
-
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
3784
|
-
SESSION_CORRUPTED: "SESSION_CORRUPTED",
|
|
3785
|
-
SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
|
|
3786
|
-
// File system
|
|
3787
|
-
FS_READ_FAILED: "FS_READ_FAILED",
|
|
3788
|
-
FS_DELETE_FAILED: "FS_DELETE_FAILED",
|
|
3789
|
-
FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
|
|
3790
|
-
// General
|
|
3791
|
-
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
3792
|
-
UNKNOWN: "UNKNOWN"
|
|
3793
|
-
};
|
|
3794
|
-
var WrongStackError = class extends Error {
|
|
3795
|
-
code;
|
|
3796
|
-
subsystem;
|
|
3797
|
-
severity;
|
|
3798
|
-
recoverable;
|
|
3799
|
-
context;
|
|
3800
|
-
constructor(opts) {
|
|
3801
|
-
super(opts.message, { cause: opts.cause });
|
|
3802
|
-
this.name = "WrongStackError";
|
|
3803
|
-
this.code = opts.code;
|
|
3804
|
-
this.subsystem = opts.subsystem;
|
|
3805
|
-
this.severity = opts.severity ?? "error";
|
|
3806
|
-
this.recoverable = opts.recoverable ?? false;
|
|
3807
|
-
this.context = opts.context;
|
|
3808
|
-
}
|
|
3809
|
-
/**
|
|
3810
|
-
* Render a one-line user-facing description.
|
|
3811
|
-
* Subclasses should override for domain-specific formatting.
|
|
3812
|
-
*/
|
|
3813
|
-
describe() {
|
|
3814
|
-
const ctx = this.context ? ` ${formatContext(this.context)}` : "";
|
|
3815
|
-
return `${this.code}: ${this.message}${ctx}`;
|
|
3816
|
-
}
|
|
3817
|
-
};
|
|
3818
|
-
function formatContext(ctx) {
|
|
3819
|
-
const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
|
|
3820
|
-
return parts.length > 0 ? `[${parts.join(" ")}]` : "";
|
|
3821
|
-
}
|
|
3822
|
-
var ConfigError = class extends WrongStackError {
|
|
3823
|
-
constructor(opts) {
|
|
3824
|
-
super({
|
|
3825
|
-
message: opts.message,
|
|
3826
|
-
code: opts.code,
|
|
3827
|
-
subsystem: "config",
|
|
3828
|
-
severity: "fatal",
|
|
3829
|
-
recoverable: false,
|
|
3830
|
-
context: opts.context,
|
|
3831
|
-
cause: opts.cause
|
|
3832
|
-
});
|
|
3833
|
-
this.name = "ConfigError";
|
|
3834
|
-
}
|
|
3835
|
-
};
|
|
3836
|
-
var SessionError = class extends WrongStackError {
|
|
3837
|
-
sessionId;
|
|
3838
|
-
constructor(opts) {
|
|
3839
|
-
super({
|
|
3840
|
-
message: opts.message,
|
|
3841
|
-
code: opts.code,
|
|
3842
|
-
subsystem: "session",
|
|
3843
|
-
severity: opts.code === ERROR_CODES.SESSION_WRITE_FAILED ? "error" : "warning",
|
|
3844
|
-
recoverable: opts.code !== ERROR_CODES.SESSION_CORRUPTED,
|
|
3845
|
-
context: { sessionId: opts.sessionId, ...opts.context },
|
|
3846
|
-
cause: opts.cause
|
|
3847
|
-
});
|
|
3848
|
-
this.name = "SessionError";
|
|
3849
|
-
this.sessionId = opts.sessionId;
|
|
3850
|
-
}
|
|
3851
|
-
};
|
|
3852
|
-
var FsError = class extends WrongStackError {
|
|
3853
|
-
path;
|
|
3854
|
-
constructor(opts) {
|
|
3855
|
-
super({
|
|
3856
|
-
message: opts.message,
|
|
3857
|
-
code: opts.code,
|
|
3858
|
-
subsystem: "fs",
|
|
3859
|
-
severity: "error",
|
|
3860
|
-
recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
|
|
3861
|
-
context: { path: opts.path, ...opts.context },
|
|
3862
|
-
cause: opts.cause
|
|
3863
|
-
});
|
|
3864
|
-
this.name = "FsError";
|
|
3865
|
-
this.path = opts.path;
|
|
3866
|
-
}
|
|
3867
|
-
};
|
|
3868
|
-
|
|
3869
4080
|
// src/storage/config-store.ts
|
|
3870
4081
|
function stripEphemeralFields(cfg) {
|
|
3871
4082
|
const env = cfg._envSource;
|
|
@@ -3945,7 +4156,7 @@ var KEK_MAGIC = Buffer.from("WSKW", "ascii");
|
|
|
3945
4156
|
var KEK_SALT_BYTES = 16;
|
|
3946
4157
|
KEK_MAGIC.length + 1 + KEK_SALT_BYTES + IV_BYTES + TAG_BYTES + KEY_BYTES;
|
|
3947
4158
|
function decryptConfigSecrets(cfg, vault, opts) {
|
|
3948
|
-
const warn = ((msg) => console.warn(msg));
|
|
4159
|
+
const warn = opts?.warn ?? ((msg) => console.warn(msg));
|
|
3949
4160
|
return walk(cfg, vault, (v, key) => {
|
|
3950
4161
|
try {
|
|
3951
4162
|
return vault.decrypt(v);
|
|
@@ -3983,6 +4194,95 @@ function isSecretField(name) {
|
|
|
3983
4194
|
return SECRET_KEY_PATTERN.test(lc);
|
|
3984
4195
|
}
|
|
3985
4196
|
|
|
4197
|
+
// src/storage/provider-config-watcher.ts
|
|
4198
|
+
async function readProviderSnapshot(configPath, vault, warn) {
|
|
4199
|
+
let raw;
|
|
4200
|
+
try {
|
|
4201
|
+
raw = await fsp2.readFile(configPath, "utf8");
|
|
4202
|
+
} catch (err) {
|
|
4203
|
+
if (err.code !== "ENOENT") {
|
|
4204
|
+
warn?.(`Could not read ${configPath}: ${err.message}`);
|
|
4205
|
+
}
|
|
4206
|
+
return void 0;
|
|
4207
|
+
}
|
|
4208
|
+
let parsed;
|
|
4209
|
+
try {
|
|
4210
|
+
parsed = JSON.parse(raw);
|
|
4211
|
+
} catch (err) {
|
|
4212
|
+
warn?.(`Config at ${configPath} is not valid JSON: ${err.message}`);
|
|
4213
|
+
return void 0;
|
|
4214
|
+
}
|
|
4215
|
+
const decrypted = decryptConfigSecrets(parsed, vault, warn ? { warn } : {});
|
|
4216
|
+
const snapshot = {
|
|
4217
|
+
providers: decrypted.providers ?? {}
|
|
4218
|
+
};
|
|
4219
|
+
if (typeof decrypted.apiKey === "string") snapshot.apiKey = decrypted.apiKey;
|
|
4220
|
+
if (typeof decrypted.baseUrl === "string") snapshot.baseUrl = decrypted.baseUrl;
|
|
4221
|
+
return snapshot;
|
|
4222
|
+
}
|
|
4223
|
+
function serializeSnapshot(s) {
|
|
4224
|
+
return JSON.stringify({
|
|
4225
|
+
providers: s.providers,
|
|
4226
|
+
apiKey: s.apiKey ?? null,
|
|
4227
|
+
baseUrl: s.baseUrl ?? null
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
4230
|
+
function watchProviderConfig(configPath, vault, onChange, opts = {}) {
|
|
4231
|
+
const debounceMs = opts.debounceMs ?? 200;
|
|
4232
|
+
const warn = opts.warn;
|
|
4233
|
+
const base = path2.basename(configPath);
|
|
4234
|
+
let timer;
|
|
4235
|
+
let closed = false;
|
|
4236
|
+
let lastSerialized;
|
|
4237
|
+
void readProviderSnapshot(configPath, vault, warn).then((seed) => {
|
|
4238
|
+
if (!closed && seed && lastSerialized === void 0) {
|
|
4239
|
+
lastSerialized = serializeSnapshot(seed);
|
|
4240
|
+
}
|
|
4241
|
+
});
|
|
4242
|
+
let watcher;
|
|
4243
|
+
try {
|
|
4244
|
+
watcher = syncFs.watch(path2.dirname(configPath), { recursive: false });
|
|
4245
|
+
} catch (err) {
|
|
4246
|
+
warn?.(`Provider config watcher could not start: ${err.message}`);
|
|
4247
|
+
return { close: () => {
|
|
4248
|
+
} };
|
|
4249
|
+
}
|
|
4250
|
+
const trigger = () => {
|
|
4251
|
+
if (closed) return;
|
|
4252
|
+
if (timer) clearTimeout(timer);
|
|
4253
|
+
timer = setTimeout(() => {
|
|
4254
|
+
timer = void 0;
|
|
4255
|
+
void readProviderSnapshot(configPath, vault, warn).then(
|
|
4256
|
+
(next) => {
|
|
4257
|
+
if (closed || !next) return;
|
|
4258
|
+
const serialized = serializeSnapshot(next);
|
|
4259
|
+
if (serialized === lastSerialized) return;
|
|
4260
|
+
lastSerialized = serialized;
|
|
4261
|
+
onChange(next);
|
|
4262
|
+
},
|
|
4263
|
+
() => {
|
|
4264
|
+
}
|
|
4265
|
+
);
|
|
4266
|
+
}, debounceMs);
|
|
4267
|
+
};
|
|
4268
|
+
watcher.on("change", (eventType, filename) => {
|
|
4269
|
+
const name = typeof filename === "string" ? filename : "";
|
|
4270
|
+
if (eventType === "rename" || eventType === "change") {
|
|
4271
|
+
if (!name || name === base) trigger();
|
|
4272
|
+
}
|
|
4273
|
+
});
|
|
4274
|
+
watcher.on("error", (err) => {
|
|
4275
|
+
warn?.(`Provider config watcher error: ${err.message}`);
|
|
4276
|
+
});
|
|
4277
|
+
return {
|
|
4278
|
+
close: () => {
|
|
4279
|
+
closed = true;
|
|
4280
|
+
if (timer) clearTimeout(timer);
|
|
4281
|
+
watcher.close();
|
|
4282
|
+
}
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
|
|
3986
4286
|
// src/types/context-window.ts
|
|
3987
4287
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
3988
4288
|
var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
@@ -4676,8 +4976,8 @@ var DefaultConfigLoader = class {
|
|
|
4676
4976
|
const t0 = Date.now();
|
|
4677
4977
|
let mtimeMs = null;
|
|
4678
4978
|
try {
|
|
4679
|
-
const
|
|
4680
|
-
mtimeMs =
|
|
4979
|
+
const stat11 = await fsp2.stat(file);
|
|
4980
|
+
mtimeMs = stat11.mtimeMs;
|
|
4681
4981
|
const cached = this.jsonCache.get(file);
|
|
4682
4982
|
if (cached && cached.mtimeMs === mtimeMs) {
|
|
4683
4983
|
return structuredClone(cached.value);
|
|
@@ -5019,11 +5319,11 @@ var DefaultSessionReader = class _DefaultSessionReader {
|
|
|
5019
5319
|
if (!rootDir) {
|
|
5020
5320
|
return await this.store.load(sessionId);
|
|
5021
5321
|
}
|
|
5022
|
-
const sessionPath =
|
|
5322
|
+
const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
|
|
5023
5323
|
let mtimeMs = null;
|
|
5024
5324
|
try {
|
|
5025
|
-
const
|
|
5026
|
-
mtimeMs =
|
|
5325
|
+
const stat11 = await fsp2.stat(sessionPath);
|
|
5326
|
+
mtimeMs = stat11.mtimeMs;
|
|
5027
5327
|
} catch {
|
|
5028
5328
|
this.eventCache.delete(sessionId);
|
|
5029
5329
|
this.eventCacheMtimes.delete(sessionId);
|
|
@@ -5379,27 +5679,6 @@ function renderPlainText(meta, events) {
|
|
|
5379
5679
|
}
|
|
5380
5680
|
return lines.join("\n");
|
|
5381
5681
|
}
|
|
5382
|
-
function sessionScopedPath(dir, sessionId, suffix) {
|
|
5383
|
-
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
5384
|
-
throw invalid(sessionId);
|
|
5385
|
-
}
|
|
5386
|
-
const resolved = path2.resolve(dir, `${sessionId}${suffix}`);
|
|
5387
|
-
const rel = path2.relative(path2.resolve(dir), resolved);
|
|
5388
|
-
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
5389
|
-
throw invalid(sessionId);
|
|
5390
|
-
}
|
|
5391
|
-
return resolved;
|
|
5392
|
-
}
|
|
5393
|
-
function invalid(sessionId) {
|
|
5394
|
-
return new FsError({
|
|
5395
|
-
message: `Invalid sessionId: ${sessionId}`,
|
|
5396
|
-
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
5397
|
-
path: sessionId,
|
|
5398
|
-
context: { reason: "path_traversal" }
|
|
5399
|
-
});
|
|
5400
|
-
}
|
|
5401
|
-
|
|
5402
|
-
// src/storage/annotations-store.ts
|
|
5403
5682
|
var FILE_VERSION = 1;
|
|
5404
5683
|
var MAX_TEXT_LENGTH = 2e3;
|
|
5405
5684
|
var MAX_ANNOTATIONS = 1e3;
|
|
@@ -5743,8 +6022,8 @@ var ReplayLogStore = class {
|
|
|
5743
6022
|
const line = JSON.stringify(entry) + "\n";
|
|
5744
6023
|
let offset2 = 0;
|
|
5745
6024
|
try {
|
|
5746
|
-
const
|
|
5747
|
-
offset2 =
|
|
6025
|
+
const stat11 = await fsp2.stat(fp);
|
|
6026
|
+
offset2 = stat11.size;
|
|
5748
6027
|
} catch (err) {
|
|
5749
6028
|
if (err.code !== "ENOENT") throw err;
|
|
5750
6029
|
}
|
|
@@ -6084,15 +6363,15 @@ var SessionRecovery = class {
|
|
|
6084
6363
|
async detectStale(sessionId) {
|
|
6085
6364
|
const fp = this.filePath(sessionId);
|
|
6086
6365
|
const TAIL_SIZE = 8192;
|
|
6087
|
-
let
|
|
6366
|
+
let stat11;
|
|
6088
6367
|
try {
|
|
6089
|
-
|
|
6368
|
+
stat11 = await fsp2.stat(fp);
|
|
6090
6369
|
} catch (err) {
|
|
6091
6370
|
if (err.code === "ENOENT") return null;
|
|
6092
6371
|
return null;
|
|
6093
6372
|
}
|
|
6094
|
-
if (
|
|
6095
|
-
const position = Math.max(0,
|
|
6373
|
+
if (stat11.size === 0) return null;
|
|
6374
|
+
const position = Math.max(0, stat11.size - TAIL_SIZE);
|
|
6096
6375
|
const buf = Buffer.alloc(TAIL_SIZE);
|
|
6097
6376
|
let fh;
|
|
6098
6377
|
try {
|
|
@@ -6904,11 +7183,11 @@ var SessionRegistry = class {
|
|
|
6904
7183
|
*/
|
|
6905
7184
|
async breakStaleLock(lockPath) {
|
|
6906
7185
|
try {
|
|
6907
|
-
const [
|
|
7186
|
+
const [stat11, content] = await Promise.all([
|
|
6908
7187
|
fsp2.stat(lockPath),
|
|
6909
7188
|
fsp2.readFile(lockPath, "utf8").catch(() => "")
|
|
6910
7189
|
]);
|
|
6911
|
-
const ageMs = Date.now() -
|
|
7190
|
+
const ageMs = Date.now() - stat11.mtimeMs;
|
|
6912
7191
|
const ownerPid = Number.parseInt(content.trim(), 10);
|
|
6913
7192
|
const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
|
|
6914
7193
|
if (ownerDead || ageMs > STALE_LOCK_MS) {
|
|
@@ -6951,9 +7230,9 @@ var SessionRegistry = class {
|
|
|
6951
7230
|
for (const name of await fsp2.readdir(dir)) {
|
|
6952
7231
|
const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
|
|
6953
7232
|
if (!isTemp) continue;
|
|
6954
|
-
const
|
|
6955
|
-
if (!
|
|
6956
|
-
if (now -
|
|
7233
|
+
const stat11 = await fsp2.stat(path2.join(dir, name)).catch(() => null);
|
|
7234
|
+
if (!stat11) continue;
|
|
7235
|
+
if (now - stat11.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat11.mtimeMs });
|
|
6957
7236
|
}
|
|
6958
7237
|
stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
6959
7238
|
await Promise.all(
|
|
@@ -6991,6 +7270,7 @@ function clampPct(pct) {
|
|
|
6991
7270
|
var AgentStatusTracker = class {
|
|
6992
7271
|
events;
|
|
6993
7272
|
registry;
|
|
7273
|
+
sessionId;
|
|
6994
7274
|
leaderName;
|
|
6995
7275
|
// Live agent map: agentId → AgentEntry
|
|
6996
7276
|
agents = /* @__PURE__ */ new Map();
|
|
@@ -7016,6 +7296,7 @@ var AgentStatusTracker = class {
|
|
|
7016
7296
|
constructor(opts) {
|
|
7017
7297
|
this.events = opts.events;
|
|
7018
7298
|
this.registry = opts.registry;
|
|
7299
|
+
this.sessionId = opts.sessionId;
|
|
7019
7300
|
this.leaderName = opts.leaderName ?? "leader";
|
|
7020
7301
|
this.onUpdate = opts.onUpdate;
|
|
7021
7302
|
}
|
|
@@ -7024,8 +7305,12 @@ var AgentStatusTracker = class {
|
|
|
7024
7305
|
return this.lastAgents.length > 0 ? [...this.lastAgents] : [];
|
|
7025
7306
|
}
|
|
7026
7307
|
start() {
|
|
7308
|
+
const on = (pattern, fn) => this.events.onPattern(pattern, (event, payload) => {
|
|
7309
|
+
if (!this.acceptsSession(payload)) return;
|
|
7310
|
+
fn(event, payload);
|
|
7311
|
+
});
|
|
7027
7312
|
this.unsubscribers.push(
|
|
7028
|
-
|
|
7313
|
+
on("agent.run.started", (_event, payload) => {
|
|
7029
7314
|
const p = payload;
|
|
7030
7315
|
this.markLeaderStarted(p?.at);
|
|
7031
7316
|
this.captureLeaderContext(p?.ctx);
|
|
@@ -7036,7 +7321,7 @@ var AgentStatusTracker = class {
|
|
|
7036
7321
|
})
|
|
7037
7322
|
);
|
|
7038
7323
|
this.unsubscribers.push(
|
|
7039
|
-
|
|
7324
|
+
on("iteration.started", (_e, payload) => {
|
|
7040
7325
|
const p = payload;
|
|
7041
7326
|
const ctx = p?.ctx;
|
|
7042
7327
|
this.markLeaderStarted();
|
|
@@ -7053,7 +7338,7 @@ var AgentStatusTracker = class {
|
|
|
7053
7338
|
})
|
|
7054
7339
|
);
|
|
7055
7340
|
this.unsubscribers.push(
|
|
7056
|
-
|
|
7341
|
+
on("agent.run.completed", (_event, payload) => {
|
|
7057
7342
|
const p = payload;
|
|
7058
7343
|
this.captureLeaderContext(p?.ctx);
|
|
7059
7344
|
this.leaderStatus = p?.status === "failed" ? "error" : "idle";
|
|
@@ -7064,7 +7349,7 @@ var AgentStatusTracker = class {
|
|
|
7064
7349
|
})
|
|
7065
7350
|
);
|
|
7066
7351
|
this.unsubscribers.push(
|
|
7067
|
-
|
|
7352
|
+
on("agent.run.error", (_event, payload) => {
|
|
7068
7353
|
const p = payload;
|
|
7069
7354
|
this.captureLeaderContext(p?.ctx);
|
|
7070
7355
|
this.leaderStatus = "error";
|
|
@@ -7074,7 +7359,7 @@ var AgentStatusTracker = class {
|
|
|
7074
7359
|
})
|
|
7075
7360
|
);
|
|
7076
7361
|
this.unsubscribers.push(
|
|
7077
|
-
|
|
7362
|
+
on("tool.started", (_event, payload) => {
|
|
7078
7363
|
const p = payload;
|
|
7079
7364
|
if (p?.name) {
|
|
7080
7365
|
this.markLeaderStarted();
|
|
@@ -7086,20 +7371,20 @@ var AgentStatusTracker = class {
|
|
|
7086
7371
|
})
|
|
7087
7372
|
);
|
|
7088
7373
|
this.unsubscribers.push(
|
|
7089
|
-
|
|
7374
|
+
on("tool.executed", () => {
|
|
7090
7375
|
this.leaderCurrentTool = void 0;
|
|
7091
7376
|
this.flush();
|
|
7092
7377
|
})
|
|
7093
7378
|
);
|
|
7094
7379
|
this.unsubscribers.push(
|
|
7095
|
-
|
|
7380
|
+
on("brain.ask_human", () => {
|
|
7096
7381
|
this.markLeaderStarted();
|
|
7097
7382
|
this.leaderStatus = "waiting_user";
|
|
7098
7383
|
this.flush();
|
|
7099
7384
|
})
|
|
7100
7385
|
);
|
|
7101
7386
|
this.unsubscribers.push(
|
|
7102
|
-
|
|
7387
|
+
on("llm.stream_started", () => {
|
|
7103
7388
|
this.markLeaderStarted();
|
|
7104
7389
|
this.leaderStatus = "streaming";
|
|
7105
7390
|
this.leaderPartialText = "";
|
|
@@ -7107,7 +7392,7 @@ var AgentStatusTracker = class {
|
|
|
7107
7392
|
})
|
|
7108
7393
|
);
|
|
7109
7394
|
this.unsubscribers.push(
|
|
7110
|
-
|
|
7395
|
+
on("provider.text_delta", (_e, payload) => {
|
|
7111
7396
|
const p = payload;
|
|
7112
7397
|
const text = p?.text;
|
|
7113
7398
|
if (!text) return;
|
|
@@ -7120,14 +7405,14 @@ var AgentStatusTracker = class {
|
|
|
7120
7405
|
})
|
|
7121
7406
|
);
|
|
7122
7407
|
this.unsubscribers.push(
|
|
7123
|
-
|
|
7408
|
+
on("provider.response", (_e, payload) => {
|
|
7124
7409
|
const p = payload;
|
|
7125
7410
|
this.captureLeaderContext(p?.ctx);
|
|
7126
7411
|
this.flush();
|
|
7127
7412
|
})
|
|
7128
7413
|
);
|
|
7129
7414
|
this.unsubscribers.push(
|
|
7130
|
-
|
|
7415
|
+
on("provider.fallback", (_e, payload) => {
|
|
7131
7416
|
const p = payload;
|
|
7132
7417
|
if (p?.to?.model) {
|
|
7133
7418
|
this.leaderModel = p.to.providerId ? `${p.to.providerId}/${p.to.model}` : p.to.model;
|
|
@@ -7136,7 +7421,7 @@ var AgentStatusTracker = class {
|
|
|
7136
7421
|
})
|
|
7137
7422
|
);
|
|
7138
7423
|
this.unsubscribers.push(
|
|
7139
|
-
|
|
7424
|
+
on("ctx.pct", (_e, payload) => {
|
|
7140
7425
|
const p = payload;
|
|
7141
7426
|
if (typeof p?.load === "number" && Number.isFinite(p.load)) {
|
|
7142
7427
|
this.leaderCtxPct = clampPct(Math.round(p.load * 100));
|
|
@@ -7145,7 +7430,7 @@ var AgentStatusTracker = class {
|
|
|
7145
7430
|
})
|
|
7146
7431
|
);
|
|
7147
7432
|
this.unsubscribers.push(
|
|
7148
|
-
|
|
7433
|
+
on("token.accounted", (_e, payload) => {
|
|
7149
7434
|
const p = payload;
|
|
7150
7435
|
if (!p) return;
|
|
7151
7436
|
this.leaderTokensIn += p.usage?.input ?? 0;
|
|
@@ -7165,7 +7450,7 @@ var AgentStatusTracker = class {
|
|
|
7165
7450
|
return entry;
|
|
7166
7451
|
};
|
|
7167
7452
|
this.unsubscribers.push(
|
|
7168
|
-
|
|
7453
|
+
on("subagent.spawned", (_e, payload) => {
|
|
7169
7454
|
const p = payload;
|
|
7170
7455
|
if (!p?.subagentId) return;
|
|
7171
7456
|
const entry = touch(p.subagentId);
|
|
@@ -7177,7 +7462,7 @@ var AgentStatusTracker = class {
|
|
|
7177
7462
|
})
|
|
7178
7463
|
);
|
|
7179
7464
|
this.unsubscribers.push(
|
|
7180
|
-
|
|
7465
|
+
on("subagent.ctx_pct", (_e, payload) => {
|
|
7181
7466
|
const p = payload;
|
|
7182
7467
|
if (!p?.subagentId) return;
|
|
7183
7468
|
const entry = touch(p.subagentId);
|
|
@@ -7186,7 +7471,7 @@ var AgentStatusTracker = class {
|
|
|
7186
7471
|
})
|
|
7187
7472
|
);
|
|
7188
7473
|
this.unsubscribers.push(
|
|
7189
|
-
|
|
7474
|
+
on("subagent.task_started", (_e, payload) => {
|
|
7190
7475
|
const p = payload;
|
|
7191
7476
|
if (!p?.subagentId) return;
|
|
7192
7477
|
const entry = touch(p.subagentId);
|
|
@@ -7197,7 +7482,7 @@ var AgentStatusTracker = class {
|
|
|
7197
7482
|
})
|
|
7198
7483
|
);
|
|
7199
7484
|
this.unsubscribers.push(
|
|
7200
|
-
|
|
7485
|
+
on("subagent.tool_executed", (_e, payload) => {
|
|
7201
7486
|
const p = payload;
|
|
7202
7487
|
if (!p?.subagentId) return;
|
|
7203
7488
|
const entry = touch(p.subagentId);
|
|
@@ -7209,7 +7494,7 @@ var AgentStatusTracker = class {
|
|
|
7209
7494
|
})
|
|
7210
7495
|
);
|
|
7211
7496
|
this.unsubscribers.push(
|
|
7212
|
-
|
|
7497
|
+
on("subagent.iteration_summary", (_e, payload) => {
|
|
7213
7498
|
const p = payload;
|
|
7214
7499
|
if (!p?.subagentId) return;
|
|
7215
7500
|
const entry = touch(p.subagentId);
|
|
@@ -7226,7 +7511,7 @@ var AgentStatusTracker = class {
|
|
|
7226
7511
|
})
|
|
7227
7512
|
);
|
|
7228
7513
|
this.unsubscribers.push(
|
|
7229
|
-
|
|
7514
|
+
on("subagent.task_completed", (_e, payload) => {
|
|
7230
7515
|
const p = payload;
|
|
7231
7516
|
if (!p?.subagentId) return;
|
|
7232
7517
|
const entry = this.agents.get(p.subagentId);
|
|
@@ -7241,7 +7526,7 @@ var AgentStatusTracker = class {
|
|
|
7241
7526
|
})
|
|
7242
7527
|
);
|
|
7243
7528
|
this.unsubscribers.push(
|
|
7244
|
-
|
|
7529
|
+
on("subagent.stopped", (_e, payload) => {
|
|
7245
7530
|
const p = payload;
|
|
7246
7531
|
if (!p?.subagentId) return;
|
|
7247
7532
|
if (this.agents.delete(p.subagentId)) this.flush();
|
|
@@ -7318,7 +7603,10 @@ var AgentStatusTracker = class {
|
|
|
7318
7603
|
const allAgents = [leaderEntry, ...this.agents.values()];
|
|
7319
7604
|
this.lastAgents = allAgents;
|
|
7320
7605
|
try {
|
|
7321
|
-
this.events.emit("session.agents_updated", {
|
|
7606
|
+
this.events.emit("session.agents_updated", {
|
|
7607
|
+
sessionId: this.currentSessionId(),
|
|
7608
|
+
agents: allAgents
|
|
7609
|
+
});
|
|
7322
7610
|
} catch {
|
|
7323
7611
|
}
|
|
7324
7612
|
this.registry.updateAgents(allAgents).then(() => {
|
|
@@ -7328,6 +7616,16 @@ var AgentStatusTracker = class {
|
|
|
7328
7616
|
}
|
|
7329
7617
|
}).catch(() => void 0);
|
|
7330
7618
|
}
|
|
7619
|
+
currentSessionId() {
|
|
7620
|
+
return typeof this.sessionId === "function" ? this.sessionId() : this.sessionId;
|
|
7621
|
+
}
|
|
7622
|
+
acceptsSession(payload) {
|
|
7623
|
+
const expected = this.currentSessionId();
|
|
7624
|
+
if (!expected) return true;
|
|
7625
|
+
if (typeof payload !== "object" || payload === null) return true;
|
|
7626
|
+
const actual = payload.sessionId;
|
|
7627
|
+
return typeof actual !== "string" || actual.length === 0 || actual === expected;
|
|
7628
|
+
}
|
|
7331
7629
|
markLeaderStarted(startedAt) {
|
|
7332
7630
|
if (this.leaderStartedAt && (this.leaderStatus === "running" || this.leaderStatus === "streaming" || this.leaderStatus === "waiting_user")) {
|
|
7333
7631
|
return;
|
|
@@ -7432,8 +7730,11 @@ var DefaultSessionRewinder = class {
|
|
|
7432
7730
|
}
|
|
7433
7731
|
sessionsDir;
|
|
7434
7732
|
projectRoot;
|
|
7733
|
+
sessionFile(sessionId) {
|
|
7734
|
+
return sessionScopedPath(this.sessionsDir, sessionId, ".jsonl");
|
|
7735
|
+
}
|
|
7435
7736
|
async listCheckpoints(sessionId) {
|
|
7436
|
-
const file =
|
|
7737
|
+
const file = this.sessionFile(sessionId);
|
|
7437
7738
|
const raw = await fsp2.readFile(file, "utf8");
|
|
7438
7739
|
const events = parseEvents(raw);
|
|
7439
7740
|
const fileCountMap = /* @__PURE__ */ new Map();
|
|
@@ -7458,7 +7759,7 @@ var DefaultSessionRewinder = class {
|
|
|
7458
7759
|
return checkpoints;
|
|
7459
7760
|
}
|
|
7460
7761
|
async rewindToCheckpoint(sessionId, checkpointIndex) {
|
|
7461
|
-
const file =
|
|
7762
|
+
const file = this.sessionFile(sessionId);
|
|
7462
7763
|
const raw = await fsp2.readFile(file, "utf8");
|
|
7463
7764
|
const events = parseEvents(raw);
|
|
7464
7765
|
let targetIdx = -1;
|
|
@@ -7497,7 +7798,7 @@ var DefaultSessionRewinder = class {
|
|
|
7497
7798
|
return { ...result, toPromptIndex: checkpointIndex, removedEvents };
|
|
7498
7799
|
}
|
|
7499
7800
|
async rewindLastN(sessionId, n) {
|
|
7500
|
-
const file =
|
|
7801
|
+
const file = this.sessionFile(sessionId);
|
|
7501
7802
|
const raw = await fsp2.readFile(file, "utf8");
|
|
7502
7803
|
const events = parseEvents(raw);
|
|
7503
7804
|
const checkpoints = [];
|
|
@@ -7526,7 +7827,7 @@ var DefaultSessionRewinder = class {
|
|
|
7526
7827
|
return { ...result, toPromptIndex: targetIndex, removedEvents: snapshotsToRevert.length };
|
|
7527
7828
|
}
|
|
7528
7829
|
async rewindToStart(sessionId) {
|
|
7529
|
-
const file =
|
|
7830
|
+
const file = this.sessionFile(sessionId);
|
|
7530
7831
|
const raw = await fsp2.readFile(file, "utf8");
|
|
7531
7832
|
const events = parseEvents(raw);
|
|
7532
7833
|
const allSnapshots = [];
|
|
@@ -7911,7 +8212,12 @@ async function mutatePlan(filePath, sessionId, fn) {
|
|
|
7911
8212
|
const updated = await fn(plan);
|
|
7912
8213
|
const persisted = await savePlan(filePath, updated);
|
|
7913
8214
|
if (!persisted) {
|
|
7914
|
-
throw new
|
|
8215
|
+
throw new SessionError({
|
|
8216
|
+
message: `Failed to persist plan to ${filePath} \u2014 the change was NOT saved.`,
|
|
8217
|
+
code: "SESSION_WRITE_FAILED",
|
|
8218
|
+
sessionId,
|
|
8219
|
+
context: { filePath, operation: "mutatePlan" }
|
|
8220
|
+
});
|
|
7915
8221
|
}
|
|
7916
8222
|
return updated;
|
|
7917
8223
|
});
|
|
@@ -8135,7 +8441,12 @@ async function mutateTasks(filePath, sessionId, fn, events, traceId) {
|
|
|
8135
8441
|
const updated = await fn(file);
|
|
8136
8442
|
const persisted = await saveTasks(filePath, updated, events, traceId);
|
|
8137
8443
|
if (!persisted) {
|
|
8138
|
-
throw new
|
|
8444
|
+
throw new SessionError({
|
|
8445
|
+
message: `Failed to persist tasks to ${filePath} \u2014 the change was NOT saved.`,
|
|
8446
|
+
code: "SESSION_WRITE_FAILED",
|
|
8447
|
+
sessionId,
|
|
8448
|
+
context: { filePath, operation: "mutateTasks" }
|
|
8449
|
+
});
|
|
8139
8450
|
}
|
|
8140
8451
|
return updated;
|
|
8141
8452
|
});
|
|
@@ -8968,8 +9279,8 @@ var CloudSync = class {
|
|
|
8968
9279
|
const localPath = this.categoryToPath(cat);
|
|
8969
9280
|
if (!localPath) continue;
|
|
8970
9281
|
try {
|
|
8971
|
-
const
|
|
8972
|
-
if (
|
|
9282
|
+
const stat11 = await fsp2.stat(localPath);
|
|
9283
|
+
if (stat11.isDirectory()) {
|
|
8973
9284
|
const files = await this.walkDir(localPath, localPath);
|
|
8974
9285
|
for (const file of files) {
|
|
8975
9286
|
const content = await fsp2.readFile(file, "utf8");
|
|
@@ -8994,8 +9305,8 @@ var CloudSync = class {
|
|
|
8994
9305
|
const localPath = this.categoryToPath(cat);
|
|
8995
9306
|
if (!localPath) continue;
|
|
8996
9307
|
try {
|
|
8997
|
-
const
|
|
8998
|
-
if (
|
|
9308
|
+
const stat11 = await fsp2.stat(localPath);
|
|
9309
|
+
if (stat11.isDirectory()) {
|
|
8999
9310
|
const files = await this.walkDir(localPath, localPath);
|
|
9000
9311
|
for (const file of files) {
|
|
9001
9312
|
const content = await fsp2.readFile(file);
|
|
@@ -9201,6 +9512,6 @@ function resolveSessionLoggingConfig(cfg) {
|
|
|
9201
9512
|
};
|
|
9202
9513
|
}
|
|
9203
9514
|
|
|
9204
|
-
export { ALL_SYNC_CATEGORIES, AgentStatusTracker, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, FileMemoryBackend, FleetNotifier, GraphMemoryBackend, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, PromptUsageStore, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, emptyTaskFile, formatGoal, formatPlan, formatPlanTemplates, generateSessionId, getPlanTemplate, getSessionRegistry, goalFilePath, hasSessionRegistry, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTasks, loadTodosCheckpoint, migratePromptEntry, mutatePlan, mutateTasks, parseEntries, parseProgressFromText, promptChecksum, recordProgress, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, sanitizeModel, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, setPlanItemStatus, setProgress, summarizeUsage };
|
|
9515
|
+
export { ALL_SYNC_CATEGORIES, AgentStatusTracker, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, FileMemoryBackend, FleetNotifier, GraphMemoryBackend, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, PromptUsageStore, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, emptyTaskFile, formatGoal, formatPlan, formatPlanTemplates, generateSessionId, getPlanTemplate, getSessionRegistry, goalFilePath, hasSessionRegistry, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTasks, loadTodosCheckpoint, migratePromptEntry, mutatePlan, mutateTasks, parseEntries, parseProgressFromText, promptChecksum, recordProgress, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, sanitizeModel, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, setPlanItemStatus, setProgress, summarizeUsage, watchProviderConfig };
|
|
9205
9516
|
//# sourceMappingURL=index.js.map
|
|
9206
9517
|
//# sourceMappingURL=index.js.map
|