@wrongstack/core 0.291.1 → 0.292.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.
Files changed (127) hide show
  1. package/dist/coordination/director-tools.d.ts.map +1 -1
  2. package/dist/coordination/index.js +113 -113
  3. package/dist/coordination/index.js.map +3 -3
  4. package/dist/core/agent-loop.d.ts.map +1 -1
  5. package/dist/core/agent-response.d.ts.map +1 -1
  6. package/dist/core/context.d.ts +10 -0
  7. package/dist/core/context.d.ts.map +1 -1
  8. package/dist/core/system-prompt-builder.d.ts +20 -1
  9. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  10. package/dist/defaults/index.d.ts +1 -0
  11. package/dist/defaults/index.d.ts.map +1 -1
  12. package/dist/defaults/index.js +3132 -2440
  13. package/dist/defaults/index.js.map +4 -4
  14. package/dist/execution/auto-compaction-middleware.d.ts +34 -2
  15. package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
  16. package/dist/execution/compaction-core.d.ts +59 -0
  17. package/dist/execution/compaction-core.d.ts.map +1 -1
  18. package/dist/execution/compactor.d.ts.map +1 -1
  19. package/dist/execution/error-handler.d.ts +7 -0
  20. package/dist/execution/error-handler.d.ts.map +1 -1
  21. package/dist/execution/index.d.ts +1 -0
  22. package/dist/execution/index.d.ts.map +1 -1
  23. package/dist/execution/index.js +4677 -3924
  24. package/dist/execution/index.js.map +4 -4
  25. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  26. package/dist/execution/model-runtime.d.ts.map +1 -1
  27. package/dist/execution/selective-compactor.d.ts +10 -0
  28. package/dist/execution/selective-compactor.d.ts.map +1 -1
  29. package/dist/execution/subagent-compaction.d.ts +24 -0
  30. package/dist/execution/subagent-compaction.d.ts.map +1 -0
  31. package/dist/hq/alerts.d.ts +5 -0
  32. package/dist/hq/alerts.d.ts.map +1 -1
  33. package/dist/hq/auth-audit.d.ts +115 -0
  34. package/dist/hq/auth-audit.d.ts.map +1 -0
  35. package/dist/hq/auth-store.d.ts +98 -2
  36. package/dist/hq/auth-store.d.ts.map +1 -1
  37. package/dist/hq/brain-bridge.d.ts +2 -8
  38. package/dist/hq/brain-bridge.d.ts.map +1 -1
  39. package/dist/hq/bridge-context.d.ts +74 -0
  40. package/dist/hq/bridge-context.d.ts.map +1 -0
  41. package/dist/hq/cost-bridge.d.ts +4 -10
  42. package/dist/hq/cost-bridge.d.ts.map +1 -1
  43. package/dist/hq/fleet-bridge.d.ts +2 -8
  44. package/dist/hq/fleet-bridge.d.ts.map +1 -1
  45. package/dist/hq/index.d.ts +2 -0
  46. package/dist/hq/index.d.ts.map +1 -1
  47. package/dist/hq/index.js +358 -222
  48. package/dist/hq/index.js.map +4 -4
  49. package/dist/hq/protocol/session.d.ts +13 -0
  50. package/dist/hq/protocol/session.d.ts.map +1 -1
  51. package/dist/hq/tool-bridge.d.ts +2 -8
  52. package/dist/hq/tool-bridge.d.ts.map +1 -1
  53. package/dist/hq/worktree-bridge.d.ts +2 -8
  54. package/dist/hq/worktree-bridge.d.ts.map +1 -1
  55. package/dist/index.d.ts +4 -2
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +6054 -4741
  58. package/dist/index.js.map +4 -4
  59. package/dist/infrastructure/index.js +32 -15
  60. package/dist/infrastructure/index.js.map +2 -2
  61. package/dist/infrastructure/provider-cache-ledger.d.ts +48 -0
  62. package/dist/infrastructure/provider-cache-ledger.d.ts.map +1 -0
  63. package/dist/infrastructure/token-counter.d.ts +4 -2
  64. package/dist/infrastructure/token-counter.d.ts.map +1 -1
  65. package/dist/kernel/events/brain-events.d.ts +2 -0
  66. package/dist/kernel/events/brain-events.d.ts.map +1 -1
  67. package/dist/kernel/events/memory-events.d.ts +102 -0
  68. package/dist/kernel/events/memory-events.d.ts.map +1 -1
  69. package/dist/kernel/events/session-events.d.ts +35 -0
  70. package/dist/kernel/events/session-events.d.ts.map +1 -1
  71. package/dist/models/index.js +161 -78
  72. package/dist/models/index.js.map +3 -3
  73. package/dist/models/llm-selector.d.ts.map +1 -1
  74. package/dist/notifications/index.d.ts +40 -0
  75. package/dist/notifications/index.d.ts.map +1 -0
  76. package/dist/notifications/notifier-impl.d.ts +34 -0
  77. package/dist/notifications/notifier-impl.d.ts.map +1 -0
  78. package/dist/notifications/notifier.d.ts +95 -0
  79. package/dist/notifications/notifier.d.ts.map +1 -0
  80. package/dist/notifications/types.d.ts +111 -0
  81. package/dist/notifications/types.d.ts.map +1 -0
  82. package/dist/plugin/api.d.ts +10 -1
  83. package/dist/plugin/api.d.ts.map +1 -1
  84. package/dist/registry/provider-registry.d.ts +1 -1
  85. package/dist/registry/provider-registry.d.ts.map +1 -1
  86. package/dist/security/index.js +15 -15
  87. package/dist/security/index.js.map +3 -3
  88. package/dist/skills/index.js +80 -80
  89. package/dist/skills/index.js.map +3 -3
  90. package/dist/storage/config-loader.d.ts +3 -5
  91. package/dist/storage/config-loader.d.ts.map +1 -1
  92. package/dist/storage/index.js +325 -259
  93. package/dist/storage/index.js.map +4 -4
  94. package/dist/storage/memory-consolidator.d.ts +14 -2
  95. package/dist/storage/memory-consolidator.d.ts.map +1 -1
  96. package/dist/storage/queue-store.d.ts +2 -0
  97. package/dist/storage/queue-store.d.ts.map +1 -1
  98. package/dist/tools/index.js +6 -6
  99. package/dist/tools/index.js.map +3 -3
  100. package/dist/tools/mcp-control.d.ts +1 -1
  101. package/dist/types/config.d.ts +64 -20
  102. package/dist/types/config.d.ts.map +1 -1
  103. package/dist/types/index.js +60 -34
  104. package/dist/types/index.js.map +3 -3
  105. package/dist/types/plugin.d.ts +11 -0
  106. package/dist/types/plugin.d.ts.map +1 -1
  107. package/dist/types/provider.d.ts +31 -0
  108. package/dist/types/provider.d.ts.map +1 -1
  109. package/dist/types/token-counter.d.ts +8 -1
  110. package/dist/types/token-counter.d.ts.map +1 -1
  111. package/dist/utils/cache-key.d.ts +19 -0
  112. package/dist/utils/cache-key.d.ts.map +1 -0
  113. package/dist/utils/compaction-preview.d.ts +8 -0
  114. package/dist/utils/compaction-preview.d.ts.map +1 -0
  115. package/dist/utils/context-breakdown.d.ts +59 -0
  116. package/dist/utils/context-breakdown.d.ts.map +1 -0
  117. package/dist/utils/context-evidence.d.ts +22 -0
  118. package/dist/utils/context-evidence.d.ts.map +1 -1
  119. package/dist/utils/index.d.ts +2 -0
  120. package/dist/utils/index.d.ts.map +1 -1
  121. package/dist/utils/index.js +1730 -1543
  122. package/dist/utils/index.js.map +4 -4
  123. package/dist/utils/token-estimate.d.ts +27 -0
  124. package/dist/utils/token-estimate.d.ts.map +1 -1
  125. package/instructions/llm/memory-consolidator.md +19 -3
  126. package/instructions/system.md +10 -2
  127. package/package.json +2 -2
