@wrongstack/core 0.107.2 → 0.119.1

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.
Files changed (63) hide show
  1. package/dist/{agent-bridge-mOxbpFcg.d.ts → agent-bridge-PRAbEX-9.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DukQLUcS.d.ts → agent-subagent-runner-DXWImnNV.d.ts} +7 -8
  3. package/dist/{brain-Dfv4Y82E.d.ts → brain-NZZqL8sn.d.ts} +1 -1
  4. package/dist/{compactor-DXLxLcmU.d.ts → compactor-C3stu664.d.ts} +1 -1
  5. package/dist/{config-BSU-6vah.d.ts → config-DZV9VK9n.d.ts} +104 -3
  6. package/dist/{context-CNRYfhUv.d.ts → context-hvXINa6a.d.ts} +31 -1
  7. package/dist/coordination/index.d.ts +12 -13
  8. package/dist/coordination/index.js +198 -0
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +27 -28
  11. package/dist/defaults/index.js +346 -86
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +16 -17
  14. package/dist/execution/index.js +266 -12
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +6 -7
  17. package/dist/{goal-preamble-CI8lxeY1.d.ts → goal-preamble-COpt55W8.d.ts} +10 -10
  18. package/dist/{goal-store-ht0VmR1A.d.ts → goal-store-CV9Yz2X_.d.ts} +11 -0
  19. package/dist/{index-DIKEcfgC.d.ts → index-4VVHuTZF.d.ts} +7 -6
  20. package/dist/{index-BWRN6wOb.d.ts → index-Cj3le7TI.d.ts} +9 -10
  21. package/dist/index.d.ts +41 -42
  22. package/dist/index.js +372 -118
  23. package/dist/index.js.map +1 -1
  24. package/dist/infrastructure/index.d.ts +6 -7
  25. package/dist/kernel/index.d.ts +10 -11
  26. package/dist/llm-selector-Bmx4PsVM.d.ts +58 -0
  27. package/dist/{mcp-servers-CXCsANdY.d.ts → mcp-servers-CofNHOqn.d.ts} +3 -3
  28. package/dist/{mode-ARA3HrkY.d.ts → mode-CZlO9iU1.d.ts} +1 -1
  29. package/dist/models/index.d.ts +157 -53
  30. package/dist/models/index.js +423 -1
  31. package/dist/models/index.js.map +1 -1
  32. package/dist/{models-registry-DU64QxQa.d.ts → models-registry-BzlsjKcy.d.ts} +1 -1
  33. package/dist/{multi-agent-coordinator-51LvnXkD.d.ts → multi-agent-coordinator-CmDmUgq2.d.ts} +1 -1
  34. package/dist/{null-fleet-bus-D09hMzFQ.d.ts → null-fleet-bus-CyH2YqP4.d.ts} +6 -6
  35. package/dist/observability/index.d.ts +2 -2
  36. package/dist/{parallel-eternal-engine-CUtmM_0V.d.ts → parallel-eternal-engine-uxVm4y4V.d.ts} +11 -10
  37. package/dist/{path-resolver-DDJiMAtX.d.ts → path-resolver-gfoGxpdd.d.ts} +3 -3
  38. package/dist/{permission-BDv7z0mk.d.ts → permission-DhLrgA30.d.ts} +1 -1
  39. package/dist/{permission-policy-dF74EpDp.d.ts → permission-policy-DGOvne7i.d.ts} +2 -2
  40. package/dist/{pipeline-BqiA_UMr.d.ts → pipeline-CqF7uju2.d.ts} +2 -2
  41. package/dist/{plan-templates-BdDxl9cI.d.ts → plan-templates-DoKhzenv.d.ts} +6 -6
  42. package/dist/{provider-runner-BUunikwY.d.ts → provider-runner-BPlI5sPZ.d.ts} +3 -3
  43. package/dist/{retry-policy-BcmuT_V0.d.ts → retry-policy-B4Q-FZgE.d.ts} +1 -1
  44. package/dist/sdd/index.d.ts +8 -9
  45. package/dist/sdd/index.js +198 -0
  46. package/dist/sdd/index.js.map +1 -1
  47. package/dist/{secret-vault-DrOhc2i5.d.ts → secret-vault-B7GnL6o9.d.ts} +1 -1
  48. package/dist/security/index.d.ts +4 -4
  49. package/dist/{selector-C7wcdqMA.d.ts → selector-BakeMnWR.d.ts} +1 -1
  50. package/dist/{session-event-bridge-BpJ5trO9.d.ts → session-event-bridge-DE5Oekwx.d.ts} +1 -1
  51. package/dist/{session-reader-DDz1Ek4V.d.ts → session-reader-DFC81-4p.d.ts} +1 -1
  52. package/dist/storage/index.d.ts +12 -13
  53. package/dist/storage/index.js +147 -88
  54. package/dist/storage/index.js.map +1 -1
  55. package/dist/types/index.d.ts +20 -21
  56. package/dist/types/index.js +35 -1
  57. package/dist/types/index.js.map +1 -1
  58. package/dist/utils/index.d.ts +3 -4
  59. package/dist/utils/index.js +2 -1
  60. package/dist/utils/index.js.map +1 -1
  61. package/dist/{wstack-paths-_lqjzErq.d.ts → wstack-paths-DD50Omgn.d.ts} +3 -0
  62. package/package.json +1 -1
  63. package/dist/models-registry-B6_KfS65.d.ts +0 -95
