@wrongstack/core 0.260.0 → 0.264.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
- package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +11 -9
- package/dist/{brain-C2yDd7Lw.d.ts → brain-O1IdKPaK.d.ts} +2 -2
- package/dist/{compactor-t0R_AIt_.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
- package/dist/{config-FG6As4H5.d.ts → config-Dz2F3H2K.d.ts} +7 -1
- package/dist/{context-JFOVvu6z.d.ts → context-BGSpZNSE.d.ts} +11 -0
- package/dist/coordination/index.d.ts +1681 -15
- package/dist/coordination/index.js +2648 -388
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +1414 -1387
- package/dist/defaults/index.js.map +1 -1
- package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +410 -388
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +7 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
- package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-CxWmCGbH.d.ts} +1 -1
- package/dist/{index-CebbJB94.d.ts → index-CYIQrXVF.d.ts} +8 -8
- package/dist/{index-BPcg4N3M.d.ts → index-CbLSI66_.d.ts} +5 -5
- package/dist/index.d.ts +45 -91
- package/dist/index.js +14976 -12551
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +6 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
- package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-DC4QRPUI.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +6 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
- package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +8 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +9 -9
- package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
- package/dist/{permission-BJ7eO9Vl.d.ts → permission-4yvGmMRB.d.ts} +1 -1
- package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-C6XpsBOy.d.ts} +2 -2
- package/dist/{pipeline-zflkI2dp.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
- package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-BvzRBkJc.d.ts} +5 -5
- package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
- package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +39 -29
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -4
- package/dist/security/index.js +208 -203
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-B7AivHsu.d.ts → selector-gIuhRTkN.d.ts} +1 -1
- package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
- package/dist/{session-reader-DtofsB-2.d.ts → session-reader-KdfVwkKP.d.ts} +1 -1
- package/dist/skills/index.js +67 -64
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +31 -12
- package/dist/storage/index.js +441 -360
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +57 -0
- package/dist/tools/index.js +411 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +703 -694
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error.d.ts +7 -0
- package/dist/utils/error.js +8 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +7 -67
- package/dist/utils/index.js +17 -5
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
package/dist/storage/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { randomBytes, randomUUID, createHash } from 'crypto';
|
|
2
2
|
import * as fsp from 'fs/promises';
|
|
3
|
-
import * as
|
|
4
|
-
import 'fs';
|
|
3
|
+
import * as path2 from 'path';
|
|
5
4
|
import * as os from 'os';
|
|
6
5
|
import { hostname } from 'os';
|
|
6
|
+
import 'fs';
|
|
7
7
|
|
|
8
8
|
// src/utils/expect-defined.ts
|
|
9
9
|
function expectDefined(value, label) {
|
|
@@ -15,9 +15,9 @@ function expectDefined(value, label) {
|
|
|
15
15
|
return value;
|
|
16
16
|
}
|
|
17
17
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
18
|
-
const dir =
|
|
18
|
+
const dir = path2.dirname(targetPath);
|
|
19
19
|
await fsp.mkdir(dir, { recursive: true });
|
|
20
|
-
const tmp =
|
|
20
|
+
const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
21
21
|
try {
|
|
22
22
|
if (typeof content === "string") {
|
|
23
23
|
await fsp.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -35,8 +35,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
35
35
|
}
|
|
36
36
|
let mode;
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
39
|
-
mode =
|
|
38
|
+
const stat6 = await fsp.stat(targetPath);
|
|
39
|
+
mode = stat6.mode & 511;
|
|
40
40
|
} catch {
|
|
41
41
|
mode = opts.mode;
|
|
42
42
|
}
|
|
@@ -56,9 +56,9 @@ async function ensureDir(dir) {
|
|
|
56
56
|
await fsp.mkdir(dir, { recursive: true });
|
|
57
57
|
}
|
|
58
58
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
59
|
-
const dir =
|
|
59
|
+
const dir = path2.dirname(targetPath);
|
|
60
60
|
await fsp.mkdir(dir, { recursive: true });
|
|
61
|
-
const lockPath =
|
|
61
|
+
const lockPath = path2.join(dir, `.${path2.basename(targetPath)}.lock`);
|
|
62
62
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
63
63
|
const staleMs = opts.staleMs ?? 3e4;
|
|
64
64
|
const started = Date.now();
|
|
@@ -69,10 +69,15 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
69
69
|
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
70
70
|
break;
|
|
71
71
|
} catch (err) {
|
|
72
|
-
|
|
72
|
+
const code = err.code;
|
|
73
|
+
if (code === "ENOENT") {
|
|
74
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (code !== "EEXIST") throw err;
|
|
73
78
|
try {
|
|
74
|
-
const
|
|
75
|
-
if (Date.now() -
|
|
79
|
+
const stat6 = await fsp.stat(lockPath);
|
|
80
|
+
if (Date.now() - stat6.mtimeMs > staleMs) {
|
|
76
81
|
await fsp.unlink(lockPath);
|
|
77
82
|
continue;
|
|
78
83
|
}
|
|
@@ -214,6 +219,215 @@ function isEmptyMessage(msg) {
|
|
|
214
219
|
return msg.content.length === 0;
|
|
215
220
|
}
|
|
216
221
|
|
|
222
|
+
// src/utils/error.ts
|
|
223
|
+
function toErrorMessage(err) {
|
|
224
|
+
return err instanceof Error ? err.message : String(err);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/utils/safe-json.ts
|
|
228
|
+
function safeParse(input, maxBytes = 5e6) {
|
|
229
|
+
if (input.length > maxBytes) {
|
|
230
|
+
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
return { ok: true, value: JSON.parse(input) };
|
|
234
|
+
} catch (err) {
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
error: toErrorMessage(err)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/utils/term.ts
|
|
243
|
+
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
244
|
+
function isStdoutTTY() {
|
|
245
|
+
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/utils/color.ts
|
|
249
|
+
var isColorTty = () => {
|
|
250
|
+
if (envFlag(process.env.NO_COLOR)) return false;
|
|
251
|
+
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
252
|
+
return isStdoutTTY();
|
|
253
|
+
};
|
|
254
|
+
function envFlag(value) {
|
|
255
|
+
if (value === void 0) return false;
|
|
256
|
+
if (value.trim() === "") return false;
|
|
257
|
+
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
258
|
+
}
|
|
259
|
+
var COLOR = isColorTty();
|
|
260
|
+
var wrap = (open6, close) => (s) => COLOR ? `\x1B[${open6}m${s}\x1B[${close}m` : s;
|
|
261
|
+
var color = {
|
|
262
|
+
reset: wrap("0", "0"),
|
|
263
|
+
bold: wrap("1", "22"),
|
|
264
|
+
dim: wrap("2", "22"),
|
|
265
|
+
italic: wrap("3", "23"),
|
|
266
|
+
underline: wrap("4", "24"),
|
|
267
|
+
red: wrap("31", "39"),
|
|
268
|
+
green: wrap("32", "39"),
|
|
269
|
+
yellow: wrap("33", "39"),
|
|
270
|
+
blue: wrap("34", "39"),
|
|
271
|
+
magenta: wrap("35", "39"),
|
|
272
|
+
cyan: wrap("36", "39"),
|
|
273
|
+
gray: wrap("90", "39"),
|
|
274
|
+
amber: wrap("38;5;214", "39"),
|
|
275
|
+
pink: wrap("38;5;205", "39"),
|
|
276
|
+
bgRed: wrap("41", "49"),
|
|
277
|
+
bgGreen: wrap("42", "49")
|
|
278
|
+
};
|
|
279
|
+
function projectHash(absRoot) {
|
|
280
|
+
return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
281
|
+
}
|
|
282
|
+
function projectSlug(absRoot) {
|
|
283
|
+
const base = slugify(path2.basename(absRoot));
|
|
284
|
+
const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
285
|
+
return `${base}-${hash}`;
|
|
286
|
+
}
|
|
287
|
+
function slugify(name) {
|
|
288
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
289
|
+
}
|
|
290
|
+
function wstackGlobalRoot() {
|
|
291
|
+
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
292
|
+
if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
|
|
293
|
+
return path2.join(os.homedir(), ".wrongstack");
|
|
294
|
+
}
|
|
295
|
+
function resolveWstackPaths(opts) {
|
|
296
|
+
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
297
|
+
const hash = projectHash(opts.projectRoot);
|
|
298
|
+
const slug = projectSlug(opts.projectRoot);
|
|
299
|
+
const projectDir = path2.join(globalRoot, "projects", slug);
|
|
300
|
+
return {
|
|
301
|
+
globalRoot,
|
|
302
|
+
configDir: globalRoot,
|
|
303
|
+
globalConfig: path2.join(globalRoot, "config.json"),
|
|
304
|
+
secretsKey: path2.join(globalRoot, ".key"),
|
|
305
|
+
globalMemory: path2.join(globalRoot, "memory.md"),
|
|
306
|
+
globalSkills: path2.join(globalRoot, "skills"),
|
|
307
|
+
globalPrompts: path2.join(globalRoot, "prompts"),
|
|
308
|
+
cacheDir: path2.join(globalRoot, "cache"),
|
|
309
|
+
modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
|
|
310
|
+
modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
|
|
311
|
+
historyFile: path2.join(globalRoot, "history"),
|
|
312
|
+
logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
|
|
313
|
+
projectDir,
|
|
314
|
+
projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
|
|
315
|
+
projectMemory: path2.join(projectDir, "memory.md"),
|
|
316
|
+
projectSessions: path2.join(projectDir, "sessions"),
|
|
317
|
+
projectTrust: path2.join(projectDir, "trust.json"),
|
|
318
|
+
projectMeta: path2.join(projectDir, "meta.json"),
|
|
319
|
+
projectLocalConfig: path2.join(projectDir, "config.local.json"),
|
|
320
|
+
inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
321
|
+
inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
322
|
+
inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
323
|
+
inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
324
|
+
projectHash: hash,
|
|
325
|
+
projectSlug: slug,
|
|
326
|
+
projectGoal: path2.join(projectDir, "goal.json"),
|
|
327
|
+
projectSpecs: path2.join(projectDir, "specs"),
|
|
328
|
+
projectTaskGraphs: path2.join(projectDir, "task-graphs"),
|
|
329
|
+
projectSddSession: path2.join(projectDir, "sdd-session.json"),
|
|
330
|
+
projectPlan: path2.join(projectDir, "plan.json"),
|
|
331
|
+
projectAutophase: path2.join(projectDir, "autophase"),
|
|
332
|
+
syncConfig: path2.join(globalRoot, "sync.json")
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/utils/deep-merge.ts
|
|
337
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
338
|
+
"__proto__",
|
|
339
|
+
"constructor",
|
|
340
|
+
"prototype",
|
|
341
|
+
"__defineGetter__",
|
|
342
|
+
"__defineSetter__",
|
|
343
|
+
"__lookupGetter__",
|
|
344
|
+
"__lookupSetter__"
|
|
345
|
+
]);
|
|
346
|
+
function isPrimitiveArray(a) {
|
|
347
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
348
|
+
}
|
|
349
|
+
function deepMerge(base, patch, options = {}) {
|
|
350
|
+
const {
|
|
351
|
+
conflictResolution = "prefer-patch",
|
|
352
|
+
arrayMode = "replace",
|
|
353
|
+
protectProto = true,
|
|
354
|
+
onNonPrimitiveArrayReplace
|
|
355
|
+
} = options;
|
|
356
|
+
if (typeof base !== "object" || base === null) {
|
|
357
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
358
|
+
}
|
|
359
|
+
if (typeof patch !== "object" || patch === null) {
|
|
360
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
361
|
+
}
|
|
362
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
363
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
364
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
365
|
+
}
|
|
366
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
369
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
370
|
+
}
|
|
371
|
+
const baseObj = base;
|
|
372
|
+
const patchObj = patch;
|
|
373
|
+
const out = { ...baseObj };
|
|
374
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
375
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
376
|
+
const existing = out[k];
|
|
377
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
378
|
+
out[k] = deepMerge(existing, v, options);
|
|
379
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
380
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
381
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
382
|
+
}
|
|
383
|
+
out[k] = deepMerge(existing, v, options);
|
|
384
|
+
} else if (v !== void 0) {
|
|
385
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
386
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
387
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
388
|
+
}
|
|
389
|
+
out[k] = v;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/utils/regex-guard.ts
|
|
396
|
+
var MAX_PATTERN_LEN = 512;
|
|
397
|
+
var DANGEROUS_PATTERNS = [
|
|
398
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
399
|
+
// (a+)+, (.*)+, etc
|
|
400
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
401
|
+
// same, with non-capturing group
|
|
402
|
+
];
|
|
403
|
+
function compileUserRegex(pattern, flags) {
|
|
404
|
+
if (typeof pattern !== "string") {
|
|
405
|
+
return { ok: false, reason: "pattern must be a string" };
|
|
406
|
+
}
|
|
407
|
+
if (pattern.length === 0) {
|
|
408
|
+
return { ok: false, reason: "pattern is empty" };
|
|
409
|
+
}
|
|
410
|
+
if (pattern.length > MAX_PATTERN_LEN) {
|
|
411
|
+
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
412
|
+
}
|
|
413
|
+
for (const rx of DANGEROUS_PATTERNS) {
|
|
414
|
+
if (rx.test(pattern)) {
|
|
415
|
+
return {
|
|
416
|
+
ok: false,
|
|
417
|
+
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
423
|
+
} catch (err) {
|
|
424
|
+
return {
|
|
425
|
+
ok: false,
|
|
426
|
+
reason: err instanceof Error ? err.message : "invalid regex"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
217
431
|
// src/storage/session-store.ts
|
|
218
432
|
function sanitizeModel(model) {
|
|
219
433
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
@@ -270,11 +484,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
270
484
|
}
|
|
271
485
|
/** Absolute path to the session index file. */
|
|
272
486
|
get indexFile() {
|
|
273
|
-
return
|
|
487
|
+
return path2.join(this.dir, "_index.jsonl");
|
|
274
488
|
}
|
|
275
489
|
/** Join session ID to its absolute path within the store directory. */
|
|
276
490
|
sessionPath(id, ext) {
|
|
277
|
-
return
|
|
491
|
+
return path2.join(this.dir, `${id}${ext}`);
|
|
278
492
|
}
|
|
279
493
|
/**
|
|
280
494
|
* Ensure the directory implied by the session ID exists. When the ID
|
|
@@ -282,7 +496,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
282
496
|
* subdirectory so sessions group naturally by day.
|
|
283
497
|
*/
|
|
284
498
|
async ensureShardDir(id) {
|
|
285
|
-
const dirPath =
|
|
499
|
+
const dirPath = path2.dirname(path2.join(this.dir, id));
|
|
286
500
|
await ensureDir(dirPath);
|
|
287
501
|
return dirPath;
|
|
288
502
|
}
|
|
@@ -290,15 +504,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
290
504
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
291
505
|
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
292
506
|
const shardDir = await this.ensureShardDir(id);
|
|
293
|
-
const file =
|
|
507
|
+
const file = path2.join(shardDir, `${path2.basename(id)}.jsonl`);
|
|
294
508
|
const t0 = Date.now();
|
|
295
509
|
let handle;
|
|
296
510
|
try {
|
|
297
511
|
handle = await fsp.open(file, "a", 384);
|
|
298
512
|
} catch (err) {
|
|
299
|
-
this.emitError(id, file, "create",
|
|
513
|
+
this.emitError(id, file, "create", toErrorMessage(err), false);
|
|
300
514
|
throw new Error(
|
|
301
|
-
`Failed to open session file: ${
|
|
515
|
+
`Failed to open session file: ${toErrorMessage(err)}`,
|
|
302
516
|
{ cause: err }
|
|
303
517
|
);
|
|
304
518
|
}
|
|
@@ -318,7 +532,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
318
532
|
message: e instanceof Error ? e.message : String(e),
|
|
319
533
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
320
534
|
})));
|
|
321
|
-
this.emitError(id, file, "create",
|
|
535
|
+
this.emitError(id, file, "create", toErrorMessage(err), true);
|
|
322
536
|
throw err;
|
|
323
537
|
}
|
|
324
538
|
}
|
|
@@ -330,9 +544,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
330
544
|
try {
|
|
331
545
|
handle = await fsp.open(file, "a", 384);
|
|
332
546
|
} catch (err) {
|
|
333
|
-
this.emitError(id, file, "resume",
|
|
547
|
+
this.emitError(id, file, "resume", toErrorMessage(err), false);
|
|
334
548
|
throw new Error(
|
|
335
|
-
`Failed to open session "${id}" for append: ${
|
|
549
|
+
`Failed to open session "${id}" for append: ${toErrorMessage(err)}`,
|
|
336
550
|
{ cause: err }
|
|
337
551
|
);
|
|
338
552
|
}
|
|
@@ -352,7 +566,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
352
566
|
// Shard directory (sessions/<date>/) — must match create() so the
|
|
353
567
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
354
568
|
// sessions root (where summaryFor() would never find it).
|
|
355
|
-
dir:
|
|
569
|
+
dir: path2.dirname(file),
|
|
356
570
|
filePath: file,
|
|
357
571
|
secretScrubber: this.secretScrubber,
|
|
358
572
|
onClose: (s) => this.appendToIndex(s)
|
|
@@ -367,7 +581,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
367
581
|
message: e instanceof Error ? e.message : String(e),
|
|
368
582
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
369
583
|
})));
|
|
370
|
-
this.emitError(id, file, "resume",
|
|
584
|
+
this.emitError(id, file, "resume", toErrorMessage(err), true);
|
|
371
585
|
throw err;
|
|
372
586
|
}
|
|
373
587
|
}
|
|
@@ -395,7 +609,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
395
609
|
return { metadata: meta, events, messages, usage, toolCallEnds };
|
|
396
610
|
} catch (err) {
|
|
397
611
|
outcome = "failure";
|
|
398
|
-
errorMsg =
|
|
612
|
+
errorMsg = toErrorMessage(err);
|
|
399
613
|
throw err;
|
|
400
614
|
} finally {
|
|
401
615
|
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
@@ -478,7 +692,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
478
692
|
await fsp.rename(tmp, this.indexFile);
|
|
479
693
|
} catch (err) {
|
|
480
694
|
outcome = "failure";
|
|
481
|
-
errorMsg =
|
|
695
|
+
errorMsg = toErrorMessage(err);
|
|
482
696
|
} finally {
|
|
483
697
|
this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
|
|
484
698
|
}
|
|
@@ -546,7 +760,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
546
760
|
continue;
|
|
547
761
|
if (entry.isDirectory()) {
|
|
548
762
|
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
549
|
-
ids.push(...await this.collectSessionIds(
|
|
763
|
+
ids.push(...await this.collectSessionIds(path2.join(dir, entry.name), childPrefix, depth + 1));
|
|
550
764
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
551
765
|
if (entry.name === "_index.jsonl") continue;
|
|
552
766
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
@@ -566,10 +780,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
566
780
|
return JSON.parse(raw);
|
|
567
781
|
} catch {
|
|
568
782
|
const full = this.sessionPath(id, ".jsonl");
|
|
569
|
-
const
|
|
570
|
-
const summary = await this.summarize(id,
|
|
783
|
+
const stat6 = await fsp.stat(full);
|
|
784
|
+
const summary = await this.summarize(id, stat6.mtime.toISOString());
|
|
571
785
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
572
|
-
const msg =
|
|
786
|
+
const msg = toErrorMessage(err);
|
|
573
787
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
574
788
|
console.warn(JSON.stringify({
|
|
575
789
|
level: "warn",
|
|
@@ -597,14 +811,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
597
811
|
async deleteSession(id) {
|
|
598
812
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
599
813
|
const summaryPath = this.sessionPath(id, ".summary.json");
|
|
600
|
-
const shardDir =
|
|
601
|
-
const base =
|
|
602
|
-
const sessDir =
|
|
814
|
+
const shardDir = path2.dirname(path2.join(this.dir, id));
|
|
815
|
+
const base = path2.basename(id);
|
|
816
|
+
const sessDir = path2.join(shardDir, base);
|
|
603
817
|
const deletions = [
|
|
604
818
|
fsp.unlink(jsonlPath),
|
|
605
819
|
fsp.unlink(summaryPath),
|
|
606
|
-
fsp.unlink(
|
|
607
|
-
fsp.unlink(
|
|
820
|
+
fsp.unlink(path2.join(shardDir, `${base}.plan.json`)),
|
|
821
|
+
fsp.unlink(path2.join(shardDir, `${base}.todos.json`))
|
|
608
822
|
];
|
|
609
823
|
const results = await Promise.allSettled(deletions);
|
|
610
824
|
for (const r of results) {
|
|
@@ -626,7 +840,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
626
840
|
level: "warn",
|
|
627
841
|
event: "session_store.rmdir_failed",
|
|
628
842
|
sessionId: id,
|
|
629
|
-
message:
|
|
843
|
+
message: toErrorMessage(err),
|
|
630
844
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
631
845
|
}));
|
|
632
846
|
});
|
|
@@ -640,17 +854,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
640
854
|
let deleted = 0;
|
|
641
855
|
let activeSessionId = null;
|
|
642
856
|
try {
|
|
643
|
-
const raw = await fsp.readFile(
|
|
857
|
+
const raw = await fsp.readFile(path2.join(this.dir, "active.json"), "utf8");
|
|
644
858
|
const active = JSON.parse(raw);
|
|
645
859
|
activeSessionId = active.sessionId ?? null;
|
|
646
860
|
} catch {
|
|
647
861
|
}
|
|
648
862
|
const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
649
863
|
const pruneFile = async (dir, name, prefix) => {
|
|
650
|
-
const jsonlPath =
|
|
864
|
+
const jsonlPath = path2.join(dir, name);
|
|
651
865
|
try {
|
|
652
|
-
const
|
|
653
|
-
if (
|
|
866
|
+
const stat6 = await fsp.stat(jsonlPath);
|
|
867
|
+
if (stat6.mtimeMs >= cutoff) return;
|
|
654
868
|
} catch {
|
|
655
869
|
return;
|
|
656
870
|
}
|
|
@@ -667,7 +881,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
667
881
|
continue;
|
|
668
882
|
}
|
|
669
883
|
if (!entry.isDirectory()) continue;
|
|
670
|
-
const dateDir =
|
|
884
|
+
const dateDir = path2.join(this.dir, entry.name);
|
|
671
885
|
const files = await fsp.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
672
886
|
for (const file of files) {
|
|
673
887
|
if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
|
|
@@ -679,7 +893,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
679
893
|
}
|
|
680
894
|
for (const entry of entries) {
|
|
681
895
|
if (!entry.isDirectory()) continue;
|
|
682
|
-
const dateDir =
|
|
896
|
+
const dateDir = path2.join(this.dir, entry.name);
|
|
683
897
|
try {
|
|
684
898
|
const remaining = await fsp.readdir(dateDir);
|
|
685
899
|
if (remaining.length === 0) {
|
|
@@ -854,7 +1068,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
854
1068
|
this.meta = meta;
|
|
855
1069
|
this.events = events;
|
|
856
1070
|
this.resumed = opts.resumed ?? false;
|
|
857
|
-
this.manifestFile = opts.dir ?
|
|
1071
|
+
this.manifestFile = opts.dir ? path2.join(opts.dir, `${path2.basename(id)}.summary.json`) : "";
|
|
858
1072
|
this.filePath = opts.filePath ?? "";
|
|
859
1073
|
this.secretScrubber = opts.secretScrubber;
|
|
860
1074
|
this.onCloseCb = opts.onClose;
|
|
@@ -1061,7 +1275,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1061
1275
|
await this.enqueueWrite(batch);
|
|
1062
1276
|
} catch (err) {
|
|
1063
1277
|
outcome = "failure";
|
|
1064
|
-
errorMsg =
|
|
1278
|
+
errorMsg = toErrorMessage(err);
|
|
1065
1279
|
this.appendFailCount += eventCount;
|
|
1066
1280
|
const now = Date.now();
|
|
1067
1281
|
if (now - this.lastAppendWarnAt > 5e3) {
|
|
@@ -1069,7 +1283,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1069
1283
|
const tail = suppressed > 0 ? ` (+${suppressed} suppressed)` : "";
|
|
1070
1284
|
console.warn(
|
|
1071
1285
|
"[session] flush failed:",
|
|
1072
|
-
|
|
1286
|
+
toErrorMessage(err),
|
|
1073
1287
|
tail
|
|
1074
1288
|
);
|
|
1075
1289
|
this.lastAppendWarnAt = now;
|
|
@@ -1159,7 +1373,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1159
1373
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
1160
1374
|
} catch (err) {
|
|
1161
1375
|
outcome = "failure";
|
|
1162
|
-
errorMsg =
|
|
1376
|
+
errorMsg = toErrorMessage(err);
|
|
1163
1377
|
} finally {
|
|
1164
1378
|
this.events?.emit("storage.write", {
|
|
1165
1379
|
sessionId: this.id,
|
|
@@ -1180,7 +1394,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1180
1394
|
await this.onCloseCb?.(this.summary);
|
|
1181
1395
|
} catch (err) {
|
|
1182
1396
|
idxOutcome = "failure";
|
|
1183
|
-
idxError =
|
|
1397
|
+
idxError = toErrorMessage(err);
|
|
1184
1398
|
} finally {
|
|
1185
1399
|
this.events?.emit("storage.write", {
|
|
1186
1400
|
sessionId: this.summary.id,
|
|
@@ -1355,7 +1569,7 @@ var QueueStore = class {
|
|
|
1355
1569
|
events;
|
|
1356
1570
|
traceId;
|
|
1357
1571
|
constructor(opts) {
|
|
1358
|
-
this.file =
|
|
1572
|
+
this.file = path2.join(opts.dir, "queue.json");
|
|
1359
1573
|
this.events = opts.events;
|
|
1360
1574
|
this.traceId = opts.traceId;
|
|
1361
1575
|
}
|
|
@@ -1383,7 +1597,7 @@ var QueueStore = class {
|
|
|
1383
1597
|
filePath: this.file,
|
|
1384
1598
|
operation: "write",
|
|
1385
1599
|
outcome: "failure",
|
|
1386
|
-
error:
|
|
1600
|
+
error: toErrorMessage(err),
|
|
1387
1601
|
recoverable: false,
|
|
1388
1602
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1389
1603
|
});
|
|
@@ -1391,7 +1605,7 @@ var QueueStore = class {
|
|
|
1391
1605
|
level: "warn",
|
|
1392
1606
|
event: "queue_store.write_failed",
|
|
1393
1607
|
path: this.file,
|
|
1394
|
-
message:
|
|
1608
|
+
message: toErrorMessage(err),
|
|
1395
1609
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1396
1610
|
}));
|
|
1397
1611
|
}
|
|
@@ -1421,7 +1635,7 @@ var QueueStore = class {
|
|
|
1421
1635
|
filePath: this.file,
|
|
1422
1636
|
operation: "read",
|
|
1423
1637
|
outcome: "failure",
|
|
1424
|
-
error:
|
|
1638
|
+
error: toErrorMessage(err),
|
|
1425
1639
|
recoverable: true,
|
|
1426
1640
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1427
1641
|
});
|
|
@@ -1429,7 +1643,7 @@ var QueueStore = class {
|
|
|
1429
1643
|
level: "warn",
|
|
1430
1644
|
event: "queue_store.read_failed",
|
|
1431
1645
|
path: this.file,
|
|
1432
|
-
message:
|
|
1646
|
+
message: toErrorMessage(err),
|
|
1433
1647
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1434
1648
|
}));
|
|
1435
1649
|
return [];
|
|
@@ -1500,7 +1714,7 @@ var QueueStore = class {
|
|
|
1500
1714
|
filePath: this.file,
|
|
1501
1715
|
operation: "clear",
|
|
1502
1716
|
outcome: "failure",
|
|
1503
|
-
error:
|
|
1717
|
+
error: toErrorMessage(err),
|
|
1504
1718
|
recoverable: true,
|
|
1505
1719
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1506
1720
|
});
|
|
@@ -1539,7 +1753,7 @@ var DefaultAttachmentStore = class {
|
|
|
1539
1753
|
let data = input.data;
|
|
1540
1754
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
1541
1755
|
await fsp.mkdir(this.spoolDir, { recursive: true });
|
|
1542
|
-
spooledPath =
|
|
1756
|
+
spooledPath = path2.join(this.spoolDir, `${id}.bin`);
|
|
1543
1757
|
await atomicWrite(spooledPath, input.data, {
|
|
1544
1758
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
1545
1759
|
});
|
|
@@ -1750,7 +1964,7 @@ var FileMemoryBackend = class {
|
|
|
1750
1964
|
}
|
|
1751
1965
|
async remember(scope, entry, filePath) {
|
|
1752
1966
|
const file = this.resolveFile(filePath, scope);
|
|
1753
|
-
await ensureDir(
|
|
1967
|
+
await ensureDir(path2.dirname(file));
|
|
1754
1968
|
let existing = "";
|
|
1755
1969
|
try {
|
|
1756
1970
|
existing = await fsp.readFile(file, "utf8");
|
|
@@ -1978,7 +2192,7 @@ ${body.trim()}`);
|
|
|
1978
2192
|
operation: "readAll",
|
|
1979
2193
|
outcome: "failure",
|
|
1980
2194
|
durationMs: dur,
|
|
1981
|
-
error:
|
|
2195
|
+
error: toErrorMessage(err),
|
|
1982
2196
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1983
2197
|
});
|
|
1984
2198
|
throw err;
|
|
@@ -2011,7 +2225,7 @@ ${body.trim()}`);
|
|
|
2011
2225
|
operation: "read",
|
|
2012
2226
|
outcome: "failure",
|
|
2013
2227
|
durationMs: dur,
|
|
2014
|
-
error:
|
|
2228
|
+
error: toErrorMessage(err),
|
|
2015
2229
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2016
2230
|
});
|
|
2017
2231
|
throw err;
|
|
@@ -2064,7 +2278,7 @@ ${body.trim()}`);
|
|
|
2064
2278
|
operation: "remember",
|
|
2065
2279
|
outcome: "failure",
|
|
2066
2280
|
durationMs: dur,
|
|
2067
|
-
error:
|
|
2281
|
+
error: toErrorMessage(err),
|
|
2068
2282
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2069
2283
|
});
|
|
2070
2284
|
throw err;
|
|
@@ -2220,7 +2434,7 @@ ${body.trim()}`);
|
|
|
2220
2434
|
operation: "forget",
|
|
2221
2435
|
outcome: "failure",
|
|
2222
2436
|
durationMs: dur,
|
|
2223
|
-
error:
|
|
2437
|
+
error: toErrorMessage(err),
|
|
2224
2438
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2225
2439
|
});
|
|
2226
2440
|
throw err;
|
|
@@ -2262,7 +2476,7 @@ ${body.trim()}`);
|
|
|
2262
2476
|
operation: "consolidate",
|
|
2263
2477
|
outcome: "failure",
|
|
2264
2478
|
durationMs: dur,
|
|
2265
|
-
error:
|
|
2479
|
+
error: toErrorMessage(err),
|
|
2266
2480
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2267
2481
|
});
|
|
2268
2482
|
throw err;
|
|
@@ -2302,7 +2516,7 @@ ${body.trim()}`);
|
|
|
2302
2516
|
operation: "clear",
|
|
2303
2517
|
outcome: "failure",
|
|
2304
2518
|
durationMs: dur,
|
|
2305
|
-
error:
|
|
2519
|
+
error: toErrorMessage(err),
|
|
2306
2520
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2307
2521
|
});
|
|
2308
2522
|
throw err;
|
|
@@ -2338,7 +2552,7 @@ ${body.trim()}`);
|
|
|
2338
2552
|
operation: "clear",
|
|
2339
2553
|
outcome: "failure",
|
|
2340
2554
|
durationMs: dur,
|
|
2341
|
-
error:
|
|
2555
|
+
error: toErrorMessage(err),
|
|
2342
2556
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2343
2557
|
});
|
|
2344
2558
|
throw err;
|
|
@@ -2899,7 +3113,7 @@ var DefaultConfigStore = class {
|
|
|
2899
3113
|
console.error(JSON.stringify({
|
|
2900
3114
|
level: "error",
|
|
2901
3115
|
event: "config_store.watcher_threw",
|
|
2902
|
-
message:
|
|
3116
|
+
message: toErrorMessage(err),
|
|
2903
3117
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2904
3118
|
}));
|
|
2905
3119
|
}
|
|
@@ -2923,67 +3137,6 @@ function deepFreeze(obj) {
|
|
|
2923
3137
|
}
|
|
2924
3138
|
return Object.freeze(obj);
|
|
2925
3139
|
}
|
|
2926
|
-
|
|
2927
|
-
// src/utils/deep-merge.ts
|
|
2928
|
-
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
2929
|
-
"__proto__",
|
|
2930
|
-
"constructor",
|
|
2931
|
-
"prototype",
|
|
2932
|
-
"__defineGetter__",
|
|
2933
|
-
"__defineSetter__",
|
|
2934
|
-
"__lookupGetter__",
|
|
2935
|
-
"__lookupSetter__"
|
|
2936
|
-
]);
|
|
2937
|
-
function isPrimitiveArray(a) {
|
|
2938
|
-
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
2939
|
-
}
|
|
2940
|
-
function deepMerge(base, patch, options = {}) {
|
|
2941
|
-
const {
|
|
2942
|
-
conflictResolution = "prefer-patch",
|
|
2943
|
-
arrayMode = "replace",
|
|
2944
|
-
protectProto = true,
|
|
2945
|
-
onNonPrimitiveArrayReplace
|
|
2946
|
-
} = options;
|
|
2947
|
-
if (typeof base !== "object" || base === null) {
|
|
2948
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2949
|
-
}
|
|
2950
|
-
if (typeof patch !== "object" || patch === null) {
|
|
2951
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2952
|
-
}
|
|
2953
|
-
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
2954
|
-
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
2955
|
-
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
2956
|
-
}
|
|
2957
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2958
|
-
}
|
|
2959
|
-
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
2960
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2961
|
-
}
|
|
2962
|
-
const baseObj = base;
|
|
2963
|
-
const patchObj = patch;
|
|
2964
|
-
const out = { ...baseObj };
|
|
2965
|
-
for (const [k, v] of Object.entries(patchObj)) {
|
|
2966
|
-
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
2967
|
-
const existing = out[k];
|
|
2968
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
2969
|
-
out[k] = deepMerge(existing, v, options);
|
|
2970
|
-
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
2971
|
-
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
2972
|
-
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
2973
|
-
}
|
|
2974
|
-
out[k] = deepMerge(existing, v, options);
|
|
2975
|
-
} else if (v !== void 0) {
|
|
2976
|
-
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
2977
|
-
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
2978
|
-
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
2979
|
-
}
|
|
2980
|
-
out[k] = v;
|
|
2981
|
-
}
|
|
2982
|
-
}
|
|
2983
|
-
return out;
|
|
2984
|
-
}
|
|
2985
|
-
|
|
2986
|
-
// src/security/secret-vault.ts
|
|
2987
3140
|
function decryptConfigSecrets(cfg, vault, opts) {
|
|
2988
3141
|
const warn = ((msg) => console.warn(msg));
|
|
2989
3142
|
return walk(cfg, vault, (v, key) => {
|
|
@@ -3074,21 +3227,6 @@ function isContextWindowModeId(id) {
|
|
|
3074
3227
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
3075
3228
|
}
|
|
3076
3229
|
|
|
3077
|
-
// src/utils/safe-json.ts
|
|
3078
|
-
function safeParse(input, maxBytes = 5e6) {
|
|
3079
|
-
if (input.length > maxBytes) {
|
|
3080
|
-
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
3081
|
-
}
|
|
3082
|
-
try {
|
|
3083
|
-
return { ok: true, value: JSON.parse(input) };
|
|
3084
|
-
} catch (err) {
|
|
3085
|
-
return {
|
|
3086
|
-
ok: false,
|
|
3087
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3088
|
-
};
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
3230
|
// src/types/default-config.ts
|
|
3093
3231
|
var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
3094
3232
|
defaultExecutionStrategy: "smart",
|
|
@@ -3261,7 +3399,7 @@ var DefaultConfigLoader = class {
|
|
|
3261
3399
|
level: "warn",
|
|
3262
3400
|
event: "config.source_load_failed",
|
|
3263
3401
|
source: src.name,
|
|
3264
|
-
message:
|
|
3402
|
+
message: toErrorMessage(err),
|
|
3265
3403
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3266
3404
|
}));
|
|
3267
3405
|
}
|
|
@@ -3398,7 +3536,7 @@ var DefaultConfigLoader = class {
|
|
|
3398
3536
|
console.warn(JSON.stringify({
|
|
3399
3537
|
level: "warn",
|
|
3400
3538
|
event: "config.sync_load_failed",
|
|
3401
|
-
message:
|
|
3539
|
+
message: toErrorMessage(err),
|
|
3402
3540
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3403
3541
|
}));
|
|
3404
3542
|
return null;
|
|
@@ -3425,7 +3563,7 @@ var DefaultConfigLoader = class {
|
|
|
3425
3563
|
level: "warn",
|
|
3426
3564
|
event: "config.read_failed",
|
|
3427
3565
|
path: file,
|
|
3428
|
-
message:
|
|
3566
|
+
message: toErrorMessage(err),
|
|
3429
3567
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3430
3568
|
}));
|
|
3431
3569
|
}
|
|
@@ -3576,7 +3714,7 @@ var RecoveryLock = class {
|
|
|
3576
3714
|
sessionStore;
|
|
3577
3715
|
probe;
|
|
3578
3716
|
constructor(opts) {
|
|
3579
|
-
this.file =
|
|
3717
|
+
this.file = path2.join(opts.dir, LOCK_FILE);
|
|
3580
3718
|
this.pid = opts.pid ?? process.pid;
|
|
3581
3719
|
this.hostname = opts.hostname ?? os.hostname();
|
|
3582
3720
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -3637,7 +3775,7 @@ var RecoveryLock = class {
|
|
|
3637
3775
|
* null return before calling this.
|
|
3638
3776
|
*/
|
|
3639
3777
|
async write(sessionId) {
|
|
3640
|
-
await ensureDir(
|
|
3778
|
+
await ensureDir(path2.dirname(this.file));
|
|
3641
3779
|
const lock = {
|
|
3642
3780
|
v: 1,
|
|
3643
3781
|
sessionId,
|
|
@@ -3704,42 +3842,6 @@ function defaultIsPidAlive(pid) {
|
|
|
3704
3842
|
}
|
|
3705
3843
|
}
|
|
3706
3844
|
|
|
3707
|
-
// src/utils/regex-guard.ts
|
|
3708
|
-
var MAX_PATTERN_LEN = 512;
|
|
3709
|
-
var DANGEROUS_PATTERNS = [
|
|
3710
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
3711
|
-
// (a+)+, (.*)+, etc
|
|
3712
|
-
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
3713
|
-
// same, with non-capturing group
|
|
3714
|
-
];
|
|
3715
|
-
function compileUserRegex(pattern, flags) {
|
|
3716
|
-
if (typeof pattern !== "string") {
|
|
3717
|
-
return { ok: false, reason: "pattern must be a string" };
|
|
3718
|
-
}
|
|
3719
|
-
if (pattern.length === 0) {
|
|
3720
|
-
return { ok: false, reason: "pattern is empty" };
|
|
3721
|
-
}
|
|
3722
|
-
if (pattern.length > MAX_PATTERN_LEN) {
|
|
3723
|
-
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
3724
|
-
}
|
|
3725
|
-
for (const rx of DANGEROUS_PATTERNS) {
|
|
3726
|
-
if (rx.test(pattern)) {
|
|
3727
|
-
return {
|
|
3728
|
-
ok: false,
|
|
3729
|
-
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
3730
|
-
};
|
|
3731
|
-
}
|
|
3732
|
-
}
|
|
3733
|
-
try {
|
|
3734
|
-
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
3735
|
-
} catch (err) {
|
|
3736
|
-
return {
|
|
3737
|
-
ok: false,
|
|
3738
|
-
reason: err instanceof Error ? err.message : "invalid regex"
|
|
3739
|
-
};
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
|
|
3743
3845
|
// src/storage/session-reader.ts
|
|
3744
3846
|
var DefaultSessionReader = class {
|
|
3745
3847
|
store;
|
|
@@ -4019,9 +4121,9 @@ function sessionScopedPath(dir, sessionId, suffix) {
|
|
|
4019
4121
|
if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
|
|
4020
4122
|
throw invalid(sessionId);
|
|
4021
4123
|
}
|
|
4022
|
-
const resolved =
|
|
4023
|
-
const rel =
|
|
4024
|
-
if (rel.startsWith("..") ||
|
|
4124
|
+
const resolved = path2.resolve(dir, `${sessionId}${suffix}`);
|
|
4125
|
+
const rel = path2.relative(path2.resolve(dir), resolved);
|
|
4126
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
4025
4127
|
throw invalid(sessionId);
|
|
4026
4128
|
}
|
|
4027
4129
|
return resolved;
|
|
@@ -4083,7 +4185,7 @@ var AnnotationsStore = class {
|
|
|
4083
4185
|
operation: "list",
|
|
4084
4186
|
outcome: "failure",
|
|
4085
4187
|
durationMs: Date.now() - t0,
|
|
4086
|
-
error:
|
|
4188
|
+
error: toErrorMessage(err),
|
|
4087
4189
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
4088
4190
|
});
|
|
4089
4191
|
return [];
|
|
@@ -4188,7 +4290,7 @@ var AnnotationsStore = class {
|
|
|
4188
4290
|
filePath: fp,
|
|
4189
4291
|
operation: "add",
|
|
4190
4292
|
outcome: "failure",
|
|
4191
|
-
error:
|
|
4293
|
+
error: toErrorMessage(err),
|
|
4192
4294
|
recoverable: false,
|
|
4193
4295
|
durationMs: Date.now() - t0,
|
|
4194
4296
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
@@ -4244,7 +4346,7 @@ var AnnotationsStore = class {
|
|
|
4244
4346
|
filePath: fp,
|
|
4245
4347
|
operation: "resolve",
|
|
4246
4348
|
outcome: "failure",
|
|
4247
|
-
error:
|
|
4349
|
+
error: toErrorMessage(err),
|
|
4248
4350
|
recoverable: false,
|
|
4249
4351
|
durationMs: Date.now() - t0,
|
|
4250
4352
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
@@ -4364,22 +4466,40 @@ var ReplayLogStore = class {
|
|
|
4364
4466
|
const t0 = Date.now();
|
|
4365
4467
|
try {
|
|
4366
4468
|
await this.enqueue(input.sessionId, async () => {
|
|
4367
|
-
await withFileLock(
|
|
4368
|
-
const
|
|
4369
|
-
if (
|
|
4469
|
+
await withFileLock(fp, async () => {
|
|
4470
|
+
const cache = await this.ensureCache(input.sessionId);
|
|
4471
|
+
if (cache.has(hash)) return;
|
|
4370
4472
|
const entry = {
|
|
4371
4473
|
hash,
|
|
4372
4474
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4373
4475
|
request: input.request,
|
|
4374
4476
|
response: input.response
|
|
4375
4477
|
};
|
|
4376
|
-
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4478
|
+
const currentCount = this.diskCount.get(input.sessionId) ?? 0;
|
|
4479
|
+
const willEvict = currentCount + 1 > this.maxEntries;
|
|
4480
|
+
if (!willEvict) {
|
|
4481
|
+
await fsp.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
|
|
4482
|
+
cache.set(hash, entry);
|
|
4483
|
+
this.diskCount.set(input.sessionId, currentCount + 1);
|
|
4484
|
+
this.events?.emit("storage.write", {
|
|
4485
|
+
sessionId: input.sessionId,
|
|
4486
|
+
store: "replay",
|
|
4487
|
+
filePath: fp,
|
|
4488
|
+
operation: "record",
|
|
4489
|
+
outcome: "success",
|
|
4490
|
+
durationMs: Date.now() - t0,
|
|
4491
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
4492
|
+
});
|
|
4493
|
+
return;
|
|
4494
|
+
}
|
|
4495
|
+
const all = await this.readAll(input.sessionId);
|
|
4496
|
+
all.push(entry);
|
|
4497
|
+
const keep = all.slice(-this.maxEntries);
|
|
4498
|
+
const refreshed = /* @__PURE__ */ new Map();
|
|
4499
|
+
for (const e of keep) refreshed.set(e.hash, e);
|
|
4500
|
+
this.cache.set(input.sessionId, refreshed);
|
|
4501
|
+
this.diskCount.set(input.sessionId, keep.length);
|
|
4502
|
+
await this.writeAll(input.sessionId, keep, "compact");
|
|
4383
4503
|
});
|
|
4384
4504
|
});
|
|
4385
4505
|
return hash;
|
|
@@ -4482,7 +4602,7 @@ var ReplayLogStore = class {
|
|
|
4482
4602
|
level: "warn",
|
|
4483
4603
|
event: "replay_log_store.list_readdir_failed",
|
|
4484
4604
|
dir,
|
|
4485
|
-
message:
|
|
4605
|
+
message: toErrorMessage(err),
|
|
4486
4606
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4487
4607
|
}));
|
|
4488
4608
|
}
|
|
@@ -4491,7 +4611,7 @@ var ReplayLogStore = class {
|
|
|
4491
4611
|
for (const entry of entries) {
|
|
4492
4612
|
if (entry.name.startsWith(".")) continue;
|
|
4493
4613
|
if (entry.isDirectory()) {
|
|
4494
|
-
if (depth === 0) await scan(
|
|
4614
|
+
if (depth === 0) await scan(path2.join(dir, entry.name), entry.name, depth + 1);
|
|
4495
4615
|
continue;
|
|
4496
4616
|
}
|
|
4497
4617
|
if (!entry.isFile() || !entry.name.endsWith(".replay.jsonl")) continue;
|
|
@@ -4501,7 +4621,7 @@ var ReplayLogStore = class {
|
|
|
4501
4621
|
out.push({
|
|
4502
4622
|
sessionId,
|
|
4503
4623
|
entryCount: all.length,
|
|
4504
|
-
path:
|
|
4624
|
+
path: path2.join(dir, entry.name)
|
|
4505
4625
|
});
|
|
4506
4626
|
}
|
|
4507
4627
|
};
|
|
@@ -4590,15 +4710,15 @@ var SessionRecovery = class {
|
|
|
4590
4710
|
async detectStale(sessionId) {
|
|
4591
4711
|
const fp = this.filePath(sessionId);
|
|
4592
4712
|
const TAIL_SIZE = 8192;
|
|
4593
|
-
let
|
|
4713
|
+
let stat6;
|
|
4594
4714
|
try {
|
|
4595
|
-
|
|
4715
|
+
stat6 = await fsp.stat(fp);
|
|
4596
4716
|
} catch (err) {
|
|
4597
4717
|
if (err.code === "ENOENT") return null;
|
|
4598
4718
|
return null;
|
|
4599
4719
|
}
|
|
4600
|
-
if (
|
|
4601
|
-
const position = Math.max(0,
|
|
4720
|
+
if (stat6.size === 0) return null;
|
|
4721
|
+
const position = Math.max(0, stat6.size - TAIL_SIZE);
|
|
4602
4722
|
const buf = Buffer.alloc(TAIL_SIZE);
|
|
4603
4723
|
let fh;
|
|
4604
4724
|
try {
|
|
@@ -4702,7 +4822,7 @@ var SessionRecovery = class {
|
|
|
4702
4822
|
continue;
|
|
4703
4823
|
if (entry.isDirectory()) {
|
|
4704
4824
|
if (depth === 0) {
|
|
4705
|
-
await collect(
|
|
4825
|
+
await collect(path2.join(dir, entry.name), entry.name, depth + 1);
|
|
4706
4826
|
}
|
|
4707
4827
|
continue;
|
|
4708
4828
|
}
|
|
@@ -4734,6 +4854,13 @@ var ToolAuditLog = class {
|
|
|
4734
4854
|
tailHash = /* @__PURE__ */ new Map();
|
|
4735
4855
|
/** In-memory counter for entry indices — avoids re-reading the file on every write. */
|
|
4736
4856
|
tailIndex = /* @__PURE__ */ new Map();
|
|
4857
|
+
/**
|
|
4858
|
+
* File mtime+size recorded after our last write, per session. Used to
|
|
4859
|
+
* detect cross-process writes (session handoff, recovery) that would
|
|
4860
|
+
* invalidate the in-memory tail cache: if the stat no longer matches
|
|
4861
|
+
* we re-read the file to re-establish the chain tip before appending.
|
|
4862
|
+
*/
|
|
4863
|
+
tailStat = /* @__PURE__ */ new Map();
|
|
4737
4864
|
/** Tracks writes since last fsync, per session. */
|
|
4738
4865
|
unSyncedWrites = /* @__PURE__ */ new Map();
|
|
4739
4866
|
writeChains = /* @__PURE__ */ new Map();
|
|
@@ -4757,10 +4884,9 @@ var ToolAuditLog = class {
|
|
|
4757
4884
|
try {
|
|
4758
4885
|
await this.enqueue(input.sessionId, async () => {
|
|
4759
4886
|
await withFileLock(fp, async () => {
|
|
4760
|
-
const
|
|
4761
|
-
const
|
|
4762
|
-
const
|
|
4763
|
-
const index = prev ? prev.index + 1 : 0;
|
|
4887
|
+
const tip = await this._resolveChainTip(input.sessionId, fp);
|
|
4888
|
+
const prevHash = tip.prevHash;
|
|
4889
|
+
const index = tip.nextIndex;
|
|
4764
4890
|
const id = randomUUID();
|
|
4765
4891
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
4766
4892
|
const content = {
|
|
@@ -4787,10 +4913,15 @@ var ToolAuditLog = class {
|
|
|
4787
4913
|
isError: input.isError,
|
|
4788
4914
|
index
|
|
4789
4915
|
};
|
|
4790
|
-
|
|
4791
|
-
|
|
4916
|
+
await fsp.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
|
|
4917
|
+
try {
|
|
4918
|
+
const st = await fsp.stat(fp);
|
|
4919
|
+
this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });
|
|
4920
|
+
} catch {
|
|
4921
|
+
}
|
|
4792
4922
|
this.tailHash.set(input.sessionId, hash);
|
|
4793
4923
|
this.tailIndex.set(input.sessionId, index + 1);
|
|
4924
|
+
await this._trackUnsynced(input.sessionId, fp);
|
|
4794
4925
|
const durationMs = Date.now() - t0;
|
|
4795
4926
|
this.events?.emit("storage.write", {
|
|
4796
4927
|
sessionId: input.sessionId,
|
|
@@ -4811,7 +4942,7 @@ var ToolAuditLog = class {
|
|
|
4811
4942
|
filePath: fp,
|
|
4812
4943
|
operation: "record",
|
|
4813
4944
|
outcome: "failure",
|
|
4814
|
-
error:
|
|
4945
|
+
error: toErrorMessage(err),
|
|
4815
4946
|
recoverable: false,
|
|
4816
4947
|
durationMs: Date.now() - t0,
|
|
4817
4948
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
@@ -4819,6 +4950,45 @@ var ToolAuditLog = class {
|
|
|
4819
4950
|
throw err;
|
|
4820
4951
|
}
|
|
4821
4952
|
}
|
|
4953
|
+
/**
|
|
4954
|
+
* Resolve the chain tip (previous hash + next index) for an append.
|
|
4955
|
+
* Uses the in-memory `tailHash`/`tailIndex` cache when the file's
|
|
4956
|
+
* stat matches our last known write; falls back to a full read on
|
|
4957
|
+
* cache miss or when an external writer has extended the file.
|
|
4958
|
+
*/
|
|
4959
|
+
async _resolveChainTip(sessionId, fp) {
|
|
4960
|
+
const cachedHash = this.tailHash.get(sessionId);
|
|
4961
|
+
const cachedIndex = this.tailIndex.get(sessionId);
|
|
4962
|
+
const cachedStat = this.tailStat.get(sessionId);
|
|
4963
|
+
if (cachedHash !== void 0 && cachedIndex !== void 0 && cachedStat) {
|
|
4964
|
+
try {
|
|
4965
|
+
const st = await fsp.stat(fp);
|
|
4966
|
+
if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {
|
|
4967
|
+
return { prevHash: cachedHash, nextIndex: cachedIndex };
|
|
4968
|
+
}
|
|
4969
|
+
} catch (err) {
|
|
4970
|
+
if (err.code === "ENOENT") {
|
|
4971
|
+
this.tailHash.delete(sessionId);
|
|
4972
|
+
this.tailIndex.delete(sessionId);
|
|
4973
|
+
this.tailStat.delete(sessionId);
|
|
4974
|
+
return { prevHash: GENESIS_PREV, nextIndex: 0 };
|
|
4975
|
+
}
|
|
4976
|
+
throw err;
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
const entries = await this.readAll(sessionId);
|
|
4980
|
+
const prev = entries.at(-1);
|
|
4981
|
+
const prevHash = prev?.hash ?? GENESIS_PREV;
|
|
4982
|
+
const nextIndex = prev ? prev.index + 1 : 0;
|
|
4983
|
+
this.tailHash.set(sessionId, prevHash);
|
|
4984
|
+
this.tailIndex.set(sessionId, nextIndex);
|
|
4985
|
+
try {
|
|
4986
|
+
const st = await fsp.stat(fp);
|
|
4987
|
+
this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });
|
|
4988
|
+
} catch {
|
|
4989
|
+
}
|
|
4990
|
+
return { prevHash, nextIndex };
|
|
4991
|
+
}
|
|
4822
4992
|
/**
|
|
4823
4993
|
* Walk the chain and verify every entry's hash and prevHash.
|
|
4824
4994
|
* Returns a structured verdict — never throws.
|
|
@@ -4837,7 +5007,7 @@ var ToolAuditLog = class {
|
|
|
4837
5007
|
operation: "verify",
|
|
4838
5008
|
outcome: "failure",
|
|
4839
5009
|
durationMs: Date.now() - t0,
|
|
4840
|
-
error:
|
|
5010
|
+
error: toErrorMessage(err),
|
|
4841
5011
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
4842
5012
|
});
|
|
4843
5013
|
return { ok: true, entries: 0 };
|
|
@@ -4921,7 +5091,7 @@ var ToolAuditLog = class {
|
|
|
4921
5091
|
operation: "load",
|
|
4922
5092
|
outcome: "failure",
|
|
4923
5093
|
durationMs,
|
|
4924
|
-
error:
|
|
5094
|
+
error: toErrorMessage(err),
|
|
4925
5095
|
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
4926
5096
|
});
|
|
4927
5097
|
throw err;
|
|
@@ -4950,10 +5120,12 @@ var ToolAuditLog = class {
|
|
|
4950
5120
|
throw err;
|
|
4951
5121
|
}
|
|
4952
5122
|
}
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
5123
|
+
/**
|
|
5124
|
+
* Tracks writes since last fsync and triggers periodic fsync.
|
|
5125
|
+
* Called after each O(1) append to maintain the same durability
|
|
5126
|
+
* guarantees as the old writeAll approach.
|
|
5127
|
+
*/
|
|
5128
|
+
async _trackUnsynced(sessionId, fp) {
|
|
4957
5129
|
const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;
|
|
4958
5130
|
this.unSyncedWrites.set(sessionId, count);
|
|
4959
5131
|
if (this.fsyncEvery !== Number.POSITIVE_INFINITY && count % this.fsyncEvery === 0) {
|
|
@@ -5122,7 +5294,7 @@ var SessionRegistry = class {
|
|
|
5122
5294
|
heartbeatTimer = null;
|
|
5123
5295
|
currentSessionId = null;
|
|
5124
5296
|
constructor(globalRoot) {
|
|
5125
|
-
this.filePath =
|
|
5297
|
+
this.filePath = path2.join(globalRoot, REGISTRY_FILE);
|
|
5126
5298
|
}
|
|
5127
5299
|
// ── Public API ──────────────────────────────────────────────────────────
|
|
5128
5300
|
/**
|
|
@@ -5289,7 +5461,7 @@ var SessionRegistry = class {
|
|
|
5289
5461
|
const retryDelayMs = 20;
|
|
5290
5462
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
5291
5463
|
try {
|
|
5292
|
-
await fsp.mkdir(
|
|
5464
|
+
await fsp.mkdir(path2.dirname(this.filePath), { recursive: true });
|
|
5293
5465
|
const lockHandle = await fsp.open(lockPath, "wx").catch(() => null);
|
|
5294
5466
|
if (!lockHandle) {
|
|
5295
5467
|
await new Promise((r) => setTimeout(r, retryDelayMs * (attempt + 1)));
|
|
@@ -5502,7 +5674,7 @@ var DefaultSessionRewinder = class {
|
|
|
5502
5674
|
sessionsDir;
|
|
5503
5675
|
projectRoot;
|
|
5504
5676
|
async listCheckpoints(sessionId) {
|
|
5505
|
-
const file =
|
|
5677
|
+
const file = path2.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
5506
5678
|
const raw = await fsp.readFile(file, "utf8");
|
|
5507
5679
|
const events = parseEvents(raw);
|
|
5508
5680
|
const fileCountMap = /* @__PURE__ */ new Map();
|
|
@@ -5527,7 +5699,7 @@ var DefaultSessionRewinder = class {
|
|
|
5527
5699
|
return checkpoints;
|
|
5528
5700
|
}
|
|
5529
5701
|
async rewindToCheckpoint(sessionId, checkpointIndex) {
|
|
5530
|
-
const file =
|
|
5702
|
+
const file = path2.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
5531
5703
|
const raw = await fsp.readFile(file, "utf8");
|
|
5532
5704
|
const events = parseEvents(raw);
|
|
5533
5705
|
let targetIdx = -1;
|
|
@@ -5566,7 +5738,7 @@ var DefaultSessionRewinder = class {
|
|
|
5566
5738
|
return { ...result, toPromptIndex: checkpointIndex, removedEvents };
|
|
5567
5739
|
}
|
|
5568
5740
|
async rewindLastN(sessionId, n) {
|
|
5569
|
-
const file =
|
|
5741
|
+
const file = path2.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
5570
5742
|
const raw = await fsp.readFile(file, "utf8");
|
|
5571
5743
|
const events = parseEvents(raw);
|
|
5572
5744
|
const checkpoints = [];
|
|
@@ -5595,7 +5767,7 @@ var DefaultSessionRewinder = class {
|
|
|
5595
5767
|
return { ...result, toPromptIndex: targetIndex, removedEvents: snapshotsToRevert.length };
|
|
5596
5768
|
}
|
|
5597
5769
|
async rewindToStart(sessionId) {
|
|
5598
|
-
const file =
|
|
5770
|
+
const file = path2.join(this.sessionsDir, `${sessionId}.jsonl`);
|
|
5599
5771
|
const raw = await fsp.readFile(file, "utf8");
|
|
5600
5772
|
const events = parseEvents(raw);
|
|
5601
5773
|
const allSnapshots = [];
|
|
@@ -5631,10 +5803,10 @@ async function revertSnapshots(snapshots, projectRoot) {
|
|
|
5631
5803
|
for (const snapshot of snapshots) {
|
|
5632
5804
|
for (const file of snapshot.files) {
|
|
5633
5805
|
try {
|
|
5634
|
-
const absPath =
|
|
5635
|
-
const root =
|
|
5636
|
-
const rel =
|
|
5637
|
-
if (rel.startsWith("..") ||
|
|
5806
|
+
const absPath = path2.resolve(file.path);
|
|
5807
|
+
const root = path2.resolve(projectRoot);
|
|
5808
|
+
const rel = path2.relative(root, absPath);
|
|
5809
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
5638
5810
|
errors.push(`${file.path}: path resolves outside project root \u2014 skipping`);
|
|
5639
5811
|
continue;
|
|
5640
5812
|
}
|
|
@@ -5653,7 +5825,7 @@ async function revertSnapshots(snapshots, projectRoot) {
|
|
|
5653
5825
|
}
|
|
5654
5826
|
}
|
|
5655
5827
|
} catch (err) {
|
|
5656
|
-
errors.push(`${file.path}: ${
|
|
5828
|
+
errors.push(`${file.path}: ${toErrorMessage(err)}`);
|
|
5657
5829
|
}
|
|
5658
5830
|
}
|
|
5659
5831
|
}
|
|
@@ -5671,7 +5843,7 @@ async function loadTodosCheckpoint(filePath, events, traceId) {
|
|
|
5671
5843
|
filePath,
|
|
5672
5844
|
operation: "load",
|
|
5673
5845
|
outcome: "failure",
|
|
5674
|
-
error:
|
|
5846
|
+
error: toErrorMessage(err),
|
|
5675
5847
|
recoverable: true
|
|
5676
5848
|
});
|
|
5677
5849
|
return null;
|
|
@@ -5743,13 +5915,13 @@ async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)
|
|
|
5743
5915
|
filePath,
|
|
5744
5916
|
operation: "save",
|
|
5745
5917
|
outcome: "failure",
|
|
5746
|
-
error:
|
|
5918
|
+
error: toErrorMessage(err),
|
|
5747
5919
|
recoverable: false
|
|
5748
5920
|
});
|
|
5749
5921
|
console.warn(JSON.stringify({
|
|
5750
5922
|
level: "warn",
|
|
5751
5923
|
event: "todos_checkpoint.save_failed",
|
|
5752
|
-
message:
|
|
5924
|
+
message: toErrorMessage(err),
|
|
5753
5925
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
5754
5926
|
}));
|
|
5755
5927
|
}
|
|
@@ -5760,7 +5932,7 @@ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
|
|
|
5760
5932
|
let writeChain = Promise.resolve();
|
|
5761
5933
|
const enqueueWrite = (todos) => {
|
|
5762
5934
|
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
|
|
5763
|
-
const msg =
|
|
5935
|
+
const msg = toErrorMessage(err);
|
|
5764
5936
|
console.error(JSON.stringify({
|
|
5765
5937
|
level: "error",
|
|
5766
5938
|
event: "todos_checkpoint.write_chain_failed",
|
|
@@ -5809,7 +5981,7 @@ async function loadPlan(filePath, events) {
|
|
|
5809
5981
|
store: "plan",
|
|
5810
5982
|
filePath,
|
|
5811
5983
|
operation: "load",
|
|
5812
|
-
error:
|
|
5984
|
+
error: toErrorMessage(err),
|
|
5813
5985
|
recoverable: true
|
|
5814
5986
|
});
|
|
5815
5987
|
return null;
|
|
@@ -5868,12 +6040,12 @@ async function savePlan(filePath, plan, events) {
|
|
|
5868
6040
|
store: "plan",
|
|
5869
6041
|
filePath,
|
|
5870
6042
|
operation: "save",
|
|
5871
|
-
error:
|
|
6043
|
+
error: toErrorMessage(err),
|
|
5872
6044
|
recoverable: false
|
|
5873
6045
|
});
|
|
5874
6046
|
console.warn(
|
|
5875
6047
|
"[plan-store] save failed:",
|
|
5876
|
-
|
|
6048
|
+
toErrorMessage(err)
|
|
5877
6049
|
);
|
|
5878
6050
|
}
|
|
5879
6051
|
}
|
|
@@ -6116,7 +6288,7 @@ async function loadTasks(filePath, events, traceId) {
|
|
|
6116
6288
|
filePath,
|
|
6117
6289
|
operation: "load",
|
|
6118
6290
|
outcome: "failure",
|
|
6119
|
-
error:
|
|
6291
|
+
error: toErrorMessage(err),
|
|
6120
6292
|
recoverable: true
|
|
6121
6293
|
});
|
|
6122
6294
|
return null;
|
|
@@ -6181,13 +6353,13 @@ async function saveTasks(filePath, tasks, events, traceId) {
|
|
|
6181
6353
|
filePath,
|
|
6182
6354
|
operation: "save",
|
|
6183
6355
|
outcome: "failure",
|
|
6184
|
-
error:
|
|
6356
|
+
error: toErrorMessage(err),
|
|
6185
6357
|
recoverable: false,
|
|
6186
6358
|
...traceId !== void 0 && { traceId }
|
|
6187
6359
|
});
|
|
6188
6360
|
console.warn(
|
|
6189
6361
|
"[task-store] save failed:",
|
|
6190
|
-
|
|
6362
|
+
toErrorMessage(err)
|
|
6191
6363
|
);
|
|
6192
6364
|
}
|
|
6193
6365
|
}
|
|
@@ -6366,7 +6538,7 @@ var DirectorStateCheckpoint = class {
|
|
|
6366
6538
|
} catch (err) {
|
|
6367
6539
|
console.warn(
|
|
6368
6540
|
"[director-state] checkpoint write failed:",
|
|
6369
|
-
|
|
6541
|
+
toErrorMessage(err)
|
|
6370
6542
|
);
|
|
6371
6543
|
} finally {
|
|
6372
6544
|
this.writing = false;
|
|
@@ -6377,102 +6549,6 @@ var DirectorStateCheckpoint = class {
|
|
|
6377
6549
|
}
|
|
6378
6550
|
}
|
|
6379
6551
|
};
|
|
6380
|
-
|
|
6381
|
-
// src/utils/term.ts
|
|
6382
|
-
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
6383
|
-
function isStdoutTTY() {
|
|
6384
|
-
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
6385
|
-
}
|
|
6386
|
-
|
|
6387
|
-
// src/utils/color.ts
|
|
6388
|
-
var isColorTty = () => {
|
|
6389
|
-
if (envFlag(process.env.NO_COLOR)) return false;
|
|
6390
|
-
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
6391
|
-
return isStdoutTTY();
|
|
6392
|
-
};
|
|
6393
|
-
function envFlag(value) {
|
|
6394
|
-
if (value === void 0) return false;
|
|
6395
|
-
if (value.trim() === "") return false;
|
|
6396
|
-
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
6397
|
-
}
|
|
6398
|
-
var COLOR = isColorTty();
|
|
6399
|
-
var wrap = (open6, close) => (s) => COLOR ? `\x1B[${open6}m${s}\x1B[${close}m` : s;
|
|
6400
|
-
var color = {
|
|
6401
|
-
reset: wrap("0", "0"),
|
|
6402
|
-
bold: wrap("1", "22"),
|
|
6403
|
-
dim: wrap("2", "22"),
|
|
6404
|
-
italic: wrap("3", "23"),
|
|
6405
|
-
underline: wrap("4", "24"),
|
|
6406
|
-
red: wrap("31", "39"),
|
|
6407
|
-
green: wrap("32", "39"),
|
|
6408
|
-
yellow: wrap("33", "39"),
|
|
6409
|
-
blue: wrap("34", "39"),
|
|
6410
|
-
magenta: wrap("35", "39"),
|
|
6411
|
-
cyan: wrap("36", "39"),
|
|
6412
|
-
gray: wrap("90", "39"),
|
|
6413
|
-
amber: wrap("38;5;214", "39"),
|
|
6414
|
-
pink: wrap("38;5;205", "39"),
|
|
6415
|
-
bgRed: wrap("41", "49"),
|
|
6416
|
-
bgGreen: wrap("42", "49")
|
|
6417
|
-
};
|
|
6418
|
-
function projectHash(absRoot) {
|
|
6419
|
-
return createHash("sha256").update(path13.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
6420
|
-
}
|
|
6421
|
-
function projectSlug(absRoot) {
|
|
6422
|
-
const base = slugify(path13.basename(absRoot));
|
|
6423
|
-
const hash = createHash("sha256").update(path13.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
6424
|
-
return `${base}-${hash}`;
|
|
6425
|
-
}
|
|
6426
|
-
function slugify(name) {
|
|
6427
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
6428
|
-
}
|
|
6429
|
-
function wstackGlobalRoot() {
|
|
6430
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
6431
|
-
if (fromEnv && fromEnv.trim().length > 0) return path13.resolve(fromEnv);
|
|
6432
|
-
return path13.join(os.homedir(), ".wrongstack");
|
|
6433
|
-
}
|
|
6434
|
-
function resolveWstackPaths(opts) {
|
|
6435
|
-
const globalRoot = opts.globalRoot ?? (opts.userHome ? path13.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
6436
|
-
const hash = projectHash(opts.projectRoot);
|
|
6437
|
-
const slug = projectSlug(opts.projectRoot);
|
|
6438
|
-
const projectDir = path13.join(globalRoot, "projects", slug);
|
|
6439
|
-
return {
|
|
6440
|
-
globalRoot,
|
|
6441
|
-
configDir: globalRoot,
|
|
6442
|
-
globalConfig: path13.join(globalRoot, "config.json"),
|
|
6443
|
-
secretsKey: path13.join(globalRoot, ".key"),
|
|
6444
|
-
globalMemory: path13.join(globalRoot, "memory.md"),
|
|
6445
|
-
globalSkills: path13.join(globalRoot, "skills"),
|
|
6446
|
-
globalPrompts: path13.join(globalRoot, "prompts"),
|
|
6447
|
-
cacheDir: path13.join(globalRoot, "cache"),
|
|
6448
|
-
modelsCache: path13.join(globalRoot, "cache", "models.dev.json"),
|
|
6449
|
-
modelsOverlayCache: path13.join(globalRoot, "cache", "models-overlay.json"),
|
|
6450
|
-
historyFile: path13.join(globalRoot, "history"),
|
|
6451
|
-
logFile: path13.join(globalRoot, "logs", "wrongstack.log"),
|
|
6452
|
-
projectDir,
|
|
6453
|
-
projectCodebaseIndex: path13.join(projectDir, "codebase-index"),
|
|
6454
|
-
projectMemory: path13.join(projectDir, "memory.md"),
|
|
6455
|
-
projectSessions: path13.join(projectDir, "sessions"),
|
|
6456
|
-
projectTrust: path13.join(projectDir, "trust.json"),
|
|
6457
|
-
projectMeta: path13.join(projectDir, "meta.json"),
|
|
6458
|
-
projectLocalConfig: path13.join(projectDir, "config.local.json"),
|
|
6459
|
-
inProjectConfig: path13.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
6460
|
-
inProjectAgentsFile: path13.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
6461
|
-
inProjectSkills: path13.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
6462
|
-
inProjectWorktrees: path13.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
6463
|
-
projectHash: hash,
|
|
6464
|
-
projectSlug: slug,
|
|
6465
|
-
projectGoal: path13.join(projectDir, "goal.json"),
|
|
6466
|
-
projectSpecs: path13.join(projectDir, "specs"),
|
|
6467
|
-
projectTaskGraphs: path13.join(projectDir, "task-graphs"),
|
|
6468
|
-
projectSddSession: path13.join(projectDir, "sdd-session.json"),
|
|
6469
|
-
projectPlan: path13.join(projectDir, "plan.json"),
|
|
6470
|
-
projectAutophase: path13.join(projectDir, "autophase"),
|
|
6471
|
-
syncConfig: path13.join(globalRoot, "sync.json")
|
|
6472
|
-
};
|
|
6473
|
-
}
|
|
6474
|
-
|
|
6475
|
-
// src/storage/goal-store.ts
|
|
6476
6552
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
6477
6553
|
function goalFilePath(projectRoot) {
|
|
6478
6554
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
@@ -6500,7 +6576,7 @@ async function loadGoal(filePath, events) {
|
|
|
6500
6576
|
store: "goal",
|
|
6501
6577
|
filePath,
|
|
6502
6578
|
operation: "load",
|
|
6503
|
-
error:
|
|
6579
|
+
error: toErrorMessage(err),
|
|
6504
6580
|
recoverable: false
|
|
6505
6581
|
});
|
|
6506
6582
|
throw err;
|
|
@@ -6573,11 +6649,11 @@ async function saveGoal(filePath, goal, events) {
|
|
|
6573
6649
|
store: "goal",
|
|
6574
6650
|
filePath,
|
|
6575
6651
|
operation: "save",
|
|
6576
|
-
error:
|
|
6652
|
+
error: toErrorMessage(err),
|
|
6577
6653
|
recoverable: false
|
|
6578
6654
|
});
|
|
6579
6655
|
throw new FsError({
|
|
6580
|
-
message:
|
|
6656
|
+
message: toErrorMessage(err),
|
|
6581
6657
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
6582
6658
|
path: filePath,
|
|
6583
6659
|
cause: err
|
|
@@ -6739,7 +6815,7 @@ var DefaultPromptStore = class {
|
|
|
6739
6815
|
if (!file.endsWith(".json")) continue;
|
|
6740
6816
|
try {
|
|
6741
6817
|
const raw = JSON.parse(
|
|
6742
|
-
await fsp.readFile(
|
|
6818
|
+
await fsp.readFile(path2.join(this.dir, file), "utf8")
|
|
6743
6819
|
);
|
|
6744
6820
|
entries.push(raw.entry);
|
|
6745
6821
|
} catch {
|
|
@@ -6752,7 +6828,7 @@ var DefaultPromptStore = class {
|
|
|
6752
6828
|
);
|
|
6753
6829
|
}
|
|
6754
6830
|
async get(id) {
|
|
6755
|
-
const file =
|
|
6831
|
+
const file = path2.join(this.dir, `${id}.json`);
|
|
6756
6832
|
try {
|
|
6757
6833
|
const raw = JSON.parse(await fsp.readFile(file, "utf8"));
|
|
6758
6834
|
return raw.entry;
|
|
@@ -6762,12 +6838,12 @@ var DefaultPromptStore = class {
|
|
|
6762
6838
|
}
|
|
6763
6839
|
async save(entry) {
|
|
6764
6840
|
await ensureDir(this.dir);
|
|
6765
|
-
const file =
|
|
6841
|
+
const file = path2.join(this.dir, `${entry.id}.json`);
|
|
6766
6842
|
const raw = { version: 1, entry };
|
|
6767
6843
|
await atomicWrite(file, JSON.stringify(raw, null, 2));
|
|
6768
6844
|
}
|
|
6769
6845
|
async delete(id) {
|
|
6770
|
-
const file =
|
|
6846
|
+
const file = path2.join(this.dir, `${id}.json`);
|
|
6771
6847
|
try {
|
|
6772
6848
|
await fsp.unlink(file);
|
|
6773
6849
|
return true;
|
|
@@ -6801,7 +6877,7 @@ var CloudSync = class {
|
|
|
6801
6877
|
this.paths = paths;
|
|
6802
6878
|
this.getConfig = getConfig;
|
|
6803
6879
|
this.setConfig = setConfig;
|
|
6804
|
-
this.statePath =
|
|
6880
|
+
this.statePath = path2.join(paths.globalRoot, "sync-state.json");
|
|
6805
6881
|
}
|
|
6806
6882
|
paths;
|
|
6807
6883
|
getConfig;
|
|
@@ -6908,7 +6984,7 @@ var CloudSync = class {
|
|
|
6908
6984
|
const rel = segments.slice(2).join("/");
|
|
6909
6985
|
const destPath = resolvePulledCategoryPath(cat, localPath, rel, entry.path);
|
|
6910
6986
|
const blobData = await this.getBlob(token, owner, repoName, entry.sha);
|
|
6911
|
-
await fsp.mkdir(
|
|
6987
|
+
await fsp.mkdir(path2.dirname(destPath), { recursive: true });
|
|
6912
6988
|
await fsp.writeFile(destPath, Buffer.from(blobData, "base64"));
|
|
6913
6989
|
}
|
|
6914
6990
|
const localRev = await this.hashLocalCategories(cfg.categories);
|
|
@@ -7015,12 +7091,12 @@ var CloudSync = class {
|
|
|
7015
7091
|
const localPath = this.categoryToPath(cat);
|
|
7016
7092
|
if (!localPath) continue;
|
|
7017
7093
|
try {
|
|
7018
|
-
const
|
|
7019
|
-
if (
|
|
7094
|
+
const stat6 = await fsp.stat(localPath);
|
|
7095
|
+
if (stat6.isDirectory()) {
|
|
7020
7096
|
const files = await this.walkDir(localPath, localPath);
|
|
7021
7097
|
for (const file of files) {
|
|
7022
7098
|
const content = await fsp.readFile(file, "utf8");
|
|
7023
|
-
const rel =
|
|
7099
|
+
const rel = path2.relative(localPath, file).replace(/\\/g, "/");
|
|
7024
7100
|
entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
|
|
7025
7101
|
hashes.push(content);
|
|
7026
7102
|
}
|
|
@@ -7041,8 +7117,8 @@ var CloudSync = class {
|
|
|
7041
7117
|
const localPath = this.categoryToPath(cat);
|
|
7042
7118
|
if (!localPath) continue;
|
|
7043
7119
|
try {
|
|
7044
|
-
const
|
|
7045
|
-
if (
|
|
7120
|
+
const stat6 = await fsp.stat(localPath);
|
|
7121
|
+
if (stat6.isDirectory()) {
|
|
7046
7122
|
const files = await this.walkDir(localPath, localPath);
|
|
7047
7123
|
for (const file of files) {
|
|
7048
7124
|
const content = await fsp.readFile(file);
|
|
@@ -7077,7 +7153,7 @@ var CloudSync = class {
|
|
|
7077
7153
|
const results = [];
|
|
7078
7154
|
const entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
7079
7155
|
for (const entry of entries) {
|
|
7080
|
-
const full =
|
|
7156
|
+
const full = path2.join(dir, entry.name);
|
|
7081
7157
|
if (entry.isDirectory()) {
|
|
7082
7158
|
results.push(...await this.walkDir(full, base));
|
|
7083
7159
|
} else {
|
|
@@ -7099,9 +7175,9 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
|
|
|
7099
7175
|
return localPath;
|
|
7100
7176
|
}
|
|
7101
7177
|
if (!rel) return localPath;
|
|
7102
|
-
const normalizedRel =
|
|
7103
|
-
const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${
|
|
7104
|
-
if (
|
|
7178
|
+
const normalizedRel = path2.normalize(rel);
|
|
7179
|
+
const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${path2.sep}`);
|
|
7180
|
+
if (path2.isAbsolute(normalizedRel) || traversesUp) {
|
|
7105
7181
|
throw new FsError({
|
|
7106
7182
|
message: `Refusing CloudSync path traversal: ${remotePath}`,
|
|
7107
7183
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -7109,10 +7185,10 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
|
|
|
7109
7185
|
context: { reason: "path_traversal", normalizedRel }
|
|
7110
7186
|
});
|
|
7111
7187
|
}
|
|
7112
|
-
const dest =
|
|
7113
|
-
const root =
|
|
7114
|
-
const relative4 =
|
|
7115
|
-
if (relative4.startsWith("..") ||
|
|
7188
|
+
const dest = path2.resolve(localPath, normalizedRel);
|
|
7189
|
+
const root = path2.resolve(localPath);
|
|
7190
|
+
const relative4 = path2.relative(root, dest);
|
|
7191
|
+
if (relative4.startsWith("..") || path2.isAbsolute(relative4)) {
|
|
7116
7192
|
throw new FsError({
|
|
7117
7193
|
message: `Refusing CloudSync path outside category root: ${remotePath}`,
|
|
7118
7194
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -7172,7 +7248,7 @@ function isAllowed(type, level) {
|
|
|
7172
7248
|
return true;
|
|
7173
7249
|
}
|
|
7174
7250
|
function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
7175
|
-
|
|
7251
|
+
let currentLevel = level ?? "standard";
|
|
7176
7252
|
const resolveWriter = typeof writer === "function" ? writer : () => writer;
|
|
7177
7253
|
const progressCounters = /* @__PURE__ */ new Map();
|
|
7178
7254
|
const toolProgressConfig = options.sampling?.toolProgress ?? {};
|
|
@@ -7193,14 +7269,19 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
7193
7269
|
return true;
|
|
7194
7270
|
}
|
|
7195
7271
|
return {
|
|
7196
|
-
level
|
|
7272
|
+
get level() {
|
|
7273
|
+
return currentLevel;
|
|
7274
|
+
},
|
|
7275
|
+
setAuditLevel(next) {
|
|
7276
|
+
currentLevel = next ?? "standard";
|
|
7277
|
+
},
|
|
7197
7278
|
allows(type) {
|
|
7198
|
-
return isAllowed(type,
|
|
7279
|
+
return isAllowed(type, currentLevel);
|
|
7199
7280
|
},
|
|
7200
7281
|
async append(event) {
|
|
7201
7282
|
const target = resolveWriter();
|
|
7202
7283
|
if (!target) return;
|
|
7203
|
-
if (!isAllowed(event.type,
|
|
7284
|
+
if (!isAllowed(event.type, currentLevel)) return;
|
|
7204
7285
|
if (!shouldSample(event)) return;
|
|
7205
7286
|
try {
|
|
7206
7287
|
await target.append(event);
|
|
@@ -7211,7 +7292,7 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
7211
7292
|
const target = resolveWriter();
|
|
7212
7293
|
if (!target || events.length === 0) return;
|
|
7213
7294
|
const allowed = events.filter(
|
|
7214
|
-
(e) => isAllowed(e.type,
|
|
7295
|
+
(e) => isAllowed(e.type, currentLevel) && shouldSample(e)
|
|
7215
7296
|
);
|
|
7216
7297
|
if (allowed.length === 0) return;
|
|
7217
7298
|
try {
|