package/dist/hq/index.js CHANGED
@@ -1,3 +1,57 @@
1
+ // src/hq/bridge-context.ts
2
+ function createBridgeContext(options) {
3
+ const publisher = options.publisher;
4
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
5
+ const bridgeSessionId = options.sessionId;
6
+ const offs = [];
7
+ function sessionIdTag(eventSessionId) {
8
+ const tag = eventSessionId ?? bridgeSessionId;
9
+ return tag !== void 0 && tag.length > 0 ? { sessionId: tag } : {};
10
+ }
11
+ function safePublish(publishOptions) {
12
+ try {
13
+ publisher.publishEvent(publishOptions);
14
+ } catch {
15
+ }
16
+ }
17
+ function track(off) {
18
+ offs.push(off);
19
+ }
20
+ function dispose() {
21
+ for (const off of offs) {
22
+ try {
23
+ off();
24
+ } catch {
25
+ }
26
+ }
27
+ offs.length = 0;
28
+ }
29
+ return { now, sessionIdTag, safePublish, track, dispose };
30
+ }
31
+
32
+ // src/hq/auth-audit.ts
33
+ import { appendFileSync, mkdirSync } from "node:fs";
34
+ import * as path from "node:path";
35
+ function hqAuthAuditPath(dataDir) {
36
+ return path.join(dataDir, "auth-audit.jsonl");
37
+ }
38
+ function appendHqAuthAudit(dataDir, entry, options) {
39
+ try {
40
+ mkdirSync(dataDir, { recursive: true });
41
+ const line = `${JSON.stringify(entry)}
42
+ `;
43
+ appendFileSync(hqAuthAuditPath(dataDir), line, { encoding: "utf8", flag: "a" });
44
+ } catch (err) {
45
+ options?.onError?.(err instanceof Error ? err : new Error(String(err)));
46
+ }
47
+ }
48
+ function logHqAuthAudit(dataDir, partial, options) {
49
+ const at = partial.at ?? (options?.now?.() ?? Date.now());
50
+ const { at: _omit, ...rest } = partial;
51
+ void _omit;
52
+ appendHqAuthAudit(dataDir, { at, ...rest }, options);
53
+ }
54
+
1
55
  // src/hq/protocol/tool.ts