@@ -1,7 +1,7 @@
1
1
  import * as crypto2 from 'crypto';
2
2
  import { randomBytes, randomUUID, createCipheriv, createDecipheriv, createHash } from 'crypto';
3
3
  import * as fsp from 'fs/promises';
4
- import * as path3 from 'path';
4
+ import * as path11 from 'path';
5
5
  import { isAbsolute, resolve } from 'path';
6
6
  import * as fs5 from 'fs';
7
7
  import * as os from 'os';
@@ -28,9 +28,9 @@ __export(atomic_write_exports, {
28
28
  withFileLock: () => withFileLock
29
29
  });
30
30
  async function atomicWrite(targetPath, content, opts = {}) {
31
- const dir = path3.dirname(targetPath);
31
+ const dir = path11.dirname(targetPath);
32
32
  await fsp.mkdir(dir, { recursive: true });
33
- const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
33
+ const tmp = path11.join(dir, `.${path11.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
34
34
  try {
35
35
  if (typeof content === "string") {
36
36
  await fsp.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -69,9 +69,9 @@ async function ensureDir(dir) {
69
69
  await fsp.mkdir(dir, { recursive: true });
70
70
  }
71
71
  async function withFileLock(targetPath, fn, opts = {}) {
72
- const dir = path3.dirname(targetPath);
72
+ const dir = path11.dirname(targetPath);
73
73
  await fsp.mkdir(dir, { recursive: true });
74
- const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
74
+ const lockPath = path11.join(dir, `.${path11.basename(targetPath)}.lock`);
75
75
  const timeoutMs = opts.timeoutMs ?? 5e3;
76
76
  const staleMs = opts.staleMs ?? 3e4;
77
77
  const started = Date.now();
@@ -218,7 +218,7 @@ var DefaultLogger = class _DefaultLogger {
218
218
  this.stderr = opts.stderr !== false;
219
219
  if (this.file) {
220
220
  try {
221
- fs5.mkdirSync(path3.dirname(this.file), { recursive: true });
221
+ fs5.mkdirSync(path11.dirname(this.file), { recursive: true });
222
222
  } catch {
223
223
  }
224
224
  }
@@ -414,11 +414,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
414
414
  }
415
415
  /** Absolute path to the session index file. */
416
416
  get indexFile() {
417
- return path3.join(this.dir, "_index.jsonl");
417
+ return path11.join(this.dir, "_index.jsonl");
418
418
  }
419
419
  /** Join session ID to its absolute path within the store directory. */
420
420
  sessionPath(id, ext) {
421
- return path3.join(this.dir, `${id}${ext}`);
421
+ return path11.join(this.dir, `${id}${ext}`);
422
422
  }
423
423
  /**
424
424
  * Ensure the directory implied by the session ID exists. When the ID
@@ -426,7 +426,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
426
426
  * subdirectory so sessions group naturally by day.
427
427
  */
428
428
  async ensureShardDir(id) {
429
- const dirPath = path3.dirname(path3.join(this.dir, id));
429
+ const dirPath = path11.dirname(path11.join(this.dir, id));
430
430
  await ensureDir(dirPath);
431
431
  return dirPath;
432
432
  }
@@ -434,7 +434,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
434
434
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
435
435
  const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
436
436
  const shardDir = await this.ensureShardDir(id);
437
- const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
437
+ const file = path11.join(shardDir, `${path11.basename(id)}.jsonl`);
438
438
  let handle;
439
439
  try {
440
440
  handle = await fsp.open(file, "a", 384);
@@ -642,7 +642,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
642
642
  continue;
643
643
  if (entry.isDirectory()) {
644
644
  const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
645
- ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
645
+ ids.push(...await this.collectSessionIds(path11.join(dir, entry.name), childPrefix, depth + 1));
646
646
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
647
647
  if (entry.name === "_index.jsonl") continue;
648
648
  const base = entry.name.replace(/\.jsonl$/, "");
@@ -676,12 +676,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
676
676
  async deleteSession(id) {
677
677
  await fsp.unlink(this.sessionPath(id, ".jsonl")).catch(() => void 0);
678
678
  await fsp.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
679
- const shardDir = path3.dirname(path3.join(this.dir, id));
680
- const base = path3.basename(id);
679
+ const shardDir = path11.dirname(path11.join(this.dir, id));
680
+ const base = path11.basename(id);
681
681
  for (const ext of [".plan.json", ".todos.json"]) {
682
- await fsp.unlink(path3.join(shardDir, `${base}${ext}`)).catch(() => void 0);
682
+ await fsp.unlink(path11.join(shardDir, `${base}${ext}`)).catch(() => void 0);
683
683
  }
684
- const sessDir = path3.join(shardDir, base);
684
+ const sessDir = path11.join(shardDir, base);
685
685
  await fsp.rm(sessDir, { recursive: true, force: true }).catch(() => void 0);
686
686
  await this.writeTombstone(id);
687
687
  }
@@ -693,7 +693,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
693
693
  let deleted = 0;
694
694
  let activeSessionId = null;
695
695
  try {
696
- const raw = await fsp.readFile(path3.join(this.dir, "active.json"), "utf8");
696
+ const raw = await fsp.readFile(path11.join(this.dir, "active.json"), "utf8");
697
697
  const active = JSON.parse(raw);
698
698
  activeSessionId = active.sessionId ?? null;
699
699
  } catch {
@@ -701,11 +701,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
701
701
  const entries = await fsp.readdir(this.dir, { withFileTypes: true }).catch(() => []);
702
702
  for (const entry of entries) {
703
703
  if (!entry.isDirectory()) continue;
704
- const dateDir = path3.join(this.dir, entry.name);
704
+ const dateDir = path11.join(this.dir, entry.name);
705
705
  const files = await fsp.readdir(dateDir, { withFileTypes: true }).catch(() => []);
706
706
  for (const file of files) {
707
707
  if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
708
- const jsonlPath = path3.join(dateDir, file.name);
708
+ const jsonlPath = path11.join(dateDir, file.name);
709
709
  try {
710
710
  const stat6 = await fsp.stat(jsonlPath);
711
711
  if (stat6.mtimeMs >= cutoff) continue;
@@ -723,7 +723,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
723
723
  }
724
724
  for (const entry of entries) {
725
725
  if (!entry.isDirectory()) continue;
726
- const dateDir = path3.join(this.dir, entry.name);
726
+ const dateDir = path11.join(this.dir, entry.name);
727
727
  try {
728
728
  const remaining = await fsp.readdir(dateDir);
729
729
  if (remaining.length === 0) {
@@ -888,7 +888,7 @@ var FileSessionWriter = class {
888
888
  this.meta = meta;
889
889
  this.events = events;
890
890
  this.resumed = opts.resumed ?? false;
891
- this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
891
+ this.manifestFile = opts.dir ? path11.join(opts.dir, `${path11.basename(id)}.summary.json`) : "";
892
892
  this.filePath = opts.filePath ?? "";
893
893
  this.secretScrubber = opts.secretScrubber;
894
894
  this.onCloseCb = opts.onClose;
@@ -1211,7 +1211,7 @@ init_atomic_write();
1211
1211
  var QueueStore = class {
1212
1212
  file;
1213
1213
  constructor(opts) {
1214
- this.file = path3.join(opts.dir, "queue.json");
1214
+ this.file = path11.join(opts.dir, "queue.json");
1215
1215
  }
1216
1216
  async write(items) {
1217
1217
  if (items.length === 0) {
@@ -1281,7 +1281,7 @@ var DefaultAttachmentStore = class {
1281
1281
  let data = input.data;
1282
1282
  if (this.spoolDir && bytes >= this.spoolThreshold) {
1283
1283
  await fsp.mkdir(this.spoolDir, { recursive: true });
1284
- spooledPath = path3.join(this.spoolDir, `${id}.bin`);
1284
+ spooledPath = path11.join(this.spoolDir, `${id}.bin`);
1285
1285
  await atomicWrite(spooledPath, input.data, {
1286
1286
  encoding: input.kind === "image" ? "base64" : "utf8"
1287
1287
  });
@@ -1493,7 +1493,7 @@ var FileMemoryBackend = class {
1493
1493
  }
1494
1494
  async remember(scope, entry, filePath) {
1495
1495
  const file = this.resolveFile(filePath, scope);
1496
- await ensureDir(path3.dirname(file));
1496
+ await ensureDir(path11.dirname(file));
1497
1497
  let existing = "";
1498
1498
  try {
1499
1499
  existing = await fsp.readFile(file, "utf8");
@@ -1510,39 +1510,41 @@ ${line}`;
1510
1510
  }
1511
1511
  async forget(scope, query, filePath) {
1512
1512
  const file = this.resolveFile(filePath, scope);
1513
- let existing;
1514
- try {
1515
- existing = await fsp.readFile(file, "utf8");
1516
- } catch {
1517
- return 0;
1518
- }
1519
- const needle = query.toLowerCase();
1520
- const idMatcher = /mem_\d+_\w+/;
1521
- let removed = 0;
1522
- const lines = existing.split("\n").filter((line) => {
1523
- const trimmed = line.trim();
1524
- if (!trimmed.startsWith("- ")) return true;
1525
- if (idMatcher.test(query)) {
1526
- const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
1527
- if (entryIdMatch && entryIdMatch[0] === query) {
1513
+ return withFileLock(file, async () => {
1514
+ let existing;
1515
+ try {
1516
+ existing = await fsp.readFile(file, "utf8");
1517
+ } catch {
1518
+ return 0;
1519
+ }
1520
+ const needle = query.toLowerCase();
1521
+ const idMatcher = /mem_\d+_\w+/;
1522
+ let removed = 0;
1523
+ const lines = existing.split("\n").filter((line) => {
1524
+ const trimmed = line.trim();
1525
+ if (!trimmed.startsWith("- ")) return true;
1526
+ if (idMatcher.test(query)) {
1527
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
1528
+ if (entryIdMatch && entryIdMatch[0] === query) {
1529
+ removed++;
1530
+ return false;
1531
+ }
1532
+ }
1533
+ if (trimmed.toLowerCase().includes(needle)) {
1528
1534
  removed++;
1529
1535
  return false;
1530
1536
  }
1537
+ return true;
1538
+ });
1539
+ if (removed > 0) {
1540
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
1541
+ await atomicWrite(file, "");
1542
+ } else {
1543
+ await atomicWrite(file, lines.join("\n"));
1544
+ }
1531
1545
  }
1532
- if (trimmed.toLowerCase().includes(needle)) {
1533
- removed++;
1534
- return false;
1535
- }
1536
- return true;
1546
+ return removed;
1537
1547
  });
1538
- if (removed > 0) {
1539
- if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
1540
- await atomicWrite(file, "");
1541
- } else {
1542
- await atomicWrite(file, lines.join("\n"));
1543
- }
1544
- }
1545
- return removed;
1546
1548
  }
1547
1549
  async readAll(scope, filePath) {
1548
1550
  const file = this.resolveFile(filePath, scope);
@@ -2255,12 +2257,14 @@ var DefaultConfigLoader = class {
2255
2257
  }
2256
2258
  async load(opts = {}) {
2257
2259
  let cfg = { ...BEHAVIOR_DEFAULTS };
2258
- const [global, local] = await Promise.all([
2260
+ const [global, local, inProject] = await Promise.all([
2259
2261
  this.readJson(this.paths.globalConfig),
2260
- this.readJson(this.paths.projectLocalConfig)
2262
+ this.readJson(this.paths.projectLocalConfig),
2263
+ this.readJson(this.paths.inProjectConfig)
2261
2264
  ]);
2262
2265
  cfg = deepMerge(cfg, global);
2263
2266
  cfg = deepMerge(cfg, local);
2267
+ cfg = deepMerge(cfg, inProject);
2264
2268
  for (const [key, fn] of Object.entries(ENV_MAP)) {
2265
2269
  const v = process.env[key];
2266
2270
  if (v) fn(cfg, v);
@@ -2462,7 +2466,7 @@ var RecoveryLock = class {
2462
2466
  sessionStore;
2463
2467
  probe;
2464
2468
  constructor(opts) {
2465
- this.file = path3.join(opts.dir, LOCK_FILE);
2469
+ this.file = path11.join(opts.dir, LOCK_FILE);
2466
2470
  this.pid = opts.pid ?? process.pid;
2467
2471
  this.hostname = opts.hostname ?? os.hostname();
2468
2472
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -2520,7 +2524,7 @@ var RecoveryLock = class {
2520
2524
  * null return before calling this.
2521
2525
  */
2522
2526
  async write(sessionId) {
2523
- await ensureDir(path3.dirname(this.file));
2527
+ await ensureDir(path11.dirname(this.file));
2524
2528
  const lock = {
2525
2529
  v: 1,
2526
2530
  sessionId,
@@ -3753,7 +3757,7 @@ var DefaultSecretVault = class {
3753
3757
  } catch (err) {
3754
3758
  if (err.code !== "ENOENT") throw err;
3755
3759
  }
3756
- fs5.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
3760
+ fs5.mkdirSync(path11.dirname(this.keyFile), { recursive: true });
3757
3761
  const key = randomBytes(KEY_BYTES);
3758
3762
  try {
3759
3763
  fs5.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
@@ -3822,7 +3826,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
3822
3826
  }
3823
3827
  const merged = deepMerge2(current, patch ?? {});
3824
3828
  const encrypted = encryptConfigSecrets(merged, vault);
3825
- await fsp.mkdir(path3.dirname(configPath), { recursive: true });
3829
+ await fsp.mkdir(path11.dirname(configPath), { recursive: true });
3826
3830
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3827
3831
  await restrictFilePermissions(configPath);
3828
3832
  }
@@ -4063,9 +4067,9 @@ function getInputString(input, key) {
4063
4067
  function pathLooksInsideProject(rawPath, projectRoot) {
4064
4068
  if (!projectRoot) return false;
4065
4069
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
4066
- const resolved = path3.resolve(projectRoot, rawPath);
4067
- const relative2 = path3.relative(projectRoot, resolved);
4068
- return !!relative2 && !relative2.startsWith("..") && !path3.isAbsolute(relative2);
4070
+ const resolved = path11.resolve(projectRoot, rawPath);
4071
+ const relative2 = path11.relative(projectRoot, resolved);
4072
+ return !!relative2 && !relative2.startsWith("..") && !path11.isAbsolute(relative2);
4069
4073
  }
4070
4074
  function tokenizeShell(command) {
4071
4075
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -4075,7 +4079,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
4075
4079
  if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
4076
4080
  if (token.includes("*")) return true;
4077
4081
  if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
4078
- if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
4082
+ if (path11.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
4079
4083
  return false;
4080
4084
  }
4081
4085
  function hasDangerousDeleteTarget(tokens, start, projectRoot) {
@@ -4612,6 +4616,7 @@ var DefaultRetryPolicy = class {
4612
4616
  if (err instanceof ProviderError) {
4613
4617
  if (err.status === 429) return 5;
4614
4618
  if (err.status === 529) return 3;
4619
+ if (err.status === 599) return 5;
4615
4620
  if (err.status >= 500) return 3;
4616
4621
  return 0;
4617
4622
  }
@@ -4731,7 +4736,7 @@ var DefaultSkillLoader = class {
4731
4736
  const entries = await fsp.readdir(dir, { withFileTypes: true });
4732
4737
  for (const e of entries) {
4733
4738
  if (!e.isDirectory()) continue;
4734
- const skillFile = path3.join(dir, e.name, "SKILL.md");
4739
+ const skillFile = path11.join(dir, e.name, "SKILL.md");
4735
4740
  try {
4736
4741
  const raw = await fsp.readFile(skillFile, "utf8");
4737
4742
  const meta = parseFrontmatter(raw);
@@ -7037,25 +7042,82 @@ var AutonomousRunner = class {
7037
7042
 
7038
7043
  // src/storage/goal-store.ts
7039
7044
  init_atomic_write();
7045
+ function projectHash(absRoot) {
7046
+ return createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 12);
7047
+ }
7048
+ function projectSlug(absRoot) {
7049
+ const base = slugify(path11.basename(absRoot));
7050
+ const hash = createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 6);
7051
+ return `${base}-${hash}`;
7052
+ }
7053
+ function slugify(name) {
7054
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
7055
+ }
7056
+ function resolveWstackPaths(opts) {
7057
+ const home = opts.userHome ?? os.homedir();
7058
+ const globalRoot = opts.globalRoot ?? path11.join(home, ".wrongstack");
7059
+ const hash = projectHash(opts.projectRoot);
7060
+ const slug = projectSlug(opts.projectRoot);
7061
+ const projectDir = path11.join(globalRoot, "projects", slug);
7062
+ return {
7063
+ globalRoot,
7064
+ configDir: globalRoot,
7065
+ globalConfig: path11.join(globalRoot, "config.json"),
7066
+ secretsKey: path11.join(globalRoot, ".key"),
7067
+ globalMemory: path11.join(globalRoot, "memory.md"),
7068
+ globalSkills: path11.join(globalRoot, "skills"),
7069
+ globalPrompts: path11.join(globalRoot, "prompts"),
7070
+ cacheDir: path11.join(globalRoot, "cache"),
7071
+ modelsCache: path11.join(globalRoot, "cache", "models.dev.json"),
7072
+ modelsOverlayCache: path11.join(globalRoot, "cache", "models-overlay.json"),
7073
+ historyFile: path11.join(globalRoot, "history"),
7074
+ logFile: path11.join(globalRoot, "logs", "wrongstack.log"),
7075
+ projectDir,
7076
+ projectCodebaseIndex: path11.join(projectDir, "codebase-index"),
7077
+ projectMemory: path11.join(projectDir, "memory.md"),
7078
+ projectSessions: path11.join(projectDir, "sessions"),
7079
+ projectTrust: path11.join(projectDir, "trust.json"),
7080
+ projectMeta: path11.join(projectDir, "meta.json"),
7081
+ projectLocalConfig: path11.join(projectDir, "config.local.json"),
7082
+ inProjectConfig: path11.join(opts.projectRoot, ".wrongstack", "config.json"),
7083
+ inProjectAgentsFile: path11.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
7084
+ inProjectSkills: path11.join(opts.projectRoot, ".wrongstack", "skills"),
7085
+ inProjectWorktrees: path11.join(opts.projectRoot, ".wrongstack", "worktrees"),
7086
+ projectHash: hash,
7087
+ projectSlug: slug,
7088
+ projectGoal: path11.join(projectDir, "goal.json"),
7089
+ projectSpecs: path11.join(projectDir, "specs"),
7090
+ projectTaskGraphs: path11.join(projectDir, "task-graphs"),
7091
+ projectSddSession: path11.join(projectDir, "sdd-session.json"),
7092
+ projectPlan: path11.join(projectDir, "plan.json"),
7093
+ projectAutophase: path11.join(projectDir, "autophase"),
7094
+ syncConfig: path11.join(globalRoot, "sync.json")
7095
+ };
7096
+ }
7097
+
7098
+ // src/storage/goal-store.ts
7040
7099
  var MAX_JOURNAL_ENTRIES = 500;
7041
7100
  function goalFilePath(projectRoot) {
7042
- const hash = createHash("sha256").update(path3.resolve(projectRoot)).digest("hex").slice(0, 12);
7043
- return path3.join(os.homedir(), ".wrongstack", "projects", hash, "goal.json");
7101
+ return resolveWstackPaths({ projectRoot }).projectGoal;
7044
7102
  }
7045
7103
  async function loadGoal(filePath) {
7046
7104
  let raw;
7047
7105
  try {
7048
7106
  raw = await fsp.readFile(filePath, "utf8");
7049
- } catch {
7050
- return null;
7107
+ } catch (err) {
7108
+ const code = err.code;
7109
+ if (code === "ENOENT") return null;
7110
+ throw err;
7051
7111
  }
7052
7112
  try {
7053
7113
  const parsed = JSON.parse(raw);
7054
7114
  if (parsed?.version !== 1 || typeof parsed.goal !== "string" || !Array.isArray(parsed.journal)) {
7115
+ console.warn(`[goal-store] Corrupt goal.json at ${filePath} \u2014 invalid schema. Consider deleting it and re-creating.`);
7055
7116
  return null;
7056
7117
  }
7057
7118
  return parsed;
7058
7119
  } catch {
7120
+ console.warn(`[goal-store] Corrupt goal.json at ${filePath} \u2014 JSON parse failed. Consider deleting it and re-creating.`);
7059
7121
  return null;
7060
7122
  }
7061
7123
  }
@@ -7109,7 +7171,7 @@ function computeTrend(history) {
7109
7171
  const recent = history.slice(-5);
7110
7172
  const deltas = [];
7111
7173
  for (let i = 1; i < recent.length; i++) {
7112
- deltas.push(recent[i].progress - recent[i - 1].progress);
7174
+ deltas.push((recent[i]?.progress ?? 0) - (recent[i - 1]?.progress ?? 0));
7113
7175
  }
7114
7176
  if (deltas.length < 2) return void 0;
7115
7177
  const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
@@ -7701,8 +7763,8 @@ ${recentJournal}` : "No prior iterations.",
7701
7763
  await saveGoal(this.goalPath, abandoned);
7702
7764
  }
7703
7765
  try {
7704
- const { unlink: unlink9 } = await import('fs/promises');
7705
- await unlink9(this.goalPath);
7766
+ const { unlink: unlink10 } = await import('fs/promises');
7767
+ await unlink10(this.goalPath);
7706
7768
  } catch {
7707
7769
  }
7708
7770
  this.opts.onEternalStop?.();
@@ -8703,6 +8765,53 @@ Working rules:
8703
8765
  "flaws"
8704
8766
  ]
8705
8767
  }
8768
+ },
8769
+ {
8770
+ config: {
8771
+ id: "refactor-planner",
8772
+ name: "Refactor Planner",
8773
+ role: "refactor-planner",
8774
+ tools: [...PLAN_TOOLS, "diff"],
8775
+ prompt: `You are the Refactor Planner agent. Your job is to analyze code
8776
+ structure and produce a concrete, phased refactoring plan with risk
8777
+ assessment, dependency ordering, and rollback strategy.
8778
+
8779
+ Scope:
8780
+ - Map module-level dependencies (import graph)
8781
+ - Identify coupling hotspots (high fan-in/out modules)
8782
+ - Assess refactoring risk by complexity and test coverage
8783
+ - Generate phased plans with checkpoint milestones
8784
+ - Produce diff-friendly task lists (one task = one concern)
8785
+
8786
+ Input format you accept:
8787
+ { "task": "plan | assess | roadmap", "target": "src/core", "constraint": "no-breaking-changes | minimal-downtime | full-rewrite", "focus": "architecture | performance | maintainability" }
8788
+
8789
+ Output: Markdown refactor plan with phases (Low Risk / Medium Risk / High Risk),
8790
+ dependency graph, rollback strategy, and exit criteria.
8791
+
8792
+ Working rules:
8793
+ - Always include rollback strategy \u2014 every refactor can fail
8794
+ - Merge tasks that take <1h into a single phase
8795
+ - Respect team constraints (reviewer availability, parallelization)
8796
+ - Never plan without analyzing the actual code first`
8797
+ },
8798
+ budget: HEAVY_BUDGET,
8799
+ capability: {
8800
+ phase: "planning",
8801
+ summary: "Refactoring planner: analyzes code structure, maps dependencies, produces risk-scored phased plans with rollback strategy.",
8802
+ keywords: [
8803
+ "refactor",
8804
+ "refactoring",
8805
+ "restructure",
8806
+ "debt",
8807
+ "technical debt",
8808
+ "clean up",
8809
+ "modularize",
8810
+ "decouple",
8811
+ "dependency graph",
8812
+ "code structure"
8813
+ ]
8814
+ }
8706
8815
  }
8707
8816
  ];
8708
8817
 
@@ -9242,6 +9351,157 @@ Working rules:
9242
9351
  "robustness"
9243
9352
  ]
9244
9353
  }
9354
+ },
9355
+ {
9356
+ config: {
9357
+ id: "security-scanner",
9358
+ name: "Security Scanner",
9359
+ role: "security-scanner",
9360
+ tools: [...TOOLS.inspect],
9361
+ prompt: `You are the Security Scanner agent. Your job is to scan code,
9362
+ configs, and dependencies for security issues from hardcoded secrets to
9363
+ supply chain risks.
9364
+
9365
+ Scope:
9366
+ - Detect hardcoded secrets: API keys, tokens, passwords, private keys
9367
+ - Find injection vectors: eval, innerHTML, SQL concat, shell injection
9368
+ - Identify insecure patterns: weak crypto, hardcoded IVs, disabled TLS
9369
+ - Scan dependencies for known CVEs (via npm/pnpm audit)
9370
+ - Flag supply chain risks: postinstall hooks, unverified scripts
9371
+
9372
+ Input format you accept:
9373
+ { "task": "scan | audit | secrets | dependencies", "paths": ["src", "config"], "depth": "quick | normal | deep" }
9374
+
9375
+ Output: Markdown security report with severity-ranked findings, injection
9376
+ vectors, dependency issues, and a remediation checklist.
9377
+
9378
+ Working rules:
9379
+ - Never scan node_modules \u2014 use npm audit instead
9380
+ - Always provide remediation steps, not just findings
9381
+ - Verify regex-based secrets before flagging (false positive risk)
9382
+ - When in doubt, flag as medium rather than ignoring potential issues`
9383
+ },
9384
+ budget: HEAVY_BUDGET,
9385
+ capability: {
9386
+ phase: "verify",
9387
+ summary: "Security scanner: detects hardcoded secrets, injection vectors, insecure patterns, and supply-chain risks with remediation.",
9388
+ keywords: [
9389
+ "security",
9390
+ "scan",
9391
+ "vulnerability",
9392
+ "secret",
9393
+ "api key",
9394
+ "hardcoded",
9395
+ "injection",
9396
+ "cve",
9397
+ "audit dependencies",
9398
+ "supply chain",
9399
+ "xss",
9400
+ "sqli",
9401
+ "shell injection",
9402
+ "sensitive data",
9403
+ "credential"
9404
+ ]
9405
+ }
9406
+ },
9407
+ {
9408
+ config: {
9409
+ id: "bug-hunter",
9410
+ name: "Bug Hunter",
9411
+ role: "bug-hunter",
9412
+ tools: [...TOOLS.inspect],
9413
+ prompt: `You are the Bug Hunter agent. Your job is to systematically scan
9414
+ source code for bugs, anti-patterns, and code smells using pattern matching
9415
+ and heuristics. Output a prioritized hit list with file:line references.
9416
+
9417
+ Scope:
9418
+ - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
9419
+ - Identify anti-patterns (callback hell, God objects, circular deps)
9420
+ - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
9421
+ - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
9422
+ - Rank findings: critical > high > medium > low
9423
+
9424
+ Input format you accept:
9425
+ { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
9426
+
9427
+ Output: Markdown bug hunt report with critically/high/medium/low sections.
9428
+ Each entry: **[TYPE]** \`file:line\` \u2014 description + suggested fix
9429
+
9430
+ Working rules:
9431
+ - Never scan node_modules \u2014 it's noise
9432
+ - Always include file:line for every finding
9433
+ - If >30% of findings are false positives, note the confidence level
9434
+ - Ask director for clarification if paths are ambiguous`
9435
+ },
9436
+ budget: HEAVY_BUDGET,
9437
+ capability: {
9438
+ phase: "verify",
9439
+ summary: "Bug hunter: scans source code for bugs, anti-patterns, and code smells, producing a file:line-ranked hit list with fixes.",
9440
+ keywords: [
9441
+ "bug",
9442
+ "hunt",
9443
+ "scan",
9444
+ "code smell",
9445
+ "anti-pattern",
9446
+ "race condition",
9447
+ "memory leak",
9448
+ "null deref",
9449
+ "type safety",
9450
+ "unhandled error",
9451
+ "find bugs",
9452
+ "audit code",
9453
+ "code quality"
9454
+ ]
9455
+ }
9456
+ },
9457
+ {
9458
+ config: {
9459
+ id: "audit-log",
9460
+ name: "Audit Log",
9461
+ role: "audit-log",
9462
+ tools: [...TOOLS.inspect],
9463
+ prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
9464
+ session logs and produce actionable markdown reports.
9465
+
9466
+ Scope:
9467
+ - Parse session logs (iteration counts, tool calls, errors, usage)
9468
+ - Detect repeated failure patterns across multiple runs
9469
+ - Identify tool usage anomalies (over-use, failures, unexpected chains)
9470
+ - Track token consumption trends
9471
+ - Generate structured audit reports with severity ratings
9472
+
9473
+ Input format you accept:
9474
+ { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
9475
+
9476
+ Output: Markdown audit report with Summary, Top Errors, Tool Usage table,
9477
+ Anomalies, and Cost Trend sections.
9478
+
9479
+ Working rules:
9480
+ - Never fabricate numbers \u2014 read the actual logs first
9481
+ - Always include file:line references for errors
9482
+ - If sessionPath is missing, ask the director to provide it
9483
+ - Report confidence level: high (>90% accuracy), medium, low`
9484
+ },
9485
+ budget: MEDIUM_BUDGET,
9486
+ capability: {
9487
+ phase: "verify",
9488
+ summary: "Audit log analyzer: parses session JSONL, detects failure patterns, tool anomalies, and cost trends with structured reports.",
9489
+ keywords: [
9490
+ "audit",
9491
+ "log",
9492
+ "logs",
9493
+ "session",
9494
+ "trace",
9495
+ "analyze logs",
9496
+ "error patterns",
9497
+ "cost analysis",
9498
+ "tool usage",
9499
+ "token usage",
9500
+ "post-mortem",
9501
+ "trend",
9502
+ "anomaly"
9503
+ ]
9504
+ }
9245
9505
  }
9246
9506
  ];
9247
9507
 
@@ -14900,7 +15160,7 @@ var Director = class _Director {
14900
15160
  })),
14901
15161
  usage: this.usage.snapshot()
14902
15162
  };
14903
- await fsp.mkdir(path3.dirname(this.manifestPath), { recursive: true });
15163
+ await fsp.mkdir(path11.dirname(this.manifestPath), { recursive: true });
14904
15164
  await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
14905
15165
  return this.manifestPath;
14906
15166
  }
@@ -15106,7 +15366,7 @@ var Director = class _Director {
15106
15366
  */
15107
15367
  async readSession(subagentId, tail) {
15108
15368
  if (!this.sessionsRoot) return null;
15109
- const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
15369
+ const filePath = path11.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
15110
15370
  let raw;
15111
15371
  try {
15112
15372
  raw = await fsp.readFile(filePath, "utf8");
@@ -15630,13 +15890,13 @@ async function readSubagentPartial(opts, subagentId) {
15630
15890
  if (!opts.sessionsRoot) return void 0;
15631
15891
  const candidates = [];
15632
15892
  if (opts.directorRunId) {
15633
- candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
15893
+ candidates.push(path11.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
15634
15894
  } else {
15635
15895
  try {
15636
15896
  const entries = await fsp.readdir(opts.sessionsRoot, { withFileTypes: true });
15637
15897
  for (const entry of entries) {
15638
15898
  if (entry.isDirectory()) {
15639
- candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
15899
+ candidates.push(path11.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
15640
15900
  }
15641
15901
  }
15642
15902
  } catch {
@@ -15685,9 +15945,9 @@ function makeDirectorSessionFactory(opts) {
15685
15945
  let dir;
15686
15946
  if (opts.store) {
15687
15947
  store = opts.store;
15688
- dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
15948
+ dir = opts.sessionsRoot ? path11.join(opts.sessionsRoot, runId) : "(caller-managed)";
15689
15949
  } else if (opts.sessionsRoot) {
15690
- dir = path3.join(opts.sessionsRoot, runId);
15950
+ dir = path11.join(opts.sessionsRoot, runId);
15691
15951
  store = new DefaultSessionStore({ dir });
15692
15952
  } else {
15693
15953
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -15894,7 +16154,7 @@ var DefaultModelsRegistry = class {
15894
16154
  this.overlay = opts.overlay;
15895
16155
  this.overlayUrl = opts.overlayUrl;
15896
16156
  this.overlayFile = opts.overlayFile;
15897
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
16157
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path11.join(path11.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
15898
16158
  }
15899
16159
  async load(opts = {}) {
15900
16160
  if (this.payload && !opts.force) return this.payload;
@@ -16107,7 +16367,7 @@ var DefaultModelsRegistry = class {
16107
16367
  }
16108
16368
  /** Used by `wstack models refresh` to expose where the cache lives. */
16109
16369
  cacheLocation() {
16110
- return path3.resolve(this.cacheFile);
16370
+ return path11.resolve(this.cacheFile);
16111
16371
  }
16112
16372
  };
16113
16373
  function hasEntries(payload) {
@@ -16403,7 +16663,7 @@ var DefaultModeStore = class {
16403
16663
  }
16404
16664
  async loadActiveMode() {
16405
16665
  try {
16406
- const configPath = path3.join(this.configDir, "mode.json");
16666
+ const configPath = path11.join(this.configDir, "mode.json");
16407
16667
  const content = await fsp.readFile(configPath, "utf8");
16408
16668
  const data = JSON.parse(content);
16409
16669
  this.activeModeId = data.activeMode ?? null;
@@ -16414,7 +16674,7 @@ var DefaultModeStore = class {
16414
16674
  async saveActiveMode() {
16415
16675
  try {
16416
16676
  await fsp.mkdir(this.configDir, { recursive: true });
16417
- const configPath = path3.join(this.configDir, "mode.json");
16677
+ const configPath = path11.join(this.configDir, "mode.json");
16418
16678
  await atomicWrite(
16419
16679
  configPath,
16420
16680
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -16429,11 +16689,11 @@ async function loadProjectModes(modesDir) {
16429
16689
  const entries = await fsp.readdir(modesDir);
16430
16690
  for (const entry of entries) {
16431
16691
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
16432
- const filePath = path3.join(modesDir, entry);
16692
+ const filePath = path11.join(modesDir, entry);
16433
16693
  const stat6 = await fsp.stat(filePath);
16434
16694
  if (!stat6.isFile()) continue;
16435
16695
  const content = await fsp.readFile(filePath, "utf8");
16436
- const id = path3.basename(entry, path3.extname(entry));
16696
+ const id = path11.basename(entry, path11.extname(entry));
16437
16697
  modes.push({
16438
16698
  id,
16439
16699
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -16449,7 +16709,7 @@ async function loadProjectModes(modesDir) {
16449
16709
  async function loadUserModes(modesDir) {
16450
16710
  const modes = [];
16451
16711
  try {
16452
- const manifestPath = path3.join(modesDir, "modes.json");
16712
+ const manifestPath = path11.join(modesDir, "modes.json");
16453
16713
  const content = await fsp.readFile(manifestPath, "utf8");
16454
16714
  const manifest = JSON.parse(content);
16455
16715
  for (const mode of manifest.modes) {
@@ -17366,7 +17626,7 @@ var SpecStore = class {
17366
17626
  indexPath;
17367
17627
  constructor(opts) {
17368
17628
  this.baseDir = opts.baseDir;
17369
- this.indexPath = path3.join(this.baseDir, "_index.json");
17629
+ this.indexPath = path11.join(this.baseDir, "_index.json");
17370
17630
  }
17371
17631
  async save(spec) {
17372
17632
  await ensureDir(this.baseDir);
@@ -17435,7 +17695,7 @@ var SpecStore = class {
17435
17695
  return updated;
17436
17696
  }
17437
17697
  filePath(id) {
17438
- return path3.join(this.baseDir, `${id}.json`);
17698
+ return path11.join(this.baseDir, `${id}.json`);
17439
17699
  }
17440
17700
  async readIndex() {
17441
17701
  try {
@@ -17492,7 +17752,7 @@ var TaskGraphStore = class {
17492
17752
  indexPath;
17493
17753
  constructor(opts) {
17494
17754
  this.baseDir = opts.baseDir;
17495
- this.indexPath = path3.join(this.baseDir, "_index.json");
17755
+ this.indexPath = path11.join(this.baseDir, "_index.json");
17496
17756
  }
17497
17757
  async save(graph) {
17498
17758
  await ensureDir(this.baseDir);
@@ -17530,7 +17790,7 @@ var TaskGraphStore = class {
17530
17790
  }
17531
17791
  }
17532
17792
  filePath(id) {
17533
- return path3.join(this.baseDir, `${id}.json`);
17793
+ return path11.join(this.baseDir, `${id}.json`);
17534
17794
  }
17535
17795
  async readIndex() {
17536
17796
  try {