2
56
  var DEFAULT_HQ_REDACTION_POLICY = {
3
57
  rawContent: true,
@@ -1424,7 +1478,7 @@ var HqPublisher = class {
1424
1478
  };
1425
1479
 
1426
1480
  // src/hq/factory.ts
1427
- import { createHash as createHash2, randomUUID as randomUUID4 } from "node:crypto";
1481
+ import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
1428
1482
  import * as fs4 from "node:fs";
1429
1483
  import { hostname } from "node:os";
1430
1484
  import { basename as basename5 } from "node:path";
@@ -1432,23 +1486,23 @@ import { basename as basename5 } from "node:path";
1432
1486
  // src/coordination/global-mailbox.ts
1433
1487
  import { randomUUID as randomUUID2 } from "node:crypto";
1434
1488
  import * as fsp from "node:fs/promises";
1435
- import * as path4 from "node:path";
1489
+ import * as path5 from "node:path";
1436
1490
 
1437
1491
  // src/utils/atomic-write.ts
1438
1492
  import { randomBytes } from "node:crypto";
1439
1493
  import * as fs2 from "node:fs/promises";
1440
1494
  import { watch as watchDir } from "node:fs";
1441
- import * as path3 from "node:path";
1495
+ import * as path4 from "node:path";
1442
1496
 
1443
1497
  // src/utils/session-scoped-path.ts
1444
- import * as path from "node:path";
1498
+ import * as path2 from "node:path";
1445
1499
  function sessionScopedPath(dir, sessionId, suffix) {
1446
1500
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
1447
1501
  throw invalid(sessionId);
1448
1502
  }
1449
- const resolved = path.resolve(dir, `${sessionId}${suffix}`);
1450
- const rel = path.relative(path.resolve(dir), resolved);
1451
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
1503
+ const resolved = path2.resolve(dir, `${sessionId}${suffix}`);
1504
+ const rel = path2.relative(path2.resolve(dir), resolved);
1505
+ if (rel.startsWith("..") || path2.isAbsolute(rel)) {
1452
1506
  throw invalid(sessionId);
1453
1507
  }
1454
1508
  return resolved;
@@ -1466,21 +1520,21 @@ function invalid(sessionId) {
1466
1520
  import { createHash } from "node:crypto";
1467
1521
  import * as fs from "node:fs";
1468
1522
  import * as os from "node:os";
1469
- import * as path2 from "node:path";
1523
+ import * as path3 from "node:path";
1470
1524
  function canonicalProjectRoot(absRoot) {
1471
- const checkoutRoot = path2.resolve(absRoot);
1472
- const dotGit = path2.join(checkoutRoot, ".git");
1525
+ const checkoutRoot = path3.resolve(absRoot);
1526
+ const dotGit = path3.join(checkoutRoot, ".git");
1473
1527
  try {
1474
1528
  if (!fs.statSync(dotGit).isFile()) return checkoutRoot;
1475
1529
  const gitDirLine = fs.readFileSync(dotGit, "utf8").trim();
1476
1530
  const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
1477
1531
  if (!match?.[1]) return checkoutRoot;
1478
- const gitDir = path2.resolve(checkoutRoot, match[1].trim());
1479
- const commonDirFile = path2.join(gitDir, "commondir");
1532
+ const gitDir = path3.resolve(checkoutRoot, match[1].trim());
1533
+ const commonDirFile = path3.join(gitDir, "commondir");
1480
1534
  if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
1481
- const commonDir = path2.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
1482
- if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
1483
- return path2.dirname(commonDir);
1535
+ const commonDir = path3.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
1536
+ if (path3.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
1537
+ return path3.dirname(commonDir);
1484
1538
  } catch {
1485
1539
  return checkoutRoot;
1486
1540
  }
@@ -1490,7 +1544,7 @@ function projectHash(absRoot) {
1490
1544
  }
1491
1545
  function projectSlug(absRoot) {
1492
1546
  const identityRoot = canonicalProjectRoot(absRoot);
1493
- const base = slugify(path2.basename(identityRoot));
1547
+ const base = slugify(path3.basename(identityRoot));
1494
1548
  const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
1495
1549
  return `${base}-${hash}`;
1496
1550
  }
@@ -1499,83 +1553,83 @@ function slugify(name) {
1499
1553
  }
1500
1554
  function wstackGlobalRoot() {
1501
1555
  const fromEnv = process.env["WRONGSTACK_HOME"];
1502
- if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
1503
- return path2.join(os.homedir(), ".wrongstack");
1556
+ if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
1557
+ return path3.join(os.homedir(), ".wrongstack");
1504
1558
  }
1505
1559
  function resolveWstackPaths(opts) {
1506
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
1560
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
1507
1561
  const homeDir = opts.userHome ?? os.homedir();
1508
1562
  const hash = projectHash(opts.projectRoot);
1509
1563
  const slug = projectSlug(opts.projectRoot);
1510
- const projectDir = path2.join(globalRoot, "projects", slug);
1564
+ const projectDir = path3.join(globalRoot, "projects", slug);
1511
1565
  return {
1512
1566
  globalRoot,
1513
1567
  projectRoot: opts.projectRoot,
1514
1568
  homeDir,
1515
1569
  configDir: globalRoot,
1516
- globalConfig: path2.join(globalRoot, "config.json"),
1517
- profilesDir: path2.join(globalRoot, "profiles"),
1570
+ globalConfig: path3.join(globalRoot, "config.json"),
1571
+ profilesDir: path3.join(globalRoot, "profiles"),
1518
1572
  profileConfig: (name) => {
1519
1573
  const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
1520
- return path2.join(globalRoot, "profiles", safe || "default", "config.json");
1574
+ return path3.join(globalRoot, "profiles", safe || "default", "config.json");
1521
1575
  },
1522
1576
  profileStatuslineConfig: (name) => {
1523
1577
  const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
1524
- return path2.join(globalRoot, "profiles", safe || "default", "statusline.json");
1578
+ return path3.join(globalRoot, "profiles", safe || "default", "statusline.json");
1525
1579
  },
1526
1580
  profileModeConfig: (name) => {
1527
1581
  const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
1528
- return path2.join(globalRoot, "profiles", safe || "default", "mode.json");
1582
+ return path3.join(globalRoot, "profiles", safe || "default", "mode.json");
1529
1583
  },
1530
1584
  profileProviderStatus: (name) => {
1531
1585
  const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
1532
- return path2.join(globalRoot, "profiles", safe || "default", "provider-status.json");
1586
+ return path3.join(globalRoot, "profiles", safe || "default", "provider-status.json");
1533
1587
  },
1534
1588
  profileUpdateCache: (name) => {
1535
1589
  const safe = name.replace(/[/\\:]/g, "_").replace(/\.\./g, "_");
1536
- return path2.join(globalRoot, "profiles", safe || "default", "update-cache.json");
1590
+ return path3.join(globalRoot, "profiles", safe || "default", "update-cache.json");
1537
1591
  },
1538
- secretsKey: path2.join(globalRoot, ".key"),
1539
- globalMemory: path2.join(globalRoot, "memory.md"),
1540
- globalSkills: path2.join(globalRoot, "skills"),
1541
- globalClaudeSkills: path2.join(homeDir, ".claude", "skills"),
1542
- globalDesignKits: path2.join(globalRoot, "design-kits"),
1543
- globalPrompts: path2.join(globalRoot, "prompts"),
1544
- globalInstructions: path2.join(globalRoot, "instructions"),
1545
- promptUsage: path2.join(globalRoot, "prompt-usage.json"),
1546
- cacheDir: path2.join(globalRoot, "cache"),
1547
- modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
1548
- modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
1549
- historyFile: path2.join(globalRoot, "history"),
1550
- logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
1592
+ secretsKey: path3.join(globalRoot, ".key"),
1593
+ globalMemory: path3.join(globalRoot, "memory.md"),
1594
+ globalSkills: path3.join(globalRoot, "skills"),
1595
+ globalClaudeSkills: path3.join(homeDir, ".claude", "skills"),
1596
+ globalDesignKits: path3.join(globalRoot, "design-kits"),
1597
+ globalPrompts: path3.join(globalRoot, "prompts"),
1598
+ globalInstructions: path3.join(globalRoot, "instructions"),
1599
+ promptUsage: path3.join(globalRoot, "prompt-usage.json"),
1600
+ cacheDir: path3.join(globalRoot, "cache"),
1601
+ modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
1602
+ modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
1603
+ historyFile: path3.join(globalRoot, "history"),
1604
+ logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
1551
1605
  projectDir,
1552
- projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
1553
- projectMemory: path2.join(projectDir, "memory.md"),
1554
- projectSessions: path2.join(projectDir, "sessions"),
1555
- projectTrust: path2.join(projectDir, "trust.json"),
1556
- projectMeta: path2.join(projectDir, "meta.json"),
1557
- projectLocalConfig: path2.join(projectDir, "config.local.json"),
1558
- inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
1559
- inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
1560
- inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
1561
- inProjectClaudeSkills: path2.join(opts.projectRoot, ".claude", "skills"),
1562
- inProjectPrompts: path2.join(opts.projectRoot, ".wrongstack", "prompts"),
1563
- inProjectInstructions: path2.join(opts.projectRoot, ".wrongstack", "instructions"),
1564
- inProjectDesignKits: path2.join(opts.projectRoot, ".wrongstack", "design-kits"),
1565
- inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
1606
+ projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
1607
+ projectMemory: path3.join(projectDir, "memory.md"),
1608
+ projectSessions: path3.join(projectDir, "sessions"),
1609
+ projectTrust: path3.join(projectDir, "trust.json"),
1610
+ projectMeta: path3.join(projectDir, "meta.json"),
1611
+ projectLocalConfig: path3.join(projectDir, "config.local.json"),
1612
+ inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
1613
+ inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
1614
+ inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
1615
+ inProjectClaudeSkills: path3.join(opts.projectRoot, ".claude", "skills"),
1616
+ inProjectPrompts: path3.join(opts.projectRoot, ".wrongstack", "prompts"),
1617
+ inProjectInstructions: path3.join(opts.projectRoot, ".wrongstack", "instructions"),
1618
+ inProjectDesignKits: path3.join(opts.projectRoot, ".wrongstack", "design-kits"),
1619
+ inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
1566
1620
  projectHash: hash,
1567
1621
  projectSlug: slug,
1568
- projectGoal: path2.join(projectDir, "goal.json"),
1569
- projectInputHistory: path2.join(projectDir, "input-history.json"),
1570
- projectSpecs: path2.join(projectDir, "specs"),
1571
- projectTaskGraphs: path2.join(projectDir, "task-graphs"),
1572
- projectSddSession: path2.join(projectDir, "sdd-session.json"),
1573
- projectPlan: path2.join(projectDir, "plan.json"),
1574
- projectAutophase: path2.join(projectDir, "autophase"),
1575
- projectSddBoards: path2.join(projectDir, "sdd-boards"),
1576
- syncConfig: path2.join(globalRoot, "sync.json"),
1577
- configHistoryDir: path2.join(globalRoot, "config-history"),
1578
- projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
1622
+ projectGoal: path3.join(projectDir, "goal.json"),
1623
+ projectInputHistory: path3.join(projectDir, "input-history.json"),
1624
+ projectSpecs: path3.join(projectDir, "specs"),
1625
+ projectTaskGraphs: path3.join(projectDir, "task-graphs"),
1626
+ projectSddSession: path3.join(projectDir, "sdd-session.json"),
1627
+ projectPlan: path3.join(projectDir, "plan.json"),
1628
+ projectAutophase: path3.join(projectDir, "autophase"),
1629
+ projectSddBoards: path3.join(projectDir, "sdd-boards"),
1630
+ syncConfig: path3.join(globalRoot, "sync.json"),
1631
+ configHistoryDir: path3.join(globalRoot, "config-history"),
1632
+ projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
1579
1633
  };
1580
1634
  }
1581
1635
 
@@ -1683,9 +1737,9 @@ var FsError = class extends WrongStackError {
1683
1737
 
1684
1738
  // src/utils/atomic-write.ts
1685
1739
  async function atomicWrite(targetPath, content, opts = {}) {
1686
- const dir = path3.dirname(targetPath);
1740
+ const dir = path4.dirname(targetPath);
1687
1741
  await fs2.mkdir(dir, { recursive: true });
1688
- const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
1742
+ const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
1689
1743
  try {
1690
1744
  if (typeof content === "string") {
1691
1745
  await fs2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -1727,9 +1781,9 @@ async function atomicWrite(targetPath, content, opts = {}) {
1727
1781
  }
1728
1782
  }
1729
1783
  async function withFileLock(targetPath, fn, opts = {}) {
1730
- const dir = path3.dirname(targetPath);
1784
+ const dir = path4.dirname(targetPath);
1731
1785
  await fs2.mkdir(dir, { recursive: true });
1732
- const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
1786
+ const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
1733
1787
  const timeoutMs = opts.timeoutMs ?? 15e3;
1734
1788
  const staleMs = opts.staleMs ?? 3e4;
1735
1789
  const started = Date.now();
@@ -1788,8 +1842,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
1788
1842
  }
1789
1843
  }
1790
1844
  async function waitForLockRelease(lockPath, remainingMs) {
1791
- const parentDir = path3.dirname(lockPath);
1792
- const lockName = path3.basename(lockPath);
1845
+ const parentDir = path4.dirname(lockPath);
1846
+ const lockName = path4.basename(lockPath);
1793
1847
  const intervalMs = Math.min(remainingMs, 100);
1794
1848
  return new Promise((resolve4) => {
1795
1849
  let settled = false;
@@ -2127,15 +2181,15 @@ var GlobalMailbox = class {
2127
2181
  * @param eventEmitter — optional SSE event emitter for HTTP bridge push
2128
2182
  */
2129
2183
  constructor(projectDir, events, hqPublisher, eventEmitter) {
2130
- this.messagePath = path4.join(projectDir, MAILBOX_FILE);
2131
- this.registryPath = path4.join(projectDir, "_mailbox.registry.json");
2132
- this.clientRegistryPath = path4.join(projectDir, CLIENT_REGISTRY_FILE);
2184
+ this.messagePath = path5.join(projectDir, MAILBOX_FILE);
2185
+ this.registryPath = path5.join(projectDir, "_mailbox.registry.json");
2186
+ this.clientRegistryPath = path5.join(projectDir, CLIENT_REGISTRY_FILE);
2133
2187
  this._events = events;
2134
2188
  this._hqPublisher = hqPublisher;
2135
2189
  this.eventEmitter = eventEmitter;
2136
2190
  }
2137
2191
  get hqMailboxId() {
2138
- return `${path4.basename(path4.dirname(this.messagePath))}:mailbox`;
2192
+ return `${path5.basename(path5.dirname(this.messagePath))}:mailbox`;
2139
2193
  }
2140
2194
  get hqPublisher() {
2141
2195
  return typeof this._hqPublisher === "function" ? this._hqPublisher() : this._hqPublisher;
@@ -2174,7 +2228,7 @@ var GlobalMailbox = class {
2174
2228
  expiresAt: input.ttlMs !== void 0 ? new Date(Date.now() + input.ttlMs).toISOString() : void 0
2175
2229
  };
2176
2230
  const line = JSON.stringify(msg) + LINE_SEPARATOR;
2177
- await fsp.mkdir(path4.dirname(this.messagePath), { recursive: true });
2231
+ await fsp.mkdir(path5.dirname(this.messagePath), { recursive: true });
2178
2232
  await withFileLock(this.messagePath, async () => {
2179
2233
  await fsp.appendFile(this.messagePath, line, "utf8");
2180
2234
  const { mtimeMs, size } = await this._statMessageFile();
@@ -3148,7 +3202,7 @@ var GlobalMailbox = class {
3148
3202
  this._messageCacheSize = size;
3149
3203
  }
3150
3204
  async _ensureRegistry() {
3151
- await fsp.mkdir(path4.dirname(this.registryPath), { recursive: true });
3205
+ await fsp.mkdir(path5.dirname(this.registryPath), { recursive: true });
3152
3206
  }
3153
3207
  /** Minimal shape-check for a deserialized agent registry entry.
3154
3208
  * Logs a warning and returns `false` for structurally invalid data
@@ -3241,7 +3295,7 @@ var GlobalMailbox = class {
3241
3295
  }
3242
3296
  // ── Client registry internals ───────────────────────────────────────────
3243
3297
  async _ensureClientRegistry() {
3244
- await fsp.mkdir(path4.dirname(this.clientRegistryPath), { recursive: true });
3298
+ await fsp.mkdir(path5.dirname(this.clientRegistryPath), { recursive: true });
3245
3299
  }
3246
3300
  async _readClientRegistry(opts) {
3247
3301
  if (!opts?.fresh && this._clientRegistryCache && Date.now() - this._clientRegistryCacheAt < REGISTRY_CACHE_TTL_MS) {
@@ -3293,21 +3347,30 @@ var GlobalMailbox = class {
3293
3347
  };
3294
3348
 
3295
3349
  // src/hq/auth-store.ts
3296
- import { randomBytes as randomBytes2, randomUUID as randomUUID3, scrypt, timingSafeEqual } from "node:crypto";
3350
+ import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID3, scrypt, timingSafeEqual } from "node:crypto";
3297
3351
  import * as fs3 from "node:fs/promises";
3298
3352
  import * as syncFs from "node:fs";
3299
- import * as path5 from "node:path";
3353
+ import * as path6 from "node:path";
3300
3354
  var HQ_AUTH_FILE_VERSION = 1;
3301
3355
  function defaultHqDataDir() {
3302
- return path5.join(wstackGlobalRoot(), "hq");
3356
+ return path6.join(wstackGlobalRoot(), "hq");
3303
3357
  }
3304
3358
  function resolveHqDataDir(override, env = process.env) {
3305
3359
  const raw = override ?? env["WRONGSTACK_HQ_DATA_DIR"]?.trim();
3306
3360
  if (!raw) return defaultHqDataDir();
3307
- return path5.isAbsolute(raw) ? path5.resolve(raw) : path5.resolve(process.cwd(), raw);
3361
+ return path6.isAbsolute(raw) ? path6.resolve(raw) : path6.resolve(process.cwd(), raw);
3362
+ }
3363
+ function isTokenExpired(token, at = Date.now()) {
3364
+ if (token === void 0) return false;
3365
+ const expiresAt = token.expiresAt;
3366
+ if (expiresAt === void 0) return false;
3367
+ const parsed = Date.parse(expiresAt);
3368
+ if (!Number.isFinite(parsed)) return false;
3369
+ return at >= parsed;
3308
3370
  }
3309
3371
  function tokenHasCapability(token, capability) {
3310
3372
  if (token === void 0) return false;
3373
+ if (isTokenExpired(token)) return false;
3311
3374
  if (token.capabilities === void 0) return true;
3312
3375
  return token.capabilities.includes(capability);
3313
3376
  }
@@ -3317,11 +3380,32 @@ function emptyHqAuthFile() {
3317
3380
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3318
3381
  };
3319
3382
  }
3383
+ var HQ_AUTH_CONTENT_HASH_REDACTED = "<redacted>";
3384
+ function hqAuthContentHash(file) {
3385
+ const REDACTED = HQ_AUTH_CONTENT_HASH_REDACTED;
3386
+ const redactToken = (t) => ({ ...t, token: REDACTED });
3387
+ try {
3388
+ const projection = {
3389
+ version: file.version,
3390
+ updatedAt: file.updatedAt,
3391
+ ...file.redactionPolicy !== void 0 ? { redactionPolicy: file.redactionPolicy } : {},
3392
+ ...file.browserTokens !== void 0 ? { browserTokens: file.browserTokens.map(redactToken) } : {},
3393
+ ...file.clientTokens !== void 0 ? { clientTokens: file.clientTokens.map(redactToken) } : {},
3394
+ ...file.passwordHash !== void 0 ? { passwordHash: REDACTED } : {},
3395
+ ...file.cookieSecret !== void 0 ? { cookieSecret: REDACTED } : {},
3396
+ ...file.alertRules !== void 0 ? { alertRules: file.alertRules } : {}
3397
+ };
3398
+ const payload = JSON.stringify(projection);
3399
+ return createHash2("sha256").update(payload).digest("hex");
3400
+ } catch {
3401
+ return void 0;
3402
+ }
3403
+ }
3320
3404
  function hqAuthFilePath(dataDir) {
3321
- return path5.join(dataDir, "auth.json");
3405
+ return path6.join(dataDir, "auth.json");
3322
3406
  }
3323
3407
  function hqRuntimeFilePath(dataDir) {
3324
- return path5.join(dataDir, "runtime.json");
3408
+ return path6.join(dataDir, "runtime.json");
3325
3409
  }
3326
3410
  async function writeHqRuntimeFile(dataDir, file) {
3327
3411
  const payload = {
@@ -3407,7 +3491,23 @@ async function ensureHqFirstRunAuthFile(dataDir, opts = {}) {
3407
3491
  cookieSecret: mintHqCookieSecret()
3408
3492
  };
3409
3493
  await writeHqAuthFile(dataDir, authFile2);
3410
- return { authFile: await readHqAuthFile(dataDir, opts), created: false };
3494
+ const persisted2 = await readHqAuthFile(dataDir, opts);
3495
+ const hash2 = hqAuthContentHash(persisted2);
3496
+ const actorField2 = opts.actor ? { actor: opts.actor } : {};
3497
+ logHqAuthAudit(
3498
+ dataDir,
3499
+ {
3500
+ kind: "password-rotate",
3501
+ scope: "browser",
3502
+ tokenId: "(password-rotation)",
3503
+ ...hash2 !== void 0 ? { contentHash: hash2 } : {},
3504
+ ...actorField2
3505
+ },
3506
+ {
3507
+ onError: (err) => opts.warn?.(`HQ password-rotate audit write failed: ${err.message}`)
3508
+ }
3509
+ );
3510
+ return { authFile: persisted2, created: false };
3411
3511
  }
3412
3512
  }
3413
3513
  return { authFile: existing, created: false };
@@ -3417,8 +3517,14 @@ async function ensureHqFirstRunAuthFile(dataDir, opts = {}) {
3417
3517
  return { authFile: await readHqAuthFile(dataDir, opts), created: false };
3418
3518
  }
3419
3519
  }
3420
- const browserToken = { ...mintHqToken("first-run browser"), capabilities: ["control.enqueue"] };
3421
- const clientToken = { ...mintHqToken("first-run client"), capabilities: ["telemetry.publish"] };
3520
+ const browserToken = {
3521
+ ...mintHqToken({ label: "first-run browser", ttlMs: opts.tokenTtlMs }),
3522
+ capabilities: ["control.enqueue"]
3523
+ };
3524
+ const clientToken = {
3525
+ ...mintHqToken({ label: "first-run client", ttlMs: opts.tokenTtlMs }),
3526
+ capabilities: ["telemetry.publish"]
3527
+ };
3422
3528
  const authFile = {
3423
3529
  version: HQ_AUTH_FILE_VERSION,
3424
3530
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3430,7 +3536,32 @@ async function ensureHqFirstRunAuthFile(dataDir, opts = {}) {
3430
3536
  authFile.cookieSecret = mintHqCookieSecret();
3431
3537
  }
3432
3538
  await writeHqAuthFile(dataDir, authFile);
3433
- return { authFile: await readHqAuthFile(dataDir, opts), created: true, browserToken, clientToken };
3539
+ const persisted = await readHqAuthFile(dataDir, opts);
3540
+ const hash = hqAuthContentHash(persisted);
3541
+ const hashField = hash !== void 0 ? { contentHash: hash } : {};
3542
+ const actorField = opts.actor ? { actor: opts.actor } : {};
3543
+ for (const [scope, token] of [
3544
+ ["browser", browserToken],
3545
+ ["client", clientToken]
3546
+ ]) {
3547
+ logHqAuthAudit(
3548
+ dataDir,
3549
+ {
3550
+ kind: "first-run",
3551
+ scope,
3552
+ tokenId: token.id,
3553
+ ...hashField,
3554
+ ...token.label ? { label: token.label } : {},
3555
+ capabilities: token.capabilities,
3556
+ ...token.expiresAt ? { expiresAt: token.expiresAt } : {},
3557
+ ...actorField
3558
+ },
3559
+ {
3560
+ onError: (err) => opts.warn?.(`HQ first-run audit write failed: ${err.message}`)
3561
+ }
3562
+ );
3563
+ }
3564
+ return { authFile: persisted, created: true, browserToken, clientToken };
3434
3565
  }
3435
3566
  async function mutateHqAuthFile(dataDir, mutator, opts = {}) {
3436
3567
  const current = await readHqAuthFile(dataDir, opts);
@@ -3474,13 +3605,16 @@ async function verifyHqPassword(password, hash) {
3474
3605
  function mintHqCookieSecret() {
3475
3606
  return randomBytes2(32).toString("base64url");
3476
3607
  }
3477
- function mintHqToken(label) {
3478
- const now = (/* @__PURE__ */ new Date()).toISOString();
3608
+ function mintHqToken(labelOrOptions) {
3609
+ const opts = typeof labelOrOptions === "string" ? { label: labelOrOptions } : labelOrOptions ?? {};
3610
+ const at = opts.now ?? Date.now();
3611
+ const createdAtIso = new Date(at).toISOString();
3479
3612
  return {
3480
3613
  id: randomUUID3(),
3481
3614
  token: randomUUID3().replace(/-/g, "") + randomUUID3().replace(/-/g, ""),
3482
- ...label ? { label } : {},
3483
- createdAt: now
3615
+ createdAt: createdAtIso,
3616
+ ...opts.label ? { label: opts.label } : {},
3617
+ ...opts.ttlMs !== void 0 && Number.isFinite(opts.ttlMs) && opts.ttlMs > 0 ? { expiresAt: new Date(at + opts.ttlMs).toISOString() } : {}
3484
3618
  };
3485
3619
  }
3486
3620
  var mintHqBrowserToken = mintHqToken;
@@ -3491,7 +3625,7 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
3491
3625
  let closed = false;
3492
3626
  let watcher;
3493
3627
  try {
3494
- watcher = syncFs.watch(path5.dirname(file), { recursive: false });
3628
+ watcher = syncFs.watch(path6.dirname(file), { recursive: false });
3495
3629
  } catch (err) {
3496
3630
  opts.warn?.(`HQ auth watcher could not start: ${err.message}`);
3497
3631
  return { close: () => {
@@ -3515,7 +3649,7 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
3515
3649
  watcher.on("change", (eventType, filename) => {
3516
3650
  const name = typeof filename === "string" ? filename : "";
3517
3651
  if (eventType === "rename" || eventType === "change") {
3518
- if (!name || name === "auth.json" || name === path5.basename(file)) {
3652
+ if (!name || name === "auth.json" || name === path6.basename(file)) {
3519
3653
  trigger();
3520
3654
  }
3521
3655
  }
@@ -3601,10 +3735,10 @@ function resolveHqConfig(options = {}) {
3601
3735
  };
3602
3736
  }
3603
3737
  function stableMachineId() {
3604
- return createHash2("sha256").update(hostname()).digest("hex").slice(0, 12);
3738
+ return createHash3("sha256").update(hostname()).digest("hex").slice(0, 12);
3605
3739
  }
3606
3740
  function deriveProjectId(projectRoot) {
3607
- return createHash2("sha256").update(projectRoot).digest("hex").slice(0, 12);
3741
+ return createHash3("sha256").update(projectRoot).digest("hex").slice(0, 12);
3608
3742
  }
3609
3743
  function createHqPublisherFromEnv(options) {
3610
3744
  const config = options.config ?? resolveHqConfig({ config: options.appConfig?.hq });
@@ -4057,7 +4191,7 @@ function startSessionTelemetryBridge(opts) {
4057
4191
  // src/hq/fleet-bridge.ts
4058
4192
  function startFleetTelemetryBridge(opts) {
4059
4193
  const { events, publisher, runId } = opts;
4060
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4194
+ const ctx = createBridgeContext(opts);
4061
4195
  let lastHash = "";
4062
4196
  function buildPayload(stats) {
4063
4197
  const subagents = stats.subagentStatuses.map((s) => ({
@@ -4080,21 +4214,20 @@ function startFleetTelemetryBridge(opts) {
4080
4214
  };
4081
4215
  }
4082
4216
  const off = events.on("coordinator.stats", (stats) => {
4217
+ const payload = buildPayload(stats);
4218
+ const hash = JSON.stringify(payload);
4219
+ if (hash === lastHash) return;
4220
+ lastHash = hash;
4083
4221
  try {
4084
- const payload = buildPayload(stats);
4085
- const hash = JSON.stringify(payload);
4086
- if (hash === lastHash) return;
4087
- lastHash = hash;
4088
4222
  publisher.publishFleetSnapshot(payload, {
4089
- ...opts.sessionId !== void 0 ? { sessionId: opts.sessionId } : {},
4090
- timestamp: now()
4223
+ ...ctx.sessionIdTag(),
4224
+ timestamp: ctx.now()
4091
4225
  });
4092
4226
  } catch {
4093
4227
  }
4094
4228
  });
4095
- return () => {
4096
- off();
4097
- };
4229
+ ctx.track(off);
4230
+ return ctx.dispose;
4098
4231
  }
4099
4232
  var FLEET_STATUS_MAP = {
4100
4233
  running: "running",
@@ -4134,27 +4267,22 @@ function extractDecisionFields(dec) {
4134
4267
  return out;
4135
4268
  }
4136
4269
  function startBrainTelemetryBridge(opts) {
4137
- const { events, publisher } = opts;
4138
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4270
+ const { events } = opts;
4271
+ const ctx = createBridgeContext(opts);
4139
4272
  function publish(kind, payload, at) {
4140
- try {
4141
- const full = { kind, at, ...payload };
4142
- publisher.publishEvent({
4143
- type: "brain.event",
4144
- payload: full,
4145
- ...opts.sessionId !== void 0 ? { sessionId: opts.sessionId } : {},
4146
- timestamp: now()
4147
- });
4148
- } catch {
4149
- }
4273
+ ctx.safePublish({
4274
+ type: "brain.event",
4275
+ payload: { kind, at, ...payload },
4276
+ ...ctx.sessionIdTag(),
4277
+ timestamp: ctx.now()
4278
+ });
4150
4279
  }
4151
- const offs = [];
4152
- offs.push(
4280
+ ctx.track(
4153
4281
  events.on("brain.decision_requested", (p) => {
4154
4282
  publish("decision_requested", extractRequestFields(p.request), p.at);
4155
4283
  })
4156
4284
  );
4157
- offs.push(
4285
+ ctx.track(
4158
4286
  events.on("brain.decision_answered", (p) => {
4159
4287
  publish("decision_answered", {
4160
4288
  ...extractRequestFields(p.request),
@@ -4162,7 +4290,7 @@ function startBrainTelemetryBridge(opts) {
4162
4290
  }, p.at);
4163
4291
  })
4164
4292
  );
4165
- offs.push(
4293
+ ctx.track(
4166
4294
  events.on("brain.decision_ask_human", (p) => {
4167
4295
  publish("decision_ask_human", {
4168
4296
  ...extractRequestFields(p.request),
@@ -4170,7 +4298,7 @@ function startBrainTelemetryBridge(opts) {
4170
4298
  }, p.at);
4171
4299
  })
4172
4300
  );
4173
- offs.push(
4301
+ ctx.track(
4174
4302
  events.on("brain.decision_denied", (p) => {
4175
4303
  publish("decision_denied", {
4176
4304
  ...extractRequestFields(p.request),
@@ -4178,7 +4306,7 @@ function startBrainTelemetryBridge(opts) {
4178
4306
  }, p.at);
4179
4307
  })
4180
4308
  );
4181
- offs.push(
4309
+ ctx.track(
4182
4310
  events.on("brain.human_answered", (p) => {
4183
4311
  const detail = typeof p.text === "string" ? p.text : p.optionId;
4184
4312
  publish("human_answered", {
@@ -4188,7 +4316,7 @@ function startBrainTelemetryBridge(opts) {
4188
4316
  }, p.at);
4189
4317
  })
4190
4318
  );
4191
- offs.push(
4319
+ ctx.track(
4192
4320
  events.on("brain.intervention", (p) => {
4193
4321
  publish("intervention", {
4194
4322
  ...extractRequestFields(p.request),
@@ -4198,29 +4326,22 @@ function startBrainTelemetryBridge(opts) {
4198
4326
  }, p.at);
4199
4327
  })
4200
4328
  );
4201
- return () => {
4202
- for (const off of offs) off();
4203
- };
4329
+ return ctx.dispose;
4204
4330
  }
4205
4331
 
4206
4332
  // src/hq/worktree-bridge.ts
4207
4333
  function startWorktreeTelemetryBridge(opts) {
4208
- const { events, publisher } = opts;
4209
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4210
- function publish(payload, sessionId) {
4211
- try {
4212
- publisher.publishEvent({
4213
- type: "worktree.event",
4214
- payload,
4215
- ...sessionId !== void 0 && sessionId !== "" ? { sessionId } : {},
4216
- ...opts.sessionId !== void 0 ? { sessionId: opts.sessionId } : {},
4217
- timestamp: now()
4218
- });
4219
- } catch {
4220
- }
4334
+ const { events } = opts;
4335
+ const ctx = createBridgeContext(opts);
4336
+ function publish(payload, eventSessionId) {
4337
+ ctx.safePublish({
4338
+ type: "worktree.event",
4339
+ payload,
4340
+ ...ctx.sessionIdTag(eventSessionId),
4341
+ timestamp: ctx.now()
4342
+ });
4221
4343
  }
4222
- const offs = [];
4223
- offs.push(
4344
+ ctx.track(
4224
4345
  events.on("worktree.allocated", (p) => {
4225
4346
  publish(
4226
4347
  {
@@ -4236,7 +4357,7 @@ function startWorktreeTelemetryBridge(opts) {
4236
4357
  );
4237
4358
  })
4238
4359
  );
4239
- offs.push(
4360
+ ctx.track(
4240
4361
  events.on("worktree.committed", (p) => {
4241
4362
  publish(
4242
4363
  {
@@ -4253,7 +4374,7 @@ function startWorktreeTelemetryBridge(opts) {
4253
4374
  );
4254
4375
  })
4255
4376
  );
4256
- offs.push(
4377
+ ctx.track(
4257
4378
  events.on("worktree.merged", (p) => {
4258
4379
  publish(
4259
4380
  {
@@ -4268,7 +4389,7 @@ function startWorktreeTelemetryBridge(opts) {
4268
4389
  );
4269
4390
  })
4270
4391
  );
4271
- offs.push(
4392
+ ctx.track(
4272
4393
  events.on("worktree.conflict", (p) => {
4273
4394
  publish(
4274
4395
  {
@@ -4282,7 +4403,7 @@ function startWorktreeTelemetryBridge(opts) {
4282
4403
  );
4283
4404
  })
4284
4405
  );
4285
- offs.push(
4406
+ ctx.track(
4286
4407
  events.on("worktree.released", (p) => {
4287
4408
  publish(
4288
4409
  {
@@ -4296,7 +4417,7 @@ function startWorktreeTelemetryBridge(opts) {
4296
4417
  );
4297
4418
  })
4298
4419
  );
4299
- offs.push(
4420
+ ctx.track(
4300
4421
  events.on("worktree.failed", (p) => {
4301
4422
  publish(
4302
4423
  {
@@ -4310,63 +4431,52 @@ function startWorktreeTelemetryBridge(opts) {
4310
4431
  );
4311
4432
  })
4312
4433
  );
4313
- return () => {
4314
- for (const off of offs) off();
4315
- };
4434
+ return ctx.dispose;
4316
4435
  }
4317
4436
 
4318
4437
  // src/hq/tool-bridge.ts
4319
4438
  function startToolTelemetryBridge(opts) {
4320
4439
  const { events, publisher } = opts;
4321
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4440
+ const ctx = createBridgeContext(opts);
4322
4441
  const inFlight = /* @__PURE__ */ new Map();
4323
- function sessionIdTag(sessionId) {
4324
- const tag = sessionId ?? opts.sessionId;
4325
- return tag !== void 0 ? { sessionId: tag } : {};
4326
- }
4327
4442
  const offStarted = events.on("tool.started", (p) => {
4328
4443
  inFlight.set(p.id, { name: p.name, startedAt: Date.now() });
4329
- try {
4330
- const payload = {
4331
- toolName: p.name,
4332
- ...p.input !== void 0 ? {
4333
- inputSummary: summarizeHqToolArgs(p.input, {
4334
- policy: publisher.redactionPolicy,
4335
- ...opts.projectRoot !== void 0 ? { projectRoot: opts.projectRoot } : {}
4336
- })
4337
- } : {}
4338
- };
4339
- publisher.publishEvent({
4340
- type: "tool.started",
4341
- payload,
4342
- ...sessionIdTag(p.sessionId),
4343
- timestamp: now()
4344
- });
4345
- } catch {
4346
- }
4444
+ const payload = {
4445
+ toolName: p.name,
4446
+ ...p.input !== void 0 ? {
4447
+ inputSummary: summarizeHqToolArgs(p.input, {
4448
+ policy: publisher.redactionPolicy,
4449
+ ...opts.projectRoot !== void 0 ? { projectRoot: opts.projectRoot } : {}
4450
+ })
4451
+ } : {}
4452
+ };
4453
+ ctx.safePublish({
4454
+ type: "tool.started",
4455
+ payload,
4456
+ ...ctx.sessionIdTag(p.sessionId),
4457
+ timestamp: ctx.now()
4458
+ });
4347
4459
  });
4348
4460
  const offExecuted = events.on("tool.executed", (p) => {
4349
4461
  const id = p.id;
4350
4462
  if (id !== void 0) inFlight.delete(id);
4351
- try {
4352
- const payload = {
4353
- toolName: p.name,
4354
- status: p.ok ? "success" : "error",
4355
- durationMs: p.durationMs,
4356
- ...p.output !== void 0 && p.output.length > 0 ? { outputSummary: truncateForSummary(p.output, publisher.redactionPolicy) } : {}
4357
- };
4358
- publisher.publishEvent({
4359
- type: "tool.completed",
4360
- payload,
4361
- ...sessionIdTag(p.sessionId),
4362
- timestamp: now()
4363
- });
4364
- } catch {
4365
- }
4463
+ const payload = {
4464
+ toolName: p.name,
4465
+ status: p.ok ? "success" : "error",
4466
+ durationMs: p.durationMs,
4467
+ ...p.output !== void 0 && p.output.length > 0 ? { outputSummary: truncateForSummary(p.output, publisher.redactionPolicy) } : {}
4468
+ };
4469
+ ctx.safePublish({
4470
+ type: "tool.completed",
4471
+ payload,
4472
+ ...ctx.sessionIdTag(p.sessionId),
4473
+ timestamp: ctx.now()
4474
+ });
4366
4475
  });
4476
+ ctx.track(offStarted);
4477
+ ctx.track(offExecuted);
4367
4478
  return () => {
4368
- offStarted();
4369
- offExecuted();
4479
+ ctx.dispose();
4370
4480
  inFlight.clear();
4371
4481
  };
4372
4482
  }
@@ -4381,38 +4491,35 @@ function truncateForSummary(output, policy, max = 280) {
4381
4491
 
4382
4492
  // src/hq/cost-bridge.ts
4383
4493
  function startCostTelemetryBridge(opts) {
4384
- const { events, publisher } = opts;
4385
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4494
+ const { events } = opts;
4495
+ const ctx = createBridgeContext(opts);
4386
4496
  const off = events.on("token.accounted", (p) => {
4387
- try {
4388
- const payload = {
4389
- inputTokens: p.usage.input,
4390
- outputTokens: p.usage.output,
4391
- totalTokens: p.usage.input + p.usage.output,
4392
- costUsd: p.cost.total
4393
- };
4394
- if (p.provider !== void 0) payload.provider = p.provider;
4395
- if (p.model !== void 0) payload.model = p.model;
4396
- if (p.usage.cacheRead !== void 0) payload.cacheRead = p.usage.cacheRead;
4397
- if (p.usage.cacheWrite !== void 0) payload.cacheWrite = p.usage.cacheWrite;
4398
- const sessionId = opts.sessionId ?? p.sessionId;
4399
- publisher.publishEvent({
4400
- type: "session.usage",
4401
- payload,
4402
- ...sessionId !== void 0 ? { sessionId } : {},
4403
- timestamp: now()
4404
- });
4405
- } catch {
4406
- }
4497
+ const usage = p.deltaUsage ?? p.usage;
4498
+ const payload = {
4499
+ inputTokens: usage.input,
4500
+ outputTokens: usage.output,
4501
+ totalTokens: usage.input + usage.output,
4502
+ costUsd: p.cost.total
4503
+ };
4504
+ if (p.provider !== void 0) payload.provider = p.provider;
4505
+ if (p.model !== void 0) payload.model = p.model;
4506
+ if (usage.cacheRead !== void 0) payload.cacheRead = usage.cacheRead;
4507
+ if (usage.cacheWrite !== void 0) payload.cacheWrite = usage.cacheWrite;
4508
+ const sessionId = opts.sessionId ?? p.sessionId;
4509
+ ctx.safePublish({
4510
+ type: "session.usage",
4511
+ payload,
4512
+ ...sessionId !== void 0 ? { sessionId } : {},
4513
+ timestamp: ctx.now()
4514
+ });
4407
4515
  });
4408
- return () => {
4409
- off();
4410
- };
4516
+ ctx.track(off);
4517
+ return ctx.dispose;
4411
4518
  }
4412
4519
 
4413
4520
  // src/hq/persistence.ts
4414
4521
  import * as fs6 from "node:fs/promises";
4415
- import * as path6 from "node:path";
4522
+ import * as path7 from "node:path";
4416
4523
  var DEFAULT_EVENT_LOG_MAX_LINES = 5e4;
4417
4524
  var DEFAULT_EVENT_LOG_ROTATE_KEEP = 2e4;
4418
4525
  var HqEventLog = class {
@@ -4423,7 +4530,7 @@ var HqEventLog = class {
4423
4530
  lineCount = 0;
4424
4531
  counted = false;
4425
4532
  constructor(opts) {
4426
- this.filePath = path6.join(opts.dataDir, "events.jsonl");
4533
+ this.filePath = path7.join(opts.dataDir, "events.jsonl");
4427
4534
  this.maxLines = opts.maxLines ?? DEFAULT_EVENT_LOG_MAX_LINES;
4428
4535
  this.rotateKeep = opts.rotateKeep ?? DEFAULT_EVENT_LOG_ROTATE_KEEP;
4429
4536
  }
@@ -4507,7 +4614,7 @@ var HqSnapshotStore = class {
4507
4614
  filePath;
4508
4615
  writeChain = Promise.resolve();
4509
4616
  constructor(opts) {
4510
- this.filePath = path6.join(opts.dataDir, "snapshot.json");
4617
+ this.filePath = path7.join(opts.dataDir, "snapshot.json");
4511
4618
  }
4512
4619
  /** Persist a snapshot. Best-effort, never rejects. */
4513
4620
  save(snapshot) {
@@ -4536,7 +4643,7 @@ var HqTimeseriesStore = class {
4536
4643
  buckets = /* @__PURE__ */ new Map();
4537
4644
  flushChain = Promise.resolve();
4538
4645
  constructor(opts) {
4539
- this.filePath = path6.join(opts.dataDir, "timeseries.jsonl");
4646
+ this.filePath = path7.join(opts.dataDir, "timeseries.jsonl");
4540
4647
  this.bucketMs = opts.bucketMs ?? 5 * 60 * 1e3;
4541
4648
  this.maxBuckets = opts.maxBuckets ?? 2016;
4542
4649
  }
@@ -4655,7 +4762,7 @@ var HqSimpleLog = class {
4655
4762
  filePath;
4656
4763
  writeChain = Promise.resolve();
4657
4764
  constructor(dataDir, filename) {
4658
- this.filePath = path6.join(dataDir, filename);
4765
+ this.filePath = path7.join(dataDir, filename);
4659
4766
  }
4660
4767
  /** Append one record as a JSON line. Best-effort, never rejects. */
4661
4768
  append(record) {
@@ -4825,7 +4932,8 @@ var HqCommandAuditLog = class {
4825
4932
  var DEFAULT_CONFIG = {
4826
4933
  costThresholdUsd: 50,
4827
4934
  staleMachineSeconds: 120,
4828
- maxAgents: 0
4935
+ maxAgents: 0,
4936
+ tokenExpiryWarningHours: 24
4829
4937
  };
4830
4938
  function resolveConfig(config) {
4831
4939
  return { ...DEFAULT_CONFIG, ...config ?? {} };
@@ -4882,6 +4990,27 @@ var RULES = [
4882
4990
  }
4883
4991
  return null;
4884
4992
  }
4993
+ },
4994
+ {
4995
+ id: "token-expired-present",
4996
+ severity: "error",
4997
+ evaluate: (snapshot) => {
4998
+ if (snapshot === null) return null;
4999
+ const stats = snapshot.totals.tokenStats;
5000
+ if (stats === void 0 || stats.expired <= 0) return null;
5001
+ return `${stats.expired} expired token(s) filtered from live auth \u2014 rotate or revoke them`;
5002
+ }
5003
+ },
5004
+ {
5005
+ id: "token-expiry-imminent",
5006
+ severity: "warn",
5007
+ evaluate: (snapshot, config) => {
5008
+ if (snapshot === null) return null;
5009
+ if (config.tokenExpiryWarningHours <= 0) return null;
5010
+ const stats = snapshot.totals.tokenStats;
5011
+ if (stats === void 0 || stats.expiringSoon <= 0) return null;
5012
+ return `${stats.expiringSoon} token(s) expire within ${config.tokenExpiryWarningHours}h \u2014 mint replacements before they lapse`;
5013
+ }
4885
5014
  }
4886
5015
  ];
4887
5016
  var HqAlertEngine = class {
@@ -4987,6 +5116,7 @@ function toAlertMessage(alert) {
4987
5116
  }
4988
5117
  export {
4989
5118
  DEFAULT_HQ_REDACTION_POLICY,
5119
+ HQ_AUTH_CONTENT_HASH_REDACTED,
4990
5120
  HQ_AUTH_FILE_VERSION,
4991
5121
  HQ_CLI_DEFAULT_HOST,
4992
5122
  HQ_COMMAND_TYPES,
@@ -5000,9 +5130,11 @@ export {
5000
5130
  HqSimpleLog,
5001
5131
  HqSnapshotStore,
5002
5132
  HqTimeseriesStore,
5133
+ appendHqAuthAudit,
5003
5134
  assessHqExposure,
5004
5135
  buildTranscriptFromEvents,
5005
5136
  classifyMailboxRecipient,
5137
+ createBridgeContext,
5006
5138
  createGlobalMailbox,
5007
5139
  createHqEventEnvelope,
5008
5140
  createHqPersistence,
@@ -5015,10 +5147,14 @@ export {
5015
5147
  emptyHqAuthFile,
5016
5148
  ensureHqFirstRunAuthFile,
5017
5149
  hashHqPassword,
5150
+ hqAuthAuditPath,
5151
+ hqAuthContentHash,
5018
5152
  hqAuthFilePath,
5019
5153
  hqRuntimeFilePath,
5020
5154
  isLoopbackHost,
5021
5155
  isOpenMode,
5156
+ isTokenExpired,
5157
+ logHqAuthAudit,
5022
5158
  mapMailboxAgentToHqSummary,
5023
5159
  mapMailboxMessageToHqSummary,
5024
5160
  mapSessionEventToEntries,