@wrongstack/core 0.302.0 → 0.303.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 (74) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-status-tracker.d.ts +6 -2
  3. package/dist/chronicle/index.js +1949 -1671
  4. package/dist/chronicle/metrics-store.d.ts +14 -0
  5. package/dist/chronicle/project-server-protocol.d.ts +13 -0
  6. package/dist/chronicle/project-server.js +1756 -1573
  7. package/dist/chronicle/rollup-adapter.d.ts +2 -0
  8. package/dist/chronicle/sqlite-journal.d.ts +59 -0
  9. package/dist/coordination/agents/index.js +4313 -3516
  10. package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
  11. package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
  12. package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
  13. package/dist/coordination/agents/project-agent-consolidation.d.ts +29 -2
  14. package/dist/coordination/agents/project-agent-files.d.ts +12 -3
  15. package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
  16. package/dist/coordination/agents/project-agent-identity.d.ts +22 -9
  17. package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
  18. package/dist/coordination/agents/project-agent-learning-structured.d.ts +27 -1
  19. package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
  20. package/dist/coordination/agents/project-agent-skill-layer.d.ts +101 -0
  21. package/dist/coordination/agents/role-skills.d.ts +11 -1
  22. package/dist/coordination/index.d.ts +1 -1
  23. package/dist/coordination/index.js +4927 -3589
  24. package/dist/coordination/mail-tools.d.ts +3 -3
  25. package/dist/core/context.d.ts +4 -0
  26. package/dist/core/continue-intent.d.ts +2 -0
  27. package/dist/core/conversation-state.d.ts +5 -0
  28. package/dist/core/index.js +129 -19
  29. package/dist/defaults/index.js +1620 -768
  30. package/dist/execution/index.js +2941 -2630
  31. package/dist/goal/index.js +7 -0
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +12269 -9212
  34. package/dist/infrastructure/index.js +722 -672
  35. package/dist/kernel/events/agent-events.d.ts +28 -0
  36. package/dist/kernel/events/memory-events.d.ts +62 -0
  37. package/dist/plugin/index.js +2167 -1986
  38. package/dist/security/index.js +69 -3
  39. package/dist/security/kanban-boundary.d.ts +5 -1
  40. package/dist/session-catalog/client.d.ts +62 -0
  41. package/dist/session-catalog/endpoint.d.ts +6 -0
  42. package/dist/session-catalog/index.d.ts +6 -0
  43. package/dist/session-catalog/index.js +2000 -0
  44. package/dist/session-catalog/project-server.d.ts +3 -0
  45. package/dist/session-catalog/project-server.js +1861 -0
  46. package/dist/session-catalog/protocol.d.ts +284 -0
  47. package/dist/session-catalog/registry.d.ts +59 -0
  48. package/dist/session-catalog/store.d.ts +71 -0
  49. package/dist/storage/index.d.ts +42 -38
  50. package/dist/storage/index.js +13896 -12931
  51. package/dist/storage/plan-store.d.ts +1 -1
  52. package/dist/storage/session-event-bridge.d.ts +2 -2
  53. package/dist/storage/session-store.d.ts +6 -0
  54. package/dist/tasking/index.js +5 -0
  55. package/dist/tools/index.js +2832 -2606
  56. package/dist/types/config/root.d.ts +11 -1
  57. package/dist/types/config/skills-fleet-brain.d.ts +34 -0
  58. package/dist/types/config/ui.d.ts +14 -0
  59. package/dist/types/config.d.ts +1 -0
  60. package/dist/types/context-evidence.d.ts +2 -0
  61. package/dist/types/index.d.ts +2 -2
  62. package/dist/types/index.js +20 -0
  63. package/dist/types/messages.d.ts +8 -0
  64. package/dist/types/multi-agent.d.ts +7 -0
  65. package/dist/types/session.d.ts +19 -0
  66. package/dist/types/task-graph.d.ts +2 -0
  67. package/dist/types/tool-executor.d.ts +2 -0
  68. package/dist/utils/context-evidence.d.ts +13 -1
  69. package/dist/utils/index.js +29 -2
  70. package/instructions/system-lite.md +23 -8
  71. package/instructions/system-pro.md +29 -9
  72. package/instructions/system.md +29 -9
  73. package/package.json +7 -3
  74. package/skills/wrongstack-kanban/SKILL.md +39 -8
@@ -295,7 +295,7 @@ var InMemoryAgentBridge = class {
295
295
  });
296
296
  }
297
297
  this.inflightGuards.add(correlationId);
298
- return new Promise((resolve19, reject) => {
298
+ return new Promise((resolve21, reject) => {
299
299
  const timer = setTimeout(() => {
300
300
  this.inflightGuards.delete(correlationId);
301
301
  this.pendingRequests.delete(correlationId);
@@ -314,7 +314,7 @@ var InMemoryAgentBridge = class {
314
314
  return;
315
315
  }
316
316
  this.pendingRequests.set(correlationId, {
317
- resolve: resolve19,
317
+ resolve: resolve21,
318
318
  reject,
319
319
  timer
320
320
  });
@@ -663,13 +663,13 @@ var SubagentBudget = class _SubagentBudget {
663
663
  if (!bus?.hasListenerFor("budget.threshold_reached")) {
664
664
  return Promise.resolve("stop");
665
665
  }
666
- return new Promise((resolve19) => {
666
+ return new Promise((resolve21) => {
667
667
  let resolved = false;
668
668
  const respond = (d) => {
669
669
  if (resolved) return;
670
670
  resolved = true;
671
671
  clearTimeout(fallback);
672
- resolve19(d);
672
+ resolve21(d);
673
673
  };
674
674
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
675
675
  const sessionId = this.currentSessionId();
@@ -1277,8 +1277,8 @@ function defaultFormatTaskInput(task) {
1277
1277
  }
1278
1278
 
1279
1279
  // src/coordination/agents/agent-prompts.ts
1280
- import { readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
1281
- import * as path5 from "node:path";
1280
+ import { readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
1281
+ import * as path6 from "node:path";
1282
1282
  import { fileURLToPath } from "node:url";
1283
1283
 
1284
1284
  // src/utils/wstack-paths.ts
@@ -1421,8 +1421,8 @@ function resolveWstackPaths(opts) {
1421
1421
  }
1422
1422
 
1423
1423
  // src/coordination/agents/project-agent-identity.ts
1424
- import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
1425
- import * as path4 from "node:path";
1424
+ import { existsSync as existsSync2, readFileSync as readFileSync5 } from "node:fs";
1425
+ import * as path5 from "node:path";
1426
1426
 
1427
1427
  // src/types/runtime-capability-manifest.ts
1428
1428
  var PLAYWRIGHT_ALIASES = {
@@ -1680,22 +1680,6 @@ function inferRuntimeCapabilities(toolNames) {
1680
1680
  return [...ids];
1681
1681
  }
1682
1682
 
1683
- // src/coordination/agents/project-agent-consolidation.ts
1684
- import {
1685
- existsSync,
1686
- mkdirSync,
1687
- readFileSync as readFileSync2,
1688
- rmSync
1689
- } from "node:fs";
1690
- import * as path3 from "node:path";
1691
-
1692
- // src/coordination/agents/project-agent-learning-entries.ts
1693
- function splitLearnedEntries(body) {
1694
- return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
1695
- (entry) => entry.replace(/^# Learned wisdom for .+$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
1696
- ).filter(Boolean);
1697
- }
1698
-
1699
1683
  // src/coordination/agents/project-agent-paths.ts
1700
1684
  import * as path2 from "node:path";
1701
1685
  var AGENT_ROLE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,95}$/i;
@@ -1717,6 +1701,190 @@ function learningPolicyPath(role, projectRoot) {
1717
1701
  return path2.join(roleDir(role, projectRoot), "learning.json");
1718
1702
  }
1719
1703
 
1704
+ // src/coordination/agents/project-agent-consolidation.ts
1705
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync } from "node:fs";
1706
+ import * as path3 from "node:path";
1707
+
1708
+ // src/coordination/agents/project-agent-learning-entries.ts
1709
+ function splitLearnedEntries(body) {
1710
+ return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
1711
+ (entry) => entry.replace(/^#\s+Learned (?:wisdom|instructions) for .+$/im, "").replace(/^>\s*Project-specific learning data for[\s\S]*?$/im, "").replace(/^_No learned entries yet\._$/im, "").replace(/^\*Last capture:[^\n]*\*$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
1712
+ ).filter(Boolean);
1713
+ }
1714
+
1715
+ // src/coordination/agents/project-agent-learning-normalize.ts
1716
+ var MIN_INSTRUCTIVE_LENGTH = 30;
1717
+ function classifyLearnedEntry(text) {
1718
+ const lower = text.toLowerCase();
1719
+ if (/\b(?:avoid|never|don'?t|do not|must not|prevent|watch out|beware|pitfall|gotcha|hazard|risk)\b/.test(
1720
+ lower
1721
+ ))
1722
+ return "warning";
1723
+ if (/\b(?:use|prefer|choose|adopt|leverage|apply|switch to|migrate to)\b/.test(lower) || /\b(?:pattern|approach|strategy|technique|idiom)\b/.test(lower))
1724
+ return "pattern";
1725
+ if (/\b(?:always|must|should|require|ensure|verify|check|run|execute|before|after|when)\b/.test(
1726
+ lower
1727
+ ))
1728
+ return "convention";
1729
+ return "fact";
1730
+ }
1731
+ function normalizeForComparison(text) {
1732
+ return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim().split(/\s+/).sort().join(" ");
1733
+ }
1734
+
1735
+ // src/coordination/agents/project-agent-learning-structured.ts
1736
+ function parseLearnedEntryStamp(entry) {
1737
+ const structuredMatch = entry.match(
1738
+ /<!--\s*learned-stamp:\s*category=([\w-]+);\s*capturedAt=([^;]+?)\s*-->/
1739
+ );
1740
+ if (structuredMatch) {
1741
+ const rawCategory = structuredMatch[1];
1742
+ const candidate = parseLearnedCategory(rawCategory);
1743
+ return {
1744
+ capturedAt: typeof structuredMatch[2] === "string" ? structuredMatch[2].trim() : "",
1745
+ category: candidate ?? null
1746
+ };
1747
+ }
1748
+ const stampMatch = entry.match(/^>\s*(?:\[\s*([\w-]+)\s*\]\s+)?(?:Captured|Taught)\s+(\S+)/m);
1749
+ if (!stampMatch) {
1750
+ return { capturedAt: "", category: null };
1751
+ }
1752
+ return {
1753
+ capturedAt: typeof stampMatch[2] === "string" ? stampMatch[2] : "",
1754
+ category: parseLearnedCategory(stampMatch[1]) ?? null
1755
+ };
1756
+ }
1757
+ function parseLearnedCategory(value) {
1758
+ return value === "convention" || value === "pattern" || value === "warning" || value === "fact" ? value : void 0;
1759
+ }
1760
+ function stripStamp(entry) {
1761
+ return entry.replace(/<!--\s*learned-stamp:[\s\S]*?-->/g, "").replace(/^>\s*(?:\[[\w-]+\]\s+)?(?:Captured|Taught)\s+.+$/m, "").replace(/\n{3,}/g, "\n\n").trim();
1762
+ }
1763
+ function directiveKey(text) {
1764
+ return normalizeForComparison(text);
1765
+ }
1766
+ function decomposeLearnedEntry(text, category) {
1767
+ const what = text;
1768
+ const why = deriveWhy(category, text);
1769
+ const how = extractHow(text);
1770
+ return { what, why, how };
1771
+ }
1772
+ var WHY_BY_CATEGORY = {
1773
+ convention: "Established convention for this codebase \u2014 skipping it risks regressions, merge friction, or out-of-sync state with peers.",
1774
+ pattern: "This project's chosen approach \u2014 alternatives were considered and either conflict with existing architecture or were rejected for known reasons.",
1775
+ warning: "Known failure mode \u2014 skipping this has caused real defects in this codebase. The cost of getting it wrong outweighs the cost of the check.",
1776
+ fact: "Current state of the project \u2014 assumed by other conventions, build steps, or peers, so acting on a stale assumption wastes a cycle."
1777
+ };
1778
+ function deriveWhy(category, text) {
1779
+ const base = WHY_BY_CATEGORY[category];
1780
+ const signals = [];
1781
+ const lower = text.toLowerCase();
1782
+ if (/\bbefore\s+(?:merge|commit|deploy|release|shipping|publishing)\b/.test(lower))
1783
+ signals.push("guard before shipping");
1784
+ if (/\bto\s+avoid\b/.test(lower)) {
1785
+ const m = text.match(/to avoid ([^.!?]+)/i);
1786
+ if (m?.[1]) signals.push(`avoid ${m[1].trim()}`);
1787
+ }
1788
+ if (/\bso\s+(?:that|we|the project)\b/.test(lower)) {
1789
+ const m = text.match(/so (?:that |we |the project )?([^.!?]+)/i);
1790
+ if (m?.[1]) signals.push(`so ${m[1].trim()}`);
1791
+ }
1792
+ if (signals.length === 0) return base;
1793
+ return `${base} Project signals: ${signals.join("; ")}.`;
1794
+ }
1795
+ function extractHow(text) {
1796
+ const anchors = /* @__PURE__ */ new Set();
1797
+ const backticked = text.match(/`([^`]+)`/g) ?? [];
1798
+ for (const raw of backticked) {
1799
+ const inner = raw.replace(/`/g, "").trim();
1800
+ if (inner.length > 0 && inner.length <= 120) anchors.add(inner);
1801
+ }
1802
+ const pathMatches = text.match(
1803
+ /(?:[a-zA-Z0-9_.-]+\/)+[a-zA-Z0-9_.-]+\.(?:tsx|jsonc|json|jsx|yaml|mjs|cjs|yml|ts|js|md)\b/g
1804
+ ) ?? [];
1805
+ for (const p of pathMatches) anchors.add(p);
1806
+ const scoped = text.match(/@[a-z0-9][\w.-]*\/[a-z0-9][\w.-]*/gi) ?? [];
1807
+ for (const p of scoped) anchors.add(p);
1808
+ if (anchors.size === 0) return "";
1809
+ return [...anchors].map((anchor) => `\`${anchor}\``).join("\n");
1810
+ }
1811
+ function cleanHowLine(line) {
1812
+ let value = line.trim();
1813
+ let previous;
1814
+ do {
1815
+ previous = value;
1816
+ value = value.replace(/^[-*]\s+/, "").replace(/^\*How:\*\s*/i, "").trim();
1817
+ } while (value !== previous);
1818
+ return value;
1819
+ }
1820
+ function parseStampAttributes(body) {
1821
+ const attributes = {};
1822
+ for (const part of body.split(";")) {
1823
+ const eq = part.indexOf("=");
1824
+ if (eq === -1) continue;
1825
+ const key = part.slice(0, eq).trim();
1826
+ const value = part.slice(eq + 1).trim();
1827
+ if (key) attributes[key] = value;
1828
+ }
1829
+ return attributes;
1830
+ }
1831
+ function parseEntryBody(body) {
1832
+ const lines = [];
1833
+ for (const line of body.split("\n")) {
1834
+ if (/^##\s/.test(line) || /^---\s*$/.test(line) || /^<!--/.test(line)) break;
1835
+ lines.push(line);
1836
+ }
1837
+ const text = lines.join("\n");
1838
+ const what = /^-\s+\*\*([\s\S]+?)\*\*\s*$/m.exec(text)?.[1]?.replace(/\s+/g, " ").trim() ?? "";
1839
+ if (!what) return void 0;
1840
+ const why = /^\s+-\s+\*Why:\*\s+(.+)$/m.exec(text)?.[1]?.trim() ?? "";
1841
+ const how = lines.filter((line) => /^\s+-\s+\*How:\*/i.test(line)).map((line) => cleanHowLine(line)).filter(Boolean).join("\n");
1842
+ return { what, why, how };
1843
+ }
1844
+ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLearnedEntries(raw)) {
1845
+ if (!raw) return [];
1846
+ const structured = [];
1847
+ const stampPattern = /<!--\s*learned-stamp:\s*([^>]*?)\s*-->/g;
1848
+ const stamps = [...raw.matchAll(stampPattern)];
1849
+ for (const [index, stamp] of stamps.entries()) {
1850
+ const attributes = parseStampAttributes(stamp[1] ?? "");
1851
+ const category = parseLearnedCategory(attributes["category"]) ?? "fact";
1852
+ const capturedAt = attributes["capturedAt"] ?? "";
1853
+ const skill = attributes["skill"];
1854
+ const start = (stamp.index ?? 0) + stamp[0].length;
1855
+ const end = stamps[index + 1]?.index ?? raw.length;
1856
+ const parsed = parseEntryBody(raw.slice(start, end));
1857
+ if (!parsed || parsed.what.length < MIN_INSTRUCTIVE_LENGTH) continue;
1858
+ structured.push({
1859
+ key: directiveKey(parsed.what),
1860
+ category,
1861
+ what: parsed.what,
1862
+ why: parsed.why || WHY_BY_CATEGORY[category],
1863
+ how: parsed.how,
1864
+ capturedAt,
1865
+ ...skill ? { skill } : {}
1866
+ });
1867
+ }
1868
+ if (structured.length === 0) {
1869
+ for (const chunk of legacyEntries) {
1870
+ const stamp = parseLearnedEntryStamp(chunk);
1871
+ const directive = stamp.capturedAt || stamp.category ? stripStamp(chunk) : chunk;
1872
+ if (directive.length < MIN_INSTRUCTIVE_LENGTH) continue;
1873
+ const category = stamp.category ?? classifyLearnedEntry(directive);
1874
+ const { what, why, how } = decomposeLearnedEntry(directive, category);
1875
+ structured.push({
1876
+ key: directiveKey(directive),
1877
+ category,
1878
+ what,
1879
+ why,
1880
+ how,
1881
+ capturedAt: stamp.capturedAt
1882
+ });
1883
+ }
1884
+ }
1885
+ return structured;
1886
+ }
1887
+
1720
1888
  // src/coordination/agents/project-agent-consolidation.ts
1721
1889
  function consolidationPath(role, projectRoot) {
1722
1890
  return path3.join(roleDir(role, projectRoot), "consolidated.md");
@@ -1831,27 +1999,317 @@ function loadProjectAgentLearningPolicy(role, projectRoot) {
1831
1999
  }
1832
2000
  }
1833
2001
 
2002
+ // src/coordination/agents/project-agent-skill-layer.ts
2003
+ import { readdirSync, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
2004
+ import * as path4 from "node:path";
2005
+
2006
+ // src/coordination/agents/role-skills.ts
2007
+ var skillSet = (...names) => names;
2008
+ var ROLE_SKILL_SETS = {
2009
+ explore: skillSet("research-web", "node-modern", "typescript-strict"),
2010
+ search: skillSet("bug-hunter", "typescript-strict", "research-web"),
2011
+ research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
2012
+ analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
2013
+ planner: skillSet("sdd", "multi-agent", "refactor-planner", "testing"),
2014
+ architect: skillSet("sdd", "api-design", "refactor-planner", "security-scanner", "observability"),
2015
+ critic: skillSet("chimera", "bug-hunter", "security-scanner", "refactor-planner"),
2016
+ "refactor-planner": skillSet(
2017
+ "refactor-planner",
2018
+ "sdd",
2019
+ "typescript-strict",
2020
+ "testing",
2021
+ "bug-hunter"
2022
+ ),
2023
+ executor: skillSet("sdd", "typescript-strict", "node-modern", "testing", "git-flow"),
2024
+ refactor: skillSet("refactor-planner", "typescript-strict", "testing", "bug-hunter"),
2025
+ simplifier: skillSet("bug-hunter", "typescript-strict", "testing", "refactor-planner"),
2026
+ migration: skillSet("tech-stack", "node-modern", "typescript-strict", "testing", "git-flow"),
2027
+ vision: skillSet("react-modern", "testing", "typescript-strict", "security-scanner"),
2028
+ debugger: skillSet("bug-hunter", "testing", "typescript-strict", "observability", "audit-log"),
2029
+ tracer: skillSet("observability", "audit-log", "bug-hunter", "testing"),
2030
+ verifier: skillSet("testing", "typescript-strict", "security-scanner", "chimera"),
2031
+ test: skillSet("testing", "typescript-strict", "bug-hunter", "security-scanner"),
2032
+ e2e: skillSet("testing", "react-modern", "api-design", "security-scanner"),
2033
+ browser: skillSet("testing", "react-modern", "security-scanner", "research-web"),
2034
+ performance: skillSet("observability", "testing", "node-modern", "audit-log"),
2035
+ chaos: skillSet("testing", "observability", "security-scanner", "audit-log"),
2036
+ "security-scanner": skillSet("security-scanner", "bug-hunter", "api-design", "tech-stack"),
2037
+ "bug-hunter": skillSet("bug-hunter", "typescript-strict", "testing", "security-scanner"),
2038
+ "audit-log": skillSet("audit-log", "observability", "bug-hunter", "output-standards"),
2039
+ reviewer: skillSet("chimera", "bug-hunter", "security-scanner", "testing"),
2040
+ "code-reviewer": skillSet("chimera", "bug-hunter", "typescript-strict", "testing"),
2041
+ "security-reviewer": skillSet("security-scanner", "chimera", "api-design", "tech-stack"),
2042
+ accessibility: skillSet("react-modern", "testing", "chimera", "security-scanner"),
2043
+ compliance: skillSet("security-scanner", "audit-log", "tech-stack", "chimera"),
2044
+ database: skillSet("api-design", "testing", "security-scanner", "observability"),
2045
+ api: skillSet("api-design", "typescript-strict", "node-modern", "testing", "security-scanner"),
2046
+ auth: skillSet("security-scanner", "api-design", "testing", "typescript-strict", "audit-log"),
2047
+ data: skillSet("typescript-strict", "testing", "observability", "security-scanner"),
2048
+ frontend: skillSet("react-modern", "typescript-strict", "testing", "security-scanner"),
2049
+ backend: skillSet("node-modern", "typescript-strict", "api-design", "testing", "observability"),
2050
+ designer: skillSet("react-modern", "prompt-engineering", "research-web", "output-standards"),
2051
+ ios: skillSet("sdd", "testing", "security-scanner", "research-web"),
2052
+ document: skillSet("output-standards", "research-web", "api-design", "prompt-engineering"),
2053
+ uml: skillSet("sdd", "refactor-planner", "api-design", "output-standards"),
2054
+ i18n: skillSet("react-modern", "testing", "typescript-strict", "output-standards"),
2055
+ prompt: skillSet("prompt-engineering", "skill-creator", "output-standards", "testing"),
2056
+ git: skillSet("git-flow", "chimera", "testing", "security-scanner"),
2057
+ release: skillSet("git-flow", "tech-stack", "chimera", "security-scanner"),
2058
+ devops: skillSet("docker-deploy", "observability", "security-scanner", "git-flow", "tech-stack"),
2059
+ observability: skillSet("observability", "node-modern", "testing", "audit-log"),
2060
+ dependency: skillSet("tech-stack", "security-scanner", "node-modern", "git-flow"),
2061
+ "skill-manage": skillSet(
2062
+ "skill-creator",
2063
+ "prompt-engineering",
2064
+ "security-scanner",
2065
+ "output-standards"
2066
+ ),
2067
+ "self-improving": skillSet(
2068
+ "audit-log",
2069
+ "mnemosyne",
2070
+ "prompt-engineering",
2071
+ "bug-hunter",
2072
+ "observability"
2073
+ ),
2074
+ context: skillSet("mnemosyne", "output-standards", "prompt-engineering", "audit-log"),
2075
+ cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
2076
+ "tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
2077
+ // ── Wave 1: Platform and systems engineering ──────────────────────────
2078
+ android: skillSet(
2079
+ "react-modern",
2080
+ "typescript-strict",
2081
+ "testing",
2082
+ "security-scanner",
2083
+ "tech-stack"
2084
+ ),
2085
+ desktop: skillSet(
2086
+ "node-modern",
2087
+ "typescript-strict",
2088
+ "testing",
2089
+ "security-scanner",
2090
+ "tech-stack"
2091
+ ),
2092
+ realtime: skillSet(
2093
+ "api-design",
2094
+ "observability",
2095
+ "node-modern",
2096
+ "typescript-strict",
2097
+ "testing",
2098
+ "tech-stack"
2099
+ ),
2100
+ "distributed-systems": skillSet(
2101
+ "api-design",
2102
+ "observability",
2103
+ "typescript-strict",
2104
+ "testing",
2105
+ "security-scanner",
2106
+ "tech-stack"
2107
+ ),
2108
+ concurrency: skillSet(
2109
+ "bug-hunter",
2110
+ "typescript-strict",
2111
+ "testing",
2112
+ "observability",
2113
+ "node-modern",
2114
+ "tech-stack"
2115
+ ),
2116
+ "platform-engineer": skillSet(
2117
+ "node-modern",
2118
+ "typescript-strict",
2119
+ "git-flow",
2120
+ "docker-deploy",
2121
+ "tech-stack"
2122
+ ),
2123
+ // ── Wave 3: Product and data specialists ──────────────────────────────
2124
+ payments: skillSet(
2125
+ "api-design",
2126
+ "security-scanner",
2127
+ "audit-log",
2128
+ "testing",
2129
+ "typescript-strict",
2130
+ "tech-stack"
2131
+ ),
2132
+ messaging: skillSet(
2133
+ "api-design",
2134
+ "observability",
2135
+ "node-modern",
2136
+ "testing",
2137
+ "security-scanner",
2138
+ "tech-stack"
2139
+ ),
2140
+ "search-relevance": skillSet(
2141
+ "api-design",
2142
+ "observability",
2143
+ "testing",
2144
+ "typescript-strict",
2145
+ "data-governance",
2146
+ "tech-stack"
2147
+ ),
2148
+ storage: skillSet(
2149
+ "api-design",
2150
+ "security-scanner",
2151
+ "testing",
2152
+ "observability",
2153
+ "node-modern",
2154
+ "tech-stack"
2155
+ ),
2156
+ "ml-engineer": skillSet(
2157
+ "observability",
2158
+ "typescript-strict",
2159
+ "testing",
2160
+ "security-scanner",
2161
+ "data-governance",
2162
+ "tech-stack"
2163
+ ),
2164
+ "data-governance": skillSet(
2165
+ "audit-log",
2166
+ "security-scanner",
2167
+ "observability",
2168
+ "testing",
2169
+ "output-standards",
2170
+ "tech-stack"
2171
+ ),
2172
+ // ── Wave 4: Agent platform and quality ───────────────────────────────
2173
+ "plugin-author": skillSet(
2174
+ "plugin-author",
2175
+ "node-modern",
2176
+ "typescript-strict",
2177
+ "testing",
2178
+ "security-scanner",
2179
+ "tech-stack"
2180
+ ),
2181
+ "tool-author": skillSet(
2182
+ "typescript-strict",
2183
+ "security-scanner",
2184
+ "testing",
2185
+ "output-standards",
2186
+ "tech-stack"
2187
+ ),
2188
+ "prompt-evaluator": skillSet(
2189
+ "prompt-engineering",
2190
+ "testing",
2191
+ "output-standards",
2192
+ "security-scanner",
2193
+ "tech-stack"
2194
+ ),
2195
+ "benchmark-engineer": skillSet("observability", "testing", "output-standards", "tech-stack"),
2196
+ "memory-curator": skillSet("mnemosyne", "audit-log", "testing", "output-standards", "tech-stack"),
2197
+ "fleet-coordinator": skillSet("multi-agent", "sdd", "output-standards", "testing", "tech-stack"),
2198
+ // ── Wave 2: Security, resilience, governance and agent platform ────────
2199
+ "threat-modeler": skillSet(
2200
+ "security-scanner",
2201
+ "api-design",
2202
+ "tech-stack",
2203
+ "audit-log",
2204
+ "testing"
2205
+ ),
2206
+ "secure-coding-coach": skillSet(
2207
+ "security-scanner",
2208
+ "testing",
2209
+ "typescript-strict",
2210
+ "api-design",
2211
+ "tech-stack"
2212
+ ),
2213
+ "resilience-engineer": skillSet(
2214
+ "observability",
2215
+ "testing",
2216
+ "typescript-strict",
2217
+ "api-design",
2218
+ "tech-stack"
2219
+ ),
2220
+ "chaos-engineer": skillSet(
2221
+ "observability",
2222
+ "testing",
2223
+ "security-scanner",
2224
+ "git-flow",
2225
+ "tech-stack"
2226
+ ),
2227
+ "compliance-auditor": skillSet(
2228
+ "audit-log",
2229
+ "security-scanner",
2230
+ "output-standards",
2231
+ "testing",
2232
+ "tech-stack"
2233
+ ),
2234
+ "privacy-engineer": skillSet(
2235
+ "security-scanner",
2236
+ "audit-log",
2237
+ "api-design",
2238
+ "data-governance",
2239
+ "tech-stack"
2240
+ )
2241
+ };
2242
+ var SHADOW_AGENT_SKILLS = skillSet(
2243
+ "wrongstack-mailbox",
2244
+ "multi-agent",
2245
+ "audit-log",
2246
+ "observability",
2247
+ "security-scanner"
2248
+ );
2249
+ var MAX_EAGER_ROSTER_SKILLS = 3;
2250
+ function assignSkillsToAgents(definitions) {
2251
+ return definitions.map((definition) => {
2252
+ const role = definition.config.role;
2253
+ if (!role || !Object.hasOwn(ROLE_SKILL_SETS, role)) {
2254
+ throw new Error(
2255
+ `Missing curated skill set for roster role: "${role ?? definition.config.name}"`
2256
+ );
2257
+ }
2258
+ const skills = ROLE_SKILL_SETS[role];
2259
+ return {
2260
+ ...definition,
2261
+ config: {
2262
+ ...definition.config,
2263
+ capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
2264
+ skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS),
2265
+ skillPool: [...skills]
2266
+ }
2267
+ };
2268
+ });
2269
+ }
2270
+
2271
+ // src/coordination/agents/project-agent-skill-layer.ts
2272
+ var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
2273
+ function isProjectSkillName(name) {
2274
+ return SKILL_NAME_PATTERN.test(name);
2275
+ }
2276
+ function projectSkillsDir(role, projectRoot) {
2277
+ return path4.join(roleDir(role, projectRoot), "skills");
2278
+ }
2279
+ function listProjectSkillAugmentations(role, projectRoot) {
2280
+ try {
2281
+ return readdirSync(projectSkillsDir(role, projectRoot)).filter((name) => name.endsWith(".md")).map((name) => name.slice(0, -3)).filter(isProjectSkillName).sort();
2282
+ } catch {
2283
+ return [];
2284
+ }
2285
+ }
2286
+
2287
+ // src/types/blocks.ts
2288
+ function isTextBlock(b) {
2289
+ return b.type === "text";
2290
+ }
2291
+
1834
2292
  // src/coordination/agents/project-agent-identity.ts
1835
2293
  function loadProjectAgentIdentity(role, projectRoot) {
1836
- const identityPath = path4.join(roleDir(role, projectRoot), "identity.md");
2294
+ const identityPath = path5.join(roleDir(role, projectRoot), "identity.md");
1837
2295
  try {
1838
- return readFileSync4(identityPath, "utf8").trim();
2296
+ return readFileSync5(identityPath, "utf8").trim();
1839
2297
  } catch {
1840
2298
  return "";
1841
2299
  }
1842
2300
  }
1843
2301
  function loadProjectAgentLearned(role, projectRoot) {
1844
- const learnedPath = path4.join(roleDir(role, projectRoot), "learned.md");
2302
+ const learnedPath = path5.join(roleDir(role, projectRoot), "learned.md");
1845
2303
  try {
1846
- return readFileSync4(learnedPath, "utf8").trim();
2304
+ return readFileSync5(learnedPath, "utf8").trim();
1847
2305
  } catch {
1848
2306
  return "";
1849
2307
  }
1850
2308
  }
1851
2309
  function loadRoleKnowledgeManifest(role, projectRoot) {
1852
- const projectPath = path4.join(roleDir(role, projectRoot), "knowledge.json");
2310
+ const projectPath = path5.join(roleDir(role, projectRoot), "knowledge.json");
1853
2311
  try {
1854
- const raw = readFileSync4(projectPath, "utf8");
2312
+ const raw = readFileSync5(projectPath, "utf8");
1855
2313
  return JSON.parse(raw);
1856
2314
  } catch {
1857
2315
  return BUILT_IN_KNOWLEDGE_MANIFESTS[role];
@@ -1879,31 +2337,35 @@ ${identity}`);
1879
2337
  let learnedLabel = "";
1880
2338
  if (learningPolicy.enabled) {
1881
2339
  const consolidated = loadProjectAgentConsolidated(role, projectRoot);
2340
+ const rawLearned = loadProjectAgentLearned(role, projectRoot);
1882
2341
  if (consolidated) {
1883
2342
  const meta = loadConsolidationMetadata(role, projectRoot);
1884
- const rawLearned = loadProjectAgentLearned(role, projectRoot);
1885
- const rawEntries = splitLearnedEntries(rawLearned);
2343
+ const rawEntries = parseStructuredLearnedEntriesFromContent(
2344
+ rawLearned,
2345
+ splitLearnedEntries(rawLearned)
2346
+ );
1886
2347
  const rawBytes = Buffer.byteLength(rawLearned, "utf8");
1887
- const stale = meta === void 0 || rawEntries.length > meta.sourceEntryCount || rawBytes > meta.sourceBytes;
1888
- if (stale) {
1889
- if (meta !== void 0 && meta.sourceEntryCount < rawEntries.length) {
1890
- const deltaEntries = rawEntries.slice(meta.sourceEntryCount);
1891
- learnedContent = `${consolidated}
2348
+ const freshEntries = meta === void 0 ? [] : rawEntries.filter((entry) => entry.capturedAt > meta.consolidatedAt);
2349
+ const stale = meta === void 0 || freshEntries.length > 0 || rawEntries.length > meta.sourceEntryCount || rawBytes > meta.sourceBytes;
2350
+ if (!stale) {
2351
+ learnedContent = consolidated;
2352
+ learnedLabel = "Consolidated knowledge for this project";
2353
+ } else if (freshEntries.length > 0) {
2354
+ const delta = freshEntries.map((entry) => `- **${entry.what}**`).join("\n");
2355
+ learnedContent = `${consolidated}
1892
2356
 
1893
2357
  ---
1894
2358
 
1895
2359
  ## Recently captured (pending next optimization)
1896
2360
 
1897
- ${deltaEntries.join("\n\n---\n\n")}`;
1898
- } else {
1899
- learnedContent = rawLearned;
1900
- }
2361
+ ${delta}`;
2362
+ learnedLabel = "Consolidated knowledge for this project";
1901
2363
  } else {
1902
- learnedContent = consolidated;
2364
+ learnedContent = rawLearned;
2365
+ learnedLabel = "Learned instructions for this project (structured: what / why / how)";
1903
2366
  }
1904
- learnedLabel = "Consolidated knowledge for this project";
1905
2367
  } else {
1906
- learnedContent = loadProjectAgentLearned(role, projectRoot);
2368
+ learnedContent = rawLearned;
1907
2369
  learnedLabel = "Learned instructions for this project (structured: what / why / how)";
1908
2370
  }
1909
2371
  }
@@ -1918,7 +2380,7 @@ ${learnedContent}`);
1918
2380
  }
1919
2381
  }
1920
2382
  const effectiveProjectRoot = projectRoot || process.cwd();
1921
- const learnedFilePath = path4.join(
2383
+ const learnedFilePath = path5.join(
1922
2384
  effectiveProjectRoot,
1923
2385
  ".wrongstack",
1924
2386
  "agents",
@@ -1945,6 +2407,8 @@ The file below stores **learning data for this project's "${role}" agent** \u201
1945
2407
  - **Self-contained** \u2014 understandable without the surrounding session context.
1946
2408
  - **Front-load concrete anchors** \u2014 commands in backticks, package names like \`@wrongstack/core\`, file paths like \`packages/core/src/.../foo.ts\`. The structured-list renderer extracts these as the "how" for the entry.
1947
2409
 
2410
+ **Tag the skill you are developing.** When a directive refines one of your skills, mark it: \`## LEARNED [skill: testing]\`. Tagged directives are distilled into that skill's project addendum, so the lesson arrives as part of the skill itself on every future run instead of as a loose fact. Untagged directives are routed automatically when the wording makes the target obvious, and stay role-level otherwise.
2411
+
1948
2412
  **Bad** (session log \u2014 rejected at capture time):
1949
2413
 
1950
2414
  \`\`\`
@@ -1963,15 +2427,40 @@ Capture only durable, cross-session knowledge \u2014 never ephemeral task detail
1963
2427
  );
1964
2428
  }
1965
2429
  const knowledge = loadRoleKnowledgeManifest(role, projectRoot);
1966
- if (knowledge && knowledge.checklist.length > 0) {
1967
- const checklist = knowledge.checklist.map((item) => `- ${item}`).join("\n");
2430
+ if (knowledge) {
2431
+ const knowledgeSections = [];
2432
+ if (knowledge.checklist.length > 0) {
2433
+ knowledgeSections.push(
2434
+ `Verify these before answering:
2435
+ ${knowledge.checklist.map((item) => `- ${item}`).join("\n")}`
2436
+ );
2437
+ }
2438
+ const liveQueries = Object.entries(knowledge.liveQueries ?? {});
2439
+ if (liveQueries.length > 0) {
2440
+ const threshold = Number.isFinite(knowledge.verifyThreshold) ? knowledge.verifyThreshold : 0.5;
2441
+ knowledgeSections.push(
2442
+ `When your confidence in any of the following is below ${threshold}, fetch the live value rather than recalling it:
2443
+ ` + liveQueries.map(
2444
+ ([topic, query]) => `- **${topic}** \u2014 ${query.description}: \`${query.registry}\` \u2192 field \`${query.key}\``
2445
+ ).join("\n")
2446
+ );
2447
+ }
2448
+ if (knowledgeSections.length > 0) {
2449
+ parts.push(`
2450
+
2451
+ ## Current knowledge requirements
2452
+
2453
+ ${knowledgeSections.join("\n\n")}`);
2454
+ }
2455
+ }
2456
+ const developedSkills = listProjectSkillAugmentations(role, projectRoot);
2457
+ if (developedSkills.length > 0) {
1968
2458
  parts.push(
1969
2459
  `
1970
2460
 
1971
- ## Current knowledge requirements
2461
+ ## Project-developed skills
1972
2462
 
1973
- Verify these before answering:
1974
- ${checklist}`
2463
+ These skills have been extended with practice learned in this project: ${developedSkills.map((skill) => `\`${skill}\``).join(", ")}. Their project addendum is attached to the skill body \u2014 follow it over the generic method when the two differ.`
1975
2464
  );
1976
2465
  }
1977
2466
  const additions = parts.slice(1).join("\n\n").trim();
@@ -2060,38 +2549,38 @@ function loadTechVersionPolicy() {
2060
2549
  return techVersionPolicyBody;
2061
2550
  }
2062
2551
  function policyCandidateDirs() {
2063
- const here = path5.dirname(fileURLToPath(import.meta.url));
2552
+ const here = path6.dirname(fileURLToPath(import.meta.url));
2064
2553
  const profileInstructions = resolveWstackPaths({ projectRoot: process.cwd() }).globalInstructions;
2065
2554
  return [
2066
- path5.resolve(here, "../../../../instructions/agents"),
2067
- path5.resolve(here, "../../../instructions/agents"),
2068
- path5.resolve(here, "../../instructions/agents"),
2069
- path5.resolve(here, "../instructions/agents"),
2070
- path5.resolve(here, "instructions/agents"),
2071
- path5.join(profileInstructions, "agents"),
2555
+ path6.resolve(here, "../../../../instructions/agents"),
2556
+ path6.resolve(here, "../../../instructions/agents"),
2557
+ path6.resolve(here, "../../instructions/agents"),
2558
+ path6.resolve(here, "../instructions/agents"),
2559
+ path6.resolve(here, "instructions/agents"),
2560
+ path6.join(profileInstructions, "agents"),
2072
2561
  profileInstructions
2073
2562
  ].filter((dir, index, all) => all.indexOf(dir) === index);
2074
2563
  }
2075
2564
  function policyBody() {
2076
- const here = path5.dirname(fileURLToPath(import.meta.url));
2565
+ const here = path6.dirname(fileURLToPath(import.meta.url));
2077
2566
  const roots = [
2078
- path5.resolve(here, "_policy/tech-version.md"),
2079
- path5.resolve(here, "../_policy/tech-version.md"),
2080
- path5.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
2081
- path5.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
2082
- path5.resolve(here, "../instructions/agents/_policy/tech-version.md"),
2083
- path5.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
2084
- path5.resolve(here, "instructions/agents/_policy/tech-version.md")
2567
+ path6.resolve(here, "_policy/tech-version.md"),
2568
+ path6.resolve(here, "../_policy/tech-version.md"),
2569
+ path6.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
2570
+ path6.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
2571
+ path6.resolve(here, "../instructions/agents/_policy/tech-version.md"),
2572
+ path6.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
2573
+ path6.resolve(here, "instructions/agents/_policy/tech-version.md")
2085
2574
  ];
2086
2575
  for (const file of roots) {
2087
2576
  try {
2088
- return readFileSync5(file, "utf8").trim();
2577
+ return readFileSync6(file, "utf8").trim();
2089
2578
  } catch {
2090
2579
  }
2091
2580
  }
2092
2581
  for (const dir of policyCandidateDirs()) {
2093
2582
  try {
2094
- return readFileSync5(path5.join(dir, "_policy", "tech-version.md"), "utf8").trim();
2583
+ return readFileSync6(path6.join(dir, "_policy", "tech-version.md"), "utf8").trim();
2095
2584
  } catch {
2096
2585
  }
2097
2586
  }
@@ -2102,14 +2591,15 @@ function agentPrompt(id) {
2102
2591
  const envDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"] ?? "";
2103
2592
  const policyOn = process.env["WRONGSTACK_AGENT_POLICY"] === "on";
2104
2593
  const cacheBypass = globalThis.__WS_DISABLE_PROMPT_CACHE__ === true;
2105
- const cacheKey = `${envDir}\0${policyOn ? 1 : 0}\0${id}`;
2594
+ const promptProjectRoot = process.env["WRONGSTACK_PROJECT_ROOT"] || process.cwd();
2595
+ const cacheKey = `${envDir}\0${policyOn ? 1 : 0}\0${promptProjectRoot}\0${id}`;
2106
2596
  const cached = cacheBypass ? void 0 : promptCache.get(cacheKey);
2107
2597
  if (cached !== void 0) return cached;
2108
2598
  const fileName = `${id}.md`;
2109
2599
  let resolved = "";
2110
2600
  for (const dir of agentPromptDirCandidates(envDir)) {
2111
2601
  try {
2112
- resolved = readFileSync5(path5.join(dir, fileName), "utf8").trim();
2602
+ resolved = readFileSync6(path6.join(dir, fileName), "utf8").trim();
2113
2603
  break;
2114
2604
  } catch {
2115
2605
  }
@@ -2126,8 +2616,7 @@ function agentPrompt(id) {
2126
2616
  ${policy}`;
2127
2617
  }
2128
2618
  }
2129
- const projectRoot = process.env["WRONGSTACK_PROJECT_ROOT"] || process.cwd();
2130
- resolved = buildProjectContextualizedPrompt(resolved, id, projectRoot);
2619
+ resolved = buildProjectContextualizedPrompt(resolved, id, promptProjectRoot);
2131
2620
  if (!cacheBypass) promptCache.set(cacheKey, resolved);
2132
2621
  return resolved;
2133
2622
  }
@@ -2136,16 +2625,16 @@ function agentPromptDirCandidates(envDir) {
2136
2625
  const candKey = `${envDir}\0${profileInstructions}`;
2137
2626
  const cached = candidateCache.get(candKey);
2138
2627
  if (cached !== void 0) return cached;
2139
- const here = path5.dirname(fileURLToPath(import.meta.url));
2628
+ const here = path6.dirname(fileURLToPath(import.meta.url));
2140
2629
  const explicitDir = envDir || void 0;
2141
2630
  const candidates = [
2142
- ...explicitDir ? [path5.resolve(explicitDir)] : [],
2143
- path5.join(profileInstructions, "agents"),
2144
- path5.resolve(here, "../../../../instructions/agents"),
2145
- path5.resolve(here, "../../../instructions/agents"),
2146
- path5.resolve(here, "../../instructions/agents"),
2147
- path5.resolve(here, "../instructions/agents"),
2148
- path5.resolve(here, "instructions/agents")
2631
+ ...explicitDir ? [path6.resolve(explicitDir)] : [],
2632
+ path6.join(profileInstructions, "agents"),
2633
+ path6.resolve(here, "../../../../instructions/agents"),
2634
+ path6.resolve(here, "../../../instructions/agents"),
2635
+ path6.resolve(here, "../../instructions/agents"),
2636
+ path6.resolve(here, "../instructions/agents"),
2637
+ path6.resolve(here, "instructions/agents")
2149
2638
  ];
2150
2639
  const ordered = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
2151
2640
  candidateCache.set(candKey, ordered);
@@ -4641,270 +5130,6 @@ var WAVE4_ROLE_METAS = {
4641
5130
  };
4642
5131
  var WAVE4_ROLES = new Set(Object.keys(WAVE4_ROLE_METAS));
4643
5132
 
4644
- // src/coordination/agents/role-skills.ts
4645
- var skillSet = (...names) => names;
4646
- var ROLE_SKILL_SETS = {
4647
- explore: skillSet("research-web", "node-modern", "typescript-strict"),
4648
- search: skillSet("bug-hunter", "typescript-strict", "research-web"),
4649
- research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
4650
- analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
4651
- planner: skillSet("sdd", "multi-agent", "refactor-planner", "testing"),
4652
- architect: skillSet("sdd", "api-design", "refactor-planner", "security-scanner", "observability"),
4653
- critic: skillSet("chimera", "bug-hunter", "security-scanner", "refactor-planner"),
4654
- "refactor-planner": skillSet(
4655
- "refactor-planner",
4656
- "sdd",
4657
- "typescript-strict",
4658
- "testing",
4659
- "bug-hunter"
4660
- ),
4661
- executor: skillSet("sdd", "typescript-strict", "node-modern", "testing", "git-flow"),
4662
- refactor: skillSet("refactor-planner", "typescript-strict", "testing", "bug-hunter"),
4663
- simplifier: skillSet("bug-hunter", "typescript-strict", "testing", "refactor-planner"),
4664
- migration: skillSet("tech-stack", "node-modern", "typescript-strict", "testing", "git-flow"),
4665
- vision: skillSet("react-modern", "testing", "typescript-strict", "security-scanner"),
4666
- debugger: skillSet("bug-hunter", "testing", "typescript-strict", "observability", "audit-log"),
4667
- tracer: skillSet("observability", "audit-log", "bug-hunter", "testing"),
4668
- verifier: skillSet("testing", "typescript-strict", "security-scanner", "chimera"),
4669
- test: skillSet("testing", "typescript-strict", "bug-hunter", "security-scanner"),
4670
- e2e: skillSet("testing", "react-modern", "api-design", "security-scanner"),
4671
- browser: skillSet("testing", "react-modern", "security-scanner", "research-web"),
4672
- performance: skillSet("observability", "testing", "node-modern", "audit-log"),
4673
- chaos: skillSet("testing", "observability", "security-scanner", "audit-log"),
4674
- "security-scanner": skillSet("security-scanner", "bug-hunter", "api-design", "tech-stack"),
4675
- "bug-hunter": skillSet("bug-hunter", "typescript-strict", "testing", "security-scanner"),
4676
- "audit-log": skillSet("audit-log", "observability", "bug-hunter", "output-standards"),
4677
- reviewer: skillSet("chimera", "bug-hunter", "security-scanner", "testing"),
4678
- "code-reviewer": skillSet("chimera", "bug-hunter", "typescript-strict", "testing"),
4679
- "security-reviewer": skillSet("security-scanner", "chimera", "api-design", "tech-stack"),
4680
- accessibility: skillSet("react-modern", "testing", "chimera", "security-scanner"),
4681
- compliance: skillSet("security-scanner", "audit-log", "tech-stack", "chimera"),
4682
- database: skillSet("api-design", "testing", "security-scanner", "observability"),
4683
- api: skillSet("api-design", "typescript-strict", "node-modern", "testing", "security-scanner"),
4684
- auth: skillSet("security-scanner", "api-design", "testing", "typescript-strict", "audit-log"),
4685
- data: skillSet("typescript-strict", "testing", "observability", "security-scanner"),
4686
- frontend: skillSet("react-modern", "typescript-strict", "testing", "security-scanner"),
4687
- backend: skillSet("node-modern", "typescript-strict", "api-design", "testing", "observability"),
4688
- designer: skillSet("react-modern", "prompt-engineering", "research-web", "output-standards"),
4689
- ios: skillSet("sdd", "testing", "security-scanner", "research-web"),
4690
- document: skillSet("output-standards", "research-web", "api-design", "prompt-engineering"),
4691
- uml: skillSet("sdd", "refactor-planner", "api-design", "output-standards"),
4692
- i18n: skillSet("react-modern", "testing", "typescript-strict", "output-standards"),
4693
- prompt: skillSet("prompt-engineering", "skill-creator", "output-standards", "testing"),
4694
- git: skillSet("git-flow", "chimera", "testing", "security-scanner"),
4695
- release: skillSet("git-flow", "tech-stack", "chimera", "security-scanner"),
4696
- devops: skillSet("docker-deploy", "observability", "security-scanner", "git-flow", "tech-stack"),
4697
- observability: skillSet("observability", "node-modern", "testing", "audit-log"),
4698
- dependency: skillSet("tech-stack", "security-scanner", "node-modern", "git-flow"),
4699
- "skill-manage": skillSet(
4700
- "skill-creator",
4701
- "prompt-engineering",
4702
- "security-scanner",
4703
- "output-standards"
4704
- ),
4705
- "self-improving": skillSet(
4706
- "audit-log",
4707
- "mnemosyne",
4708
- "prompt-engineering",
4709
- "bug-hunter",
4710
- "observability"
4711
- ),
4712
- context: skillSet("mnemosyne", "output-standards", "prompt-engineering", "audit-log"),
4713
- cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
4714
- "tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
4715
- // ── Wave 1: Platform and systems engineering ──────────────────────────
4716
- android: skillSet(
4717
- "react-modern",
4718
- "typescript-strict",
4719
- "testing",
4720
- "security-scanner",
4721
- "tech-stack"
4722
- ),
4723
- desktop: skillSet(
4724
- "node-modern",
4725
- "typescript-strict",
4726
- "testing",
4727
- "security-scanner",
4728
- "tech-stack"
4729
- ),
4730
- realtime: skillSet(
4731
- "api-design",
4732
- "observability",
4733
- "node-modern",
4734
- "typescript-strict",
4735
- "testing",
4736
- "tech-stack"
4737
- ),
4738
- "distributed-systems": skillSet(
4739
- "api-design",
4740
- "observability",
4741
- "typescript-strict",
4742
- "testing",
4743
- "security-scanner",
4744
- "tech-stack"
4745
- ),
4746
- concurrency: skillSet(
4747
- "bug-hunter",
4748
- "typescript-strict",
4749
- "testing",
4750
- "observability",
4751
- "node-modern",
4752
- "tech-stack"
4753
- ),
4754
- "platform-engineer": skillSet(
4755
- "node-modern",
4756
- "typescript-strict",
4757
- "git-flow",
4758
- "docker-deploy",
4759
- "tech-stack"
4760
- ),
4761
- // ── Wave 3: Product and data specialists ──────────────────────────────
4762
- payments: skillSet(
4763
- "api-design",
4764
- "security-scanner",
4765
- "audit-log",
4766
- "testing",
4767
- "typescript-strict",
4768
- "tech-stack"
4769
- ),
4770
- messaging: skillSet(
4771
- "api-design",
4772
- "observability",
4773
- "node-modern",
4774
- "testing",
4775
- "security-scanner",
4776
- "tech-stack"
4777
- ),
4778
- "search-relevance": skillSet(
4779
- "api-design",
4780
- "observability",
4781
- "testing",
4782
- "typescript-strict",
4783
- "data-governance",
4784
- "tech-stack"
4785
- ),
4786
- storage: skillSet(
4787
- "api-design",
4788
- "security-scanner",
4789
- "testing",
4790
- "observability",
4791
- "node-modern",
4792
- "tech-stack"
4793
- ),
4794
- "ml-engineer": skillSet(
4795
- "observability",
4796
- "typescript-strict",
4797
- "testing",
4798
- "security-scanner",
4799
- "data-governance",
4800
- "tech-stack"
4801
- ),
4802
- "data-governance": skillSet(
4803
- "audit-log",
4804
- "security-scanner",
4805
- "observability",
4806
- "testing",
4807
- "output-standards",
4808
- "tech-stack"
4809
- ),
4810
- // ── Wave 4: Agent platform and quality ───────────────────────────────
4811
- "plugin-author": skillSet(
4812
- "plugin-author",
4813
- "node-modern",
4814
- "typescript-strict",
4815
- "testing",
4816
- "security-scanner",
4817
- "tech-stack"
4818
- ),
4819
- "tool-author": skillSet(
4820
- "typescript-strict",
4821
- "security-scanner",
4822
- "testing",
4823
- "output-standards",
4824
- "tech-stack"
4825
- ),
4826
- "prompt-evaluator": skillSet(
4827
- "prompt-engineering",
4828
- "testing",
4829
- "output-standards",
4830
- "security-scanner",
4831
- "tech-stack"
4832
- ),
4833
- "benchmark-engineer": skillSet("observability", "testing", "output-standards", "tech-stack"),
4834
- "memory-curator": skillSet("mnemosyne", "audit-log", "testing", "output-standards", "tech-stack"),
4835
- "fleet-coordinator": skillSet("multi-agent", "sdd", "output-standards", "testing", "tech-stack"),
4836
- // ── Wave 2: Security, resilience, governance and agent platform ────────
4837
- "threat-modeler": skillSet(
4838
- "security-scanner",
4839
- "api-design",
4840
- "tech-stack",
4841
- "audit-log",
4842
- "testing"
4843
- ),
4844
- "secure-coding-coach": skillSet(
4845
- "security-scanner",
4846
- "testing",
4847
- "typescript-strict",
4848
- "api-design",
4849
- "tech-stack"
4850
- ),
4851
- "resilience-engineer": skillSet(
4852
- "observability",
4853
- "testing",
4854
- "typescript-strict",
4855
- "api-design",
4856
- "tech-stack"
4857
- ),
4858
- "chaos-engineer": skillSet(
4859
- "observability",
4860
- "testing",
4861
- "security-scanner",
4862
- "git-flow",
4863
- "tech-stack"
4864
- ),
4865
- "compliance-auditor": skillSet(
4866
- "audit-log",
4867
- "security-scanner",
4868
- "output-standards",
4869
- "testing",
4870
- "tech-stack"
4871
- ),
4872
- "privacy-engineer": skillSet(
4873
- "security-scanner",
4874
- "audit-log",
4875
- "api-design",
4876
- "data-governance",
4877
- "tech-stack"
4878
- )
4879
- };
4880
- var SHADOW_AGENT_SKILLS = skillSet(
4881
- "wrongstack-mailbox",
4882
- "multi-agent",
4883
- "audit-log",
4884
- "observability",
4885
- "security-scanner"
4886
- );
4887
- var MAX_EAGER_ROSTER_SKILLS = 3;
4888
- function assignSkillsToAgents(definitions) {
4889
- return definitions.map((definition) => {
4890
- const role = definition.config.role;
4891
- if (!role || !Object.hasOwn(ROLE_SKILL_SETS, role)) {
4892
- throw new Error(
4893
- `Missing curated skill set for roster role: "${role ?? definition.config.name}"`
4894
- );
4895
- }
4896
- const skills = ROLE_SKILL_SETS[role];
4897
- return {
4898
- ...definition,
4899
- config: {
4900
- ...definition.config,
4901
- capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
4902
- skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS)
4903
- }
4904
- };
4905
- });
4906
- }
4907
-
4908
5133
  // src/coordination/agents/index.ts
4909
5134
  var ALL_AGENT_DEFINITIONS = assignSkillsToAgents([
4910
5135
  ...DISCOVERY_AGENTS,
@@ -5020,7 +5245,7 @@ function attachAutoExtend(events, policy = {}) {
5020
5245
  // src/coordination/delegate-tool.ts
5021
5246
  import { randomUUID as randomUUID2 } from "node:crypto";
5022
5247
  import * as fsp from "node:fs/promises";
5023
- import * as path6 from "node:path";
5248
+ import * as path7 from "node:path";
5024
5249
 
5025
5250
  // src/utils/safe-json.ts
5026
5251
  function safeParse(input, maxBytes = 5e6) {
@@ -5510,7 +5735,7 @@ function createDelegateTool(opts) {
5510
5735
  };
5511
5736
  }
5512
5737
  async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abortSignal) {
5513
- return new Promise((resolve19) => {
5738
+ return new Promise((resolve21) => {
5514
5739
  let settled = false;
5515
5740
  let timer;
5516
5741
  let offAbort = () => {
@@ -5523,7 +5748,7 @@ async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abo
5523
5748
  offIter();
5524
5749
  offProgress();
5525
5750
  offAbort();
5526
- resolve19(value);
5751
+ resolve21(value);
5527
5752
  };
5528
5753
  const arm = () => {
5529
5754
  if (timer) clearTimeout(timer);
@@ -5683,13 +5908,13 @@ async function readSubagentPartial(opts, subagentId) {
5683
5908
  if (!opts.sessionsRoot) return void 0;
5684
5909
  const candidates = [];
5685
5910
  if (opts.directorRunId) {
5686
- candidates.push(path6.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
5911
+ candidates.push(path7.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
5687
5912
  } else {
5688
5913
  try {
5689
5914
  const entries = await fsp.readdir(opts.sessionsRoot, { withFileTypes: true });
5690
5915
  for (const entry of entries) {
5691
5916
  if (entry.isDirectory()) {
5692
- candidates.push(path6.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
5917
+ candidates.push(path7.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
5693
5918
  }
5694
5919
  }
5695
5920
  } catch {
@@ -5734,7 +5959,7 @@ async function readSubagentPartial(opts, subagentId) {
5734
5959
  }
5735
5960
 
5736
5961
  // src/coordination/director.ts
5737
- import { randomUUID as randomUUID11 } from "node:crypto";
5962
+ import { randomUUID as randomUUID12 } from "node:crypto";
5738
5963
  import * as fsp19 from "node:fs/promises";
5739
5964
 
5740
5965
  // src/core/instruction-template.ts
@@ -6116,7 +6341,7 @@ var DirectorStateCheckpoint = class {
6116
6341
 
6117
6342
  // src/coordination/checkpoint-wiring.ts
6118
6343
  import * as fsp3 from "node:fs/promises";
6119
- import * as path7 from "node:path";
6344
+ import * as path8 from "node:path";
6120
6345
  async function appendSessionEvent(host, event) {
6121
6346
  if (!host.sessionWriter) return;
6122
6347
  try {
@@ -6164,7 +6389,7 @@ async function writeManifest(host) {
6164
6389
  }),
6165
6390
  usage: host.usage.snapshot()
6166
6391
  };
6167
- await fsp3.mkdir(path7.dirname(host.manifestPath), { recursive: true });
6392
+ await fsp3.mkdir(path8.dirname(host.manifestPath), { recursive: true });
6168
6393
  await atomicWrite(host.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
6169
6394
  return host.manifestPath;
6170
6395
  }
@@ -7524,10 +7749,10 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
7524
7749
  pending: taskIds.filter((id) => !done.has(id))
7525
7750
  });
7526
7751
  }
7527
- return new Promise((resolve19) => {
7752
+ return new Promise((resolve21) => {
7528
7753
  const entry = {
7529
7754
  ids: new Set(taskIds),
7530
- resolve: (result) => resolve19({
7755
+ resolve: (result) => resolve21({
7531
7756
  completed: [result],
7532
7757
  pending: taskIds.filter((id) => id !== result.taskId)
7533
7758
  })
@@ -7535,7 +7760,7 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
7535
7760
  if (opts?.timeoutMs !== void 0) {
7536
7761
  entry.timer = setTimeout(() => {
7537
7762
  this.anyWaiters.delete(entry);
7538
- resolve19({ completed: [], pending: [...taskIds], timedOut: true });
7763
+ resolve21({ completed: [], pending: [...taskIds], timedOut: true });
7539
7764
  }, opts.timeoutMs);
7540
7765
  }
7541
7766
  this.anyWaiters.add(entry);
@@ -7653,11 +7878,11 @@ ${JSON.stringify(result.result, null, 2)}
7653
7878
  this.makeStoppedResult(taskId, "director", `Unknown task id "${taskId}" \u2014 never assigned`)
7654
7879
  );
7655
7880
  }
7656
- let resolve19;
7881
+ let resolve21;
7657
7882
  const promise = new Promise((done) => {
7658
- resolve19 = done;
7883
+ resolve21 = done;
7659
7884
  });
7660
- this.taskWaiters.set(taskId, { promise, resolve: resolve19 });
7885
+ this.taskWaiters.set(taskId, { promise, resolve: resolve21 });
7661
7886
  return promise;
7662
7887
  }
7663
7888
  recordAssignment(task) {
@@ -7918,8 +8143,8 @@ function ensureKanbanTool(tools) {
7918
8143
  }
7919
8144
 
7920
8145
  // src/utils/instruction-file.ts
7921
- import { readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
7922
- import * as path8 from "node:path";
8146
+ import { readFileSync as readFileSync7, statSync as statSync3 } from "node:fs";
8147
+ import * as path9 from "node:path";
7923
8148
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7924
8149
  var textCache = /* @__PURE__ */ new Map();
7925
8150
  var rootCandidates;
@@ -7929,7 +8154,7 @@ function readBundledInstructionText(relativePath) {
7929
8154
  let resolved = "";
7930
8155
  for (const root of instructionRootCandidates()) {
7931
8156
  try {
7932
- resolved = readFileSync6(path8.join(root, relativePath), "utf8").trimEnd();
8157
+ resolved = readFileSync7(path9.join(root, relativePath), "utf8").trimEnd();
7933
8158
  break;
7934
8159
  } catch {
7935
8160
  }
@@ -7945,11 +8170,11 @@ function renderInstructionTemplate(template, values) {
7945
8170
  }
7946
8171
  function instructionRootCandidates() {
7947
8172
  if (rootCandidates !== void 0) return rootCandidates;
7948
- const here = path8.dirname(fileURLToPath2(import.meta.url));
8173
+ const here = path9.dirname(fileURLToPath2(import.meta.url));
7949
8174
  const candidates = [
7950
- path8.resolve(here, "../../instructions"),
7951
- path8.resolve(here, "../instructions"),
7952
- path8.resolve(here, "instructions")
8175
+ path9.resolve(here, "../../instructions"),
8176
+ path9.resolve(here, "../instructions"),
8177
+ path9.resolve(here, "instructions")
7953
8178
  ];
7954
8179
  rootCandidates = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
7955
8180
  return rootCandidates;
@@ -9547,11 +9772,12 @@ function rosterSummaryFromConfigs(roster) {
9547
9772
 
9548
9773
  // src/coordination/director-session.ts
9549
9774
  import * as fsp18 from "node:fs/promises";
9550
- import * as path21 from "node:path";
9775
+ import * as path24 from "node:path";
9551
9776
 
9552
9777
  // src/storage/session-store.ts
9778
+ import { randomUUID as randomUUID10 } from "node:crypto";
9553
9779
  import * as fsp17 from "node:fs/promises";
9554
- import * as path20 from "node:path";
9780
+ import * as path23 from "node:path";
9555
9781
 
9556
9782
  // src/security/file-permissions.ts
9557
9783
  import { chmod } from "node:fs/promises";
@@ -9881,6 +10107,388 @@ var DefaultSecretScrubber = class {
9881
10107
  }
9882
10108
  };
9883
10109
 
10110
+ // src/session-catalog/client.ts
10111
+ import { spawn } from "node:child_process";
10112
+ import * as fs2 from "node:fs";
10113
+ import * as net from "node:net";
10114
+ import * as path11 from "node:path";
10115
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
10116
+
10117
+ // src/session-catalog/endpoint.ts
10118
+ import { createHash as createHash2 } from "node:crypto";
10119
+ import * as os2 from "node:os";
10120
+ import * as path10 from "node:path";
10121
+
10122
+ // src/session-catalog/protocol.ts
10123
+ var SESSION_CATALOG_PROTOCOL_VERSION = 1;
10124
+ var SESSION_CATALOG_MAX_FRAME_CHARS = 4 * 1024 * 1024;
10125
+ function encodeSessionCatalogMessage(message) {
10126
+ return `${JSON.stringify(message)}
10127
+ `;
10128
+ }
10129
+
10130
+ // src/session-catalog/endpoint.ts
10131
+ var SESSION_CATALOG_METADATA_FILE = ".session-catalog-server.json";
10132
+ function normalizedPath(value) {
10133
+ const resolved = path10.resolve(value);
10134
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
10135
+ }
10136
+ function sessionCatalogProjectServerKey(projectDir) {
10137
+ return createHash2("sha256").update(normalizedPath(projectDir)).digest("hex").slice(0, 24);
10138
+ }
10139
+ function sessionCatalogProjectServerEndpoint(projectDir) {
10140
+ const key = sessionCatalogProjectServerKey(projectDir);
10141
+ if (process.platform === "win32") {
10142
+ return `\\\\.\\pipe\\wrongstack-session-catalog-v${SESSION_CATALOG_PROTOCOL_VERSION}-${key}`;
10143
+ }
10144
+ return path10.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
10145
+ }
10146
+ function sessionCatalogProjectServerMetadataPath(projectDir) {
10147
+ return path10.join(projectDir, SESSION_CATALOG_METADATA_FILE);
10148
+ }
10149
+
10150
+ // src/session-catalog/client.ts
10151
+ var CONNECT_TIMEOUT_MS = 750;
10152
+ var START_TIMEOUT_MS = 1e4;
10153
+ var CALL_TIMEOUT_MS = 3e4;
10154
+ var MAX_PENDING_REQUESTS = 1024;
10155
+ var MAX_EVENT_LISTENERS = 64;
10156
+ function locateServer(moduleUrl, exists) {
10157
+ for (const candidate of [
10158
+ "./project-server.js",
10159
+ "../session-catalog/project-server.js",
10160
+ "./session-catalog/project-server.js",
10161
+ "../../dist/session-catalog/project-server.js"
10162
+ ]) {
10163
+ try {
10164
+ const url = new URL(candidate, moduleUrl);
10165
+ if (url.protocol === "file:" && exists(fileURLToPath3(url))) return url;
10166
+ } catch {
10167
+ }
10168
+ }
10169
+ return null;
10170
+ }
10171
+ function resolveSessionCatalogDaemonAvailability(moduleUrl = import.meta.url, exists = fs2.existsSync) {
10172
+ if (process.env["WRONGSTACK_SESSION_CATALOG_INLINE"] || process.env["WRONGSTACK_SESSION_CATALOG_SERVER"] === "0")
10173
+ return { kind: "inline-requested" };
10174
+ const url = locateServer(moduleUrl, exists);
10175
+ return url ? { kind: "available", url } : { kind: "missing-build" };
10176
+ }
10177
+ function resolveSessionCatalogProjectServerUrl(moduleUrl = import.meta.url, exists = fs2.existsSync) {
10178
+ const availability = resolveSessionCatalogDaemonAvailability(moduleUrl, exists);
10179
+ return availability.kind === "available" ? availability.url : null;
10180
+ }
10181
+ function normalize2(value) {
10182
+ const resolved = path11.resolve(value);
10183
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
10184
+ }
10185
+ function delay(ms) {
10186
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
10187
+ }
10188
+ var SessionCatalogProjectClient = class {
10189
+ constructor(options) {
10190
+ this.options = options;
10191
+ this.endpoint = sessionCatalogProjectServerEndpoint(options.projectDir);
10192
+ }
10193
+ options;
10194
+ endpoint;
10195
+ socket = null;
10196
+ info = null;
10197
+ buffer = "";
10198
+ connecting = null;
10199
+ connectResolve = null;
10200
+ connectReject = null;
10201
+ authToken;
10202
+ nextId = 1;
10203
+ pending = /* @__PURE__ */ new Map();
10204
+ eventListeners = /* @__PURE__ */ new Set();
10205
+ reconnectTimer;
10206
+ explicitlyClosed = false;
10207
+ async call(op, args, options = {}) {
10208
+ await this.ensureConnected(true);
10209
+ return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
10210
+ }
10211
+ /**
10212
+ * Call an already-running project daemon without starting one.
10213
+ *
10214
+ * Cross-project discovery must use this path: observing another project is
10215
+ * never sufficient authority to wake that project's IPC owner.
10216
+ */
10217
+ async callExisting(op, args, options = {}) {
10218
+ await this.ensureConnected(false);
10219
+ return this.request({ type: "request", op, args }, options.timeoutMs ?? CALL_TIMEOUT_MS);
10220
+ }
10221
+ ping() {
10222
+ return this.call("ping", {}, { timeoutMs: 3e3 });
10223
+ }
10224
+ async subscribe(listener) {
10225
+ if (!this.eventListeners.has(listener) && this.eventListeners.size >= MAX_EVENT_LISTENERS) {
10226
+ throw new Error(`Session Catalog listener limit reached (${MAX_EVENT_LISTENERS})`);
10227
+ }
10228
+ this.explicitlyClosed = false;
10229
+ this.eventListeners.add(listener);
10230
+ try {
10231
+ await this.call("subscribe", {});
10232
+ } catch (error2) {
10233
+ this.eventListeners.delete(listener);
10234
+ throw error2;
10235
+ }
10236
+ return async () => {
10237
+ this.eventListeners.delete(listener);
10238
+ if (this.eventListeners.size === 0)
10239
+ await this.callExisting("unsubscribe", {}).catch(() => void 0);
10240
+ };
10241
+ }
10242
+ async shutdown(reason) {
10243
+ try {
10244
+ await this.ensureConnected(false);
10245
+ } catch {
10246
+ return { stopped: false };
10247
+ }
10248
+ const result = await this.request({ type: "shutdown", ...reason !== void 0 ? { reason } : {} }, 5e3).catch(
10249
+ () => ({ stopped: false })
10250
+ );
10251
+ if (result.stopped) {
10252
+ const metadataPath = sessionCatalogProjectServerMetadataPath(this.options.projectDir);
10253
+ const deadline = Date.now() + 5e3;
10254
+ while (fs2.existsSync(metadataPath) && Date.now() < deadline) await delay(20);
10255
+ if (result.pid && result.pid !== process.pid) {
10256
+ while (Date.now() < deadline) {
10257
+ try {
10258
+ process.kill(result.pid, 0);
10259
+ await delay(20);
10260
+ } catch {
10261
+ break;
10262
+ }
10263
+ }
10264
+ }
10265
+ }
10266
+ return result;
10267
+ }
10268
+ async close() {
10269
+ this.explicitlyClosed = true;
10270
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
10271
+ this.reconnectTimer = void 0;
10272
+ const socket = this.socket;
10273
+ this.socket = null;
10274
+ this.info = null;
10275
+ if (socket && !socket.destroyed)
10276
+ await new Promise((resolve21) => {
10277
+ socket.once("close", resolve21);
10278
+ socket.end();
10279
+ });
10280
+ }
10281
+ async ensureConnected(spawnIfMissing) {
10282
+ if (this.socket && !this.socket.destroyed && this.info) return;
10283
+ if (this.connecting) return this.connecting;
10284
+ this.connecting = this.connectWithElection(spawnIfMissing).finally(() => {
10285
+ this.connecting = null;
10286
+ });
10287
+ return this.connecting;
10288
+ }
10289
+ async connectWithElection(spawnIfMissing) {
10290
+ const deadline = Date.now() + (spawnIfMissing ? START_TIMEOUT_MS : CONNECT_TIMEOUT_MS);
10291
+ let spawned = false;
10292
+ let lastError = new Error("Session Catalog project server unavailable");
10293
+ while (Date.now() < deadline) {
10294
+ try {
10295
+ await this.connectOnce();
10296
+ return;
10297
+ } catch (error2) {
10298
+ lastError = error2;
10299
+ }
10300
+ if (!spawnIfMissing) break;
10301
+ if (!spawned) {
10302
+ this.spawnDetached();
10303
+ spawned = true;
10304
+ }
10305
+ await delay(75);
10306
+ }
10307
+ throw lastError;
10308
+ }
10309
+ connectOnce() {
10310
+ this.socket?.destroy();
10311
+ this.socket = null;
10312
+ this.info = null;
10313
+ this.authToken = void 0;
10314
+ this.buffer = "";
10315
+ return new Promise((resolve21, reject) => {
10316
+ const socket = net.createConnection(this.endpoint);
10317
+ this.socket = socket;
10318
+ socket.setEncoding("utf8");
10319
+ const timer = setTimeout(() => {
10320
+ reject(new Error("Session Catalog handshake timed out"));
10321
+ socket.destroy();
10322
+ }, CONNECT_TIMEOUT_MS);
10323
+ timer.unref?.();
10324
+ this.connectResolve = () => {
10325
+ clearTimeout(timer);
10326
+ this.connectResolve = null;
10327
+ this.connectReject = null;
10328
+ resolve21();
10329
+ };
10330
+ this.connectReject = (error2) => {
10331
+ clearTimeout(timer);
10332
+ this.connectResolve = null;
10333
+ this.connectReject = null;
10334
+ reject(error2);
10335
+ };
10336
+ socket.on("data", (chunk) => this.onData(socket, chunk));
10337
+ socket.on("error", (error2) => {
10338
+ if (!this.info) this.connectReject?.(error2);
10339
+ });
10340
+ socket.on("close", () => this.onClose(socket));
10341
+ });
10342
+ }
10343
+ currentAuthToken() {
10344
+ if (this.authToken === void 0) {
10345
+ try {
10346
+ const parsed = JSON.parse(
10347
+ fs2.readFileSync(sessionCatalogProjectServerMetadataPath(this.options.projectDir), "utf8")
10348
+ );
10349
+ if (typeof parsed.authToken === "string" && parsed.authToken)
10350
+ this.authToken = parsed.authToken;
10351
+ } catch {
10352
+ }
10353
+ }
10354
+ return this.authToken;
10355
+ }
10356
+ request(message, timeoutMs) {
10357
+ const socket = this.socket;
10358
+ if (!socket || socket.destroyed)
10359
+ return Promise.reject(new Error("Session Catalog connection is unavailable"));
10360
+ const id = this.nextId++;
10361
+ if (this.pending.size >= MAX_PENDING_REQUESTS) {
10362
+ return Promise.reject(
10363
+ new Error(`Session Catalog pending request limit reached (${MAX_PENDING_REQUESTS})`)
10364
+ );
10365
+ }
10366
+ const encoded = encodeSessionCatalogMessage({
10367
+ ...message,
10368
+ id,
10369
+ authToken: this.currentAuthToken()
10370
+ });
10371
+ if (encoded.length > SESSION_CATALOG_MAX_FRAME_CHARS)
10372
+ return Promise.reject(new Error("Session Catalog request exceeded frame limit"));
10373
+ return new Promise((resolve21, reject) => {
10374
+ const timer = setTimeout(() => {
10375
+ const pending = this.pending.get(id);
10376
+ if (!pending) return;
10377
+ this.pending.delete(id);
10378
+ pending.reject(
10379
+ new Error(
10380
+ `Session Catalog ${message.type === "request" ? message.op : message.type} timed out`
10381
+ )
10382
+ );
10383
+ }, timeoutMs);
10384
+ timer.unref?.();
10385
+ this.pending.set(id, { resolve: resolve21, reject, timer });
10386
+ socket.write(encoded);
10387
+ });
10388
+ }
10389
+ onData(socket, chunk) {
10390
+ if (socket !== this.socket) return;
10391
+ this.buffer += chunk;
10392
+ if (this.buffer.length > SESSION_CATALOG_MAX_FRAME_CHARS) {
10393
+ socket.destroy(new Error("Session Catalog response exceeded frame limit"));
10394
+ return;
10395
+ }
10396
+ while (true) {
10397
+ const newline = this.buffer.indexOf("\n");
10398
+ if (newline < 0) return;
10399
+ const line = this.buffer.slice(0, newline);
10400
+ this.buffer = this.buffer.slice(newline + 1);
10401
+ if (!line) continue;
10402
+ try {
10403
+ this.onMessage(JSON.parse(line));
10404
+ } catch {
10405
+ socket.destroy(new Error("Invalid Session Catalog response"));
10406
+ return;
10407
+ }
10408
+ }
10409
+ }
10410
+ onMessage(message) {
10411
+ if (message.type === "hello") {
10412
+ if (message.protocolVersion !== SESSION_CATALOG_PROTOCOL_VERSION) {
10413
+ this.connectReject?.(
10414
+ new Error(
10415
+ `Session Catalog protocol mismatch: client=${SESSION_CATALOG_PROTOCOL_VERSION}, server=${message.protocolVersion}`
10416
+ )
10417
+ );
10418
+ this.socket?.destroy();
10419
+ return;
10420
+ }
10421
+ if (normalize2(message.projectDir) !== normalize2(this.options.projectDir) || normalize2(message.projectRoot) !== normalize2(this.options.projectRoot)) {
10422
+ this.connectReject?.(new Error("Session Catalog project identity mismatch"));
10423
+ this.socket?.destroy();
10424
+ return;
10425
+ }
10426
+ this.info = message;
10427
+ this.connectResolve?.();
10428
+ return;
10429
+ }
10430
+ if (message.type === "event") {
10431
+ for (const listener of this.eventListeners) {
10432
+ try {
10433
+ listener(message.event);
10434
+ } catch {
10435
+ }
10436
+ }
10437
+ return;
10438
+ }
10439
+ const pending = this.pending.get(message.id);
10440
+ if (!pending) return;
10441
+ this.pending.delete(message.id);
10442
+ clearTimeout(pending.timer);
10443
+ if (message.ok) pending.resolve(message.result);
10444
+ else {
10445
+ const error2 = new Error(message.error);
10446
+ if (message.errorName) error2.name = message.errorName;
10447
+ pending.reject(error2);
10448
+ }
10449
+ }
10450
+ onClose(socket) {
10451
+ if (socket !== this.socket) return;
10452
+ this.socket = null;
10453
+ this.info = null;
10454
+ this.authToken = void 0;
10455
+ const error2 = new Error("Session Catalog connection closed");
10456
+ this.connectReject?.(error2);
10457
+ this.connectResolve = null;
10458
+ this.connectReject = null;
10459
+ for (const pending of this.pending.values()) {
10460
+ clearTimeout(pending.timer);
10461
+ pending.reject(error2);
10462
+ }
10463
+ this.pending.clear();
10464
+ this.scheduleSubscriptionReconnect();
10465
+ }
10466
+ scheduleSubscriptionReconnect() {
10467
+ if (this.explicitlyClosed || this.eventListeners.size === 0 || this.reconnectTimer) return;
10468
+ this.reconnectTimer = setTimeout(() => {
10469
+ this.reconnectTimer = void 0;
10470
+ void this.call("subscribe", {}).catch(() => this.scheduleSubscriptionReconnect());
10471
+ }, 250);
10472
+ this.reconnectTimer.unref?.();
10473
+ }
10474
+ spawnDetached() {
10475
+ const url = resolveSessionCatalogProjectServerUrl();
10476
+ if (!url) throw new Error("Built Session Catalog project server is unavailable");
10477
+ const child = spawn(
10478
+ process.execPath,
10479
+ [
10480
+ fileURLToPath3(url),
10481
+ "--project-dir",
10482
+ this.options.projectDir,
10483
+ "--project-root",
10484
+ this.options.projectRoot
10485
+ ],
10486
+ { detached: true, stdio: "ignore", windowsHide: true, env: process.env }
10487
+ );
10488
+ child.unref();
10489
+ }
10490
+ };
10491
+
9884
10492
  // src/utils/assert-never.ts
9885
10493
  function assertNever(x, message) {
9886
10494
  const err = new Error(
@@ -10087,20 +10695,20 @@ var color = {
10087
10695
  };
10088
10696
 
10089
10697
  // src/utils/config-backup.ts
10090
- import * as fs2 from "node:fs/promises";
10091
- import * as path9 from "node:path";
10698
+ import * as fs3 from "node:fs/promises";
10699
+ import * as path12 from "node:path";
10092
10700
  function configHistoryDir(globalRoot) {
10093
- return path9.join(globalRoot, "config-history");
10701
+ return path12.join(globalRoot, "config-history");
10094
10702
  }
10095
10703
  function configSlug(absolutePath, globalRoot) {
10096
- const rel = path9.relative(globalRoot, absolutePath);
10704
+ const rel = path12.relative(globalRoot, absolutePath);
10097
10705
  const normalized = rel.replace(/\\/g, "/").replace(/\.json$/i, "");
10098
10706
  return normalized.replace(/\//g, "-");
10099
10707
  }
10100
10708
  async function backupConfigFile(filePath, paths) {
10101
10709
  let currentContent;
10102
10710
  try {
10103
- currentContent = await fs2.readFile(filePath, "utf8");
10711
+ currentContent = await fs3.readFile(filePath, "utf8");
10104
10712
  if (!currentContent.trim()) return;
10105
10713
  } catch {
10106
10714
  return;
@@ -10109,10 +10717,10 @@ async function backupConfigFile(filePath, paths) {
10109
10717
  const ts = now.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
10110
10718
  const slug = configSlug(filePath, paths.globalRoot);
10111
10719
  const backupDir = configHistoryDir(paths.globalRoot);
10112
- const backupFile = path9.join(backupDir, `${slug}-${ts}.json`);
10720
+ const backupFile = path12.join(backupDir, `${slug}-${ts}.json`);
10113
10721
  try {
10114
- await fs2.mkdir(backupDir, { recursive: true });
10115
- await fs2.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
10722
+ await fs3.mkdir(backupDir, { recursive: true });
10723
+ await fs3.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
10116
10724
  } catch {
10117
10725
  }
10118
10726
  }
@@ -10176,15 +10784,11 @@ function deepMerge(base, patch, options = {}) {
10176
10784
  return out;
10177
10785
  }
10178
10786
 
10179
- // src/types/blocks.ts
10180
- function isTextBlock(b) {
10181
- return b.type === "text";
10182
- }
10183
-
10184
10787
  // src/utils/context-evidence.ts
10185
10788
  var MAX_DIGEST_CHARS = 4e3;
10186
10789
  function createContextEvidenceState() {
10187
10790
  return {
10791
+ recentUserTurns: [],
10188
10792
  sessionGoals: [],
10189
10793
  implicitFacts: [],
10190
10794
  activeErrors: [],
@@ -10201,6 +10805,11 @@ function buildContextEvidenceDigest(ctx) {
10201
10805
  if (state.currentIntent?.text) {
10202
10806
  lines.push(`intent: ${state.currentIntent.text}`);
10203
10807
  }
10808
+ const priorTurns = (state.recentUserTurns ?? []).slice(-6, -1);
10809
+ if (priorTurns.length > 0) {
10810
+ lines.push("recent_human_instructions:");
10811
+ for (const turn of priorTurns) lines.push(`- ${turn.text}`);
10812
+ }
10204
10813
  const goals = state.sessionGoals.slice(-3);
10205
10814
  if (goals.length > 0) {
10206
10815
  lines.push("session_goals:");
@@ -10290,6 +10899,7 @@ function ensureEvidence(ctx) {
10290
10899
  ctx.contextEvidence = createContextEvidenceState();
10291
10900
  }
10292
10901
  ctx.contextEvidence.completedWork ??= [];
10902
+ ctx.contextEvidence.recentUserTurns ??= [];
10293
10903
  return ctx.contextEvidence;
10294
10904
  }
10295
10905
 
@@ -10400,7 +11010,7 @@ function isEmptyMessage(msg) {
10400
11010
  }
10401
11011
 
10402
11012
  // src/core/context.ts
10403
- import * as path10 from "node:path";
11013
+ import * as path13 from "node:path";
10404
11014
 
10405
11015
  // src/utils/tool-wire-compact.ts
10406
11016
  var TOOL_DESCRIPTION_MAX_CHARS = 400;
@@ -10804,12 +11414,11 @@ var ConversationState = class {
10804
11414
  }
10805
11415
  this.ctx.messages.splice(this.ctx.messages.length, 0, message);
10806
11416
  const overflow = this.overflowCount(this.ctx.messages);
11417
+ this.emit({ kind: "message_appended", message });
10807
11418
  if (overflow > 0) {
10808
11419
  this.ctx.messages.splice(0, overflow);
10809
11420
  this.ctx.toolAdjacencyDirty = true;
10810
- this.emit({ kind: "messages_replaced", messages: [...this.ctx.messages] });
10811
- } else {
10812
- this.emit({ kind: "message_appended", message });
11421
+ this.emit({ kind: "messages_dropped", count: overflow });
10813
11422
  }
10814
11423
  }
10815
11424
  /**
@@ -11307,6 +11916,11 @@ var Context = class _Context {
11307
11916
  ts,
11308
11917
  version: 1,
11309
11918
  messages: [...change.messages]
11919
+ } : change.kind === "messages_dropped" ? {
11920
+ type: "messages_dropped",
11921
+ ts,
11922
+ version: 1,
11923
+ count: change.count
11310
11924
  } : null;
11311
11925
  if (!event) return;
11312
11926
  this.enqueueConversationJournal(event, this.session);
@@ -11499,6 +12113,12 @@ var Context = class _Context {
11499
12113
  setCurrentKanbanTask(taskId, boardId) {
11500
12114
  this.currentKanbanTaskId = taskId;
11501
12115
  this.currentKanbanBoardId = boardId;
12116
+ const existing = this.meta["kanban"] && typeof this.meta["kanban"] === "object" ? this.meta["kanban"] : {};
12117
+ this.state.setMeta("kanban", {
12118
+ ...existing,
12119
+ ...taskId ? { taskId } : { taskId: void 0 },
12120
+ ...boardId ? { boardId } : { boardId: void 0 }
12121
+ });
11502
12122
  }
11503
12123
  /**
11504
12124
  * Record a comprehensive file event for the audit trail.
@@ -11592,11 +12212,11 @@ var Context = class _Context {
11592
12212
  * Returns the resolved absolute path.
11593
12213
  */
11594
12214
  setWorkingDir(dir) {
11595
- const resolved = path10.isAbsolute(dir) ? path10.resolve(dir) : path10.resolve(this.projectRoot, dir);
12215
+ const resolved = path13.isAbsolute(dir) ? path13.resolve(dir) : path13.resolve(this.projectRoot, dir);
11596
12216
  if (!this.allowOutsideProjectRoot) {
11597
- const root = path10.resolve(this.projectRoot);
11598
- const rel = path10.relative(root, resolved);
11599
- if (rel.startsWith("..") || path10.isAbsolute(rel)) {
12217
+ const root = path13.resolve(this.projectRoot);
12218
+ const rel = path13.relative(root, resolved);
12219
+ if (rel.startsWith("..") || path13.isAbsolute(rel)) {
11600
12220
  throw new Error(`Working directory "${resolved}" is outside project root "${root}"`);
11601
12221
  }
11602
12222
  }
@@ -12025,10 +12645,10 @@ function validateAgainstSchema(value, schema) {
12025
12645
  return { ok: errors.length === 0, errors };
12026
12646
  }
12027
12647
  var MAX_SCHEMA_DEPTH = 64;
12028
- function walk(value, schema, path42, errors, depth) {
12648
+ function walk(value, schema, path45, errors, depth) {
12029
12649
  if (depth > MAX_SCHEMA_DEPTH) {
12030
12650
  errors.push({
12031
- path: path42 || "<root>",
12651
+ path: path45 || "<root>",
12032
12652
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
12033
12653
  });
12034
12654
  return;
@@ -12036,7 +12656,7 @@ function walk(value, schema, path42, errors, depth) {
12036
12656
  if (schema.enum !== void 0) {
12037
12657
  if (!enumIncludes(schema.enum, value)) {
12038
12658
  errors.push({
12039
- path: path42 || "<root>",
12659
+ path: path45 || "<root>",
12040
12660
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
12041
12661
  });
12042
12662
  return;
@@ -12045,7 +12665,7 @@ function walk(value, schema, path42, errors, depth) {
12045
12665
  if (typeof schema.type === "string") {
12046
12666
  if (!checkType(value, schema.type)) {
12047
12667
  errors.push({
12048
- path: path42 || "<root>",
12668
+ path: path45 || "<root>",
12049
12669
  message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
12050
12670
  });
12051
12671
  return;
@@ -12057,7 +12677,7 @@ function walk(value, schema, path42, errors, depth) {
12057
12677
  if (!(req in obj)) {
12058
12678
  const expected = schema.properties?.[req]?.type;
12059
12679
  errors.push({
12060
- path: joinPath(path42, req),
12680
+ path: joinPath(path45, req),
12061
12681
  message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
12062
12682
  });
12063
12683
  }
@@ -12065,14 +12685,14 @@ function walk(value, schema, path42, errors, depth) {
12065
12685
  if (schema.properties) {
12066
12686
  for (const [key, subSchema] of Object.entries(schema.properties)) {
12067
12687
  if (key in obj) {
12068
- walk(obj[key], subSchema, joinPath(path42, key), errors, depth + 1);
12688
+ walk(obj[key], subSchema, joinPath(path45, key), errors, depth + 1);
12069
12689
  }
12070
12690
  }
12071
12691
  }
12072
12692
  }
12073
12693
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
12074
12694
  for (let i = 0; i < value.length; i++) {
12075
- walk(value[i], schema.items, `${path42}[${i}]`, errors, depth + 1);
12695
+ walk(value[i], schema.items, `${path45}[${i}]`, errors, depth + 1);
12076
12696
  }
12077
12697
  }
12078
12698
  }
@@ -12432,14 +13052,14 @@ function compileUserRegex(pattern, flags) {
12432
13052
  var MAX_SUBJECT_LEN = 64 * 1024;
12433
13053
 
12434
13054
  // src/utils/session-scoped-path.ts
12435
- import * as path11 from "node:path";
13055
+ import * as path14 from "node:path";
12436
13056
  function sessionScopedPath(dir, sessionId, suffix) {
12437
13057
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
12438
13058
  throw invalid(sessionId);
12439
13059
  }
12440
- const resolved = path11.resolve(dir, `${sessionId}${suffix}`);
12441
- const rel = path11.relative(path11.resolve(dir), resolved);
12442
- if (rel.startsWith("..") || path11.isAbsolute(rel)) {
13060
+ const resolved = path14.resolve(dir, `${sessionId}${suffix}`);
13061
+ const rel = path14.relative(path14.resolve(dir), resolved);
13062
+ if (rel.startsWith("..") || path14.isAbsolute(rel)) {
12443
13063
  throw invalid(sessionId);
12444
13064
  }
12445
13065
  return resolved;
@@ -12455,7 +13075,7 @@ function invalid(sessionId) {
12455
13075
 
12456
13076
  // src/utils/sleep.ts
12457
13077
  function sleep(ms) {
12458
- return new Promise((resolve19) => setTimeout(resolve19, ms));
13078
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
12459
13079
  }
12460
13080
 
12461
13081
  // src/utils/slug.ts
@@ -13194,6 +13814,9 @@ function subjectForToolInput(toolName, input, subjectKey) {
13194
13814
  const obj = input;
13195
13815
  if (subjectKey) {
13196
13816
  const value = obj[subjectKey];
13817
+ if (Array.isArray(value)) {
13818
+ return escapeGlobSubject(JSON.stringify(value));
13819
+ }
13197
13820
  if (typeof value === "string") {
13198
13821
  if (isPathSubjectKey(subjectKey)) {
13199
13822
  const normalized = normalizePathSubject(value);
@@ -13238,7 +13861,7 @@ function subjectForToolInput(toolName, input, subjectKey) {
13238
13861
  // src/storage/file-session-writer.ts
13239
13862
  import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
13240
13863
  import * as fsp7 from "node:fs/promises";
13241
- import * as path12 from "node:path";
13864
+ import * as path15 from "node:path";
13242
13865
  import { createInterface } from "node:readline";
13243
13866
 
13244
13867
  // src/storage/session-workspace-checkpoints.ts
@@ -13504,7 +14127,7 @@ var FileSessionWriter = class _FileSessionWriter {
13504
14127
  this.meta = meta;
13505
14128
  this.events = events;
13506
14129
  this.resumed = opts.resumed ?? false;
13507
- this.manifestFile = opts.dir ? path12.join(opts.dir, `${path12.basename(id)}.summary.json`) : "";
14130
+ this.manifestFile = opts.dir ? path15.join(opts.dir, `${path15.basename(id)}.summary.json`) : "";
13508
14131
  this.filePath = opts.filePath ?? "";
13509
14132
  this.secretScrubber = opts.secretScrubber;
13510
14133
  this.checkpointCas = opts.checkpointCas;
@@ -14393,10 +15016,10 @@ var FileSessionWriter = class _FileSessionWriter {
14393
15016
  };
14394
15017
 
14395
15018
  // src/storage/session-checkpoint-cas.ts
14396
- import { spawn } from "node:child_process";
14397
- import { createHash as createHash2, randomUUID as randomUUID9 } from "node:crypto";
15019
+ import { spawn as spawn2 } from "node:child_process";
15020
+ import { createHash as createHash3, randomUUID as randomUUID9 } from "node:crypto";
14398
15021
  import * as fsp8 from "node:fs/promises";
14399
- import * as path13 from "node:path";
15022
+ import * as path16 from "node:path";
14400
15023
 
14401
15024
  // src/storage/storage-concurrency.ts
14402
15025
  async function mapWithConcurrency(items, concurrency, mapper) {
@@ -14424,16 +15047,16 @@ var MAX_GIT_OUTPUT = 16 * 1024 * 1024;
14424
15047
  var MAX_BLOB_BYTES = 64 * 1024 * 1024;
14425
15048
  var MAX_CHECKPOINT_BYTES = 512 * 1024 * 1024;
14426
15049
  function sha256(content) {
14427
- return createHash2("sha256").update(content).digest("hex");
15050
+ return createHash3("sha256").update(content).digest("hex");
14428
15051
  }
14429
15052
  function isInside(root, target) {
14430
- const relative8 = path13.relative(root, target);
14431
- return relative8 === "" || !relative8.startsWith("..") && !path13.isAbsolute(relative8);
15053
+ const relative8 = path16.relative(root, target);
15054
+ return relative8 === "" || !relative8.startsWith("..") && !path16.isAbsolute(relative8);
14432
15055
  }
14433
15056
  function normalizeRelative(input) {
14434
- if (!input || path13.isAbsolute(input)) return null;
15057
+ if (!input || path16.isAbsolute(input)) return null;
14435
15058
  const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
14436
- const resolved = path13.posix.normalize(normalized);
15059
+ const resolved = path16.posix.normalize(normalized);
14437
15060
  if (!resolved || resolved === "." || resolved === ".." || resolved.startsWith("../")) return null;
14438
15061
  return resolved;
14439
15062
  }
@@ -14448,8 +15071,8 @@ var SessionCheckpointCas = class {
14448
15071
  projectRoot;
14449
15072
  runGit;
14450
15073
  constructor(opts) {
14451
- this.rootDir = path13.resolve(opts.rootDir);
14452
- this.projectRoot = path13.resolve(opts.projectRoot);
15074
+ this.rootDir = path16.resolve(opts.rootDir);
15075
+ this.projectRoot = path16.resolve(opts.projectRoot);
14453
15076
  this.runGit = opts.runGit ?? defaultRunGit;
14454
15077
  }
14455
15078
  async capture(_sessionId, _promptIndex) {
@@ -14474,7 +15097,7 @@ var SessionCheckpointCas = class {
14474
15097
  relativePaths,
14475
15098
  CAPTURE_CONCURRENCY,
14476
15099
  async (relative8) => {
14477
- const absolute = path13.resolve(this.projectRoot, ...relative8.split("/"));
15100
+ const absolute = path16.resolve(this.projectRoot, ...relative8.split("/"));
14478
15101
  if (!isInside(this.projectRoot, absolute)) {
14479
15102
  unresolved.push({ path: relative8, reason: "path escapes project root" });
14480
15103
  return null;
@@ -14483,8 +15106,8 @@ var SessionCheckpointCas = class {
14483
15106
  const stat15 = await fsp8.lstat(absolute);
14484
15107
  if (stat15.isSymbolicLink()) {
14485
15108
  const linkTarget = await fsp8.readlink(absolute);
14486
- const resolvedLink = path13.resolve(path13.dirname(absolute), linkTarget);
14487
- if (path13.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
15109
+ const resolvedLink = path16.resolve(path16.dirname(absolute), linkTarget);
15110
+ if (path16.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
14488
15111
  unresolved.push({
14489
15112
  path: relative8,
14490
15113
  reason: "symlink target escapes project root"
@@ -14546,7 +15169,7 @@ var SessionCheckpointCas = class {
14546
15169
  };
14547
15170
  }
14548
15171
  async materialize(checkpoint, targetRoot) {
14549
- const target = path13.resolve(targetRoot);
15172
+ const target = path16.resolve(targetRoot);
14550
15173
  if (target === this.projectRoot) {
14551
15174
  throw new Error("Refusing to materialize a workspace checkpoint over the parent project root");
14552
15175
  }
@@ -14586,10 +15209,10 @@ var SessionCheckpointCas = class {
14586
15209
  try {
14587
15210
  const output = await this.safeOutputPath(target, realTarget, entry.path);
14588
15211
  if (entry.state === "symlink") {
14589
- if (path13.isAbsolute(entry.linkTarget)) {
15212
+ if (path16.isAbsolute(entry.linkTarget)) {
14590
15213
  throw new Error("absolute symlink target refused");
14591
15214
  }
14592
- const resolvedLink = path13.resolve(path13.dirname(output), entry.linkTarget);
15215
+ const resolvedLink = path16.resolve(path16.dirname(output), entry.linkTarget);
14593
15216
  if (!isInside(target, resolvedLink)) throw new Error("symlink target escapes checkpoint root");
14594
15217
  }
14595
15218
  prepared.push({
@@ -14617,7 +15240,7 @@ var SessionCheckpointCas = class {
14617
15240
  await fsp8.unlink(output).catch((err) => {
14618
15241
  if (err.code !== "ENOENT") throw err;
14619
15242
  });
14620
- await fsp8.mkdir(path13.dirname(output), { recursive: true });
15243
+ await fsp8.mkdir(path16.dirname(output), { recursive: true });
14621
15244
  await fsp8.symlink(entry.linkTarget, output);
14622
15245
  writtenFiles.push(entry.path);
14623
15246
  continue;
@@ -14634,15 +15257,15 @@ var SessionCheckpointCas = class {
14634
15257
  }
14635
15258
  objectPath(hash2) {
14636
15259
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid CAS object hash: ${hash2}`);
14637
- return path13.join(this.rootDir, "objects", hash2.slice(0, 2), hash2.slice(2));
15260
+ return path16.join(this.rootDir, "objects", hash2.slice(0, 2), hash2.slice(2));
14638
15261
  }
14639
15262
  manifestPath(hash2) {
14640
15263
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid checkpoint manifest hash: ${hash2}`);
14641
- return path13.join(this.rootDir, "manifests", `${hash2}.json`);
15264
+ return path16.join(this.rootDir, "manifests", `${hash2}.json`);
14642
15265
  }
14643
15266
  async putBlob(hash2, content) {
14644
15267
  const target = this.objectPath(hash2);
14645
- await fsp8.mkdir(path13.dirname(target), { recursive: true });
15268
+ await fsp8.mkdir(path16.dirname(target), { recursive: true });
14646
15269
  try {
14647
15270
  const existing = await fsp8.readFile(target);
14648
15271
  if (sha256(existing) !== hash2) throw new Error(`Corrupt CAS object collision: ${hash2}`);
@@ -14650,9 +15273,9 @@ var SessionCheckpointCas = class {
14650
15273
  } catch (err) {
14651
15274
  if (err.code !== "ENOENT") throw err;
14652
15275
  }
14653
- const temp = path13.join(
14654
- path13.dirname(target),
14655
- `.${path13.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
15276
+ const temp = path16.join(
15277
+ path16.dirname(target),
15278
+ `.${path16.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
14656
15279
  );
14657
15280
  let handle;
14658
15281
  try {
@@ -14713,7 +15336,7 @@ var SessionCheckpointCas = class {
14713
15336
  async safeOutputPath(target, realTarget, relative8) {
14714
15337
  const normalized = normalizeRelative(relative8);
14715
15338
  if (!normalized) throw new Error("invalid relative path");
14716
- const output = path13.resolve(target, ...normalized.split("/"));
15339
+ const output = path16.resolve(target, ...normalized.split("/"));
14717
15340
  if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
14718
15341
  let probe = output;
14719
15342
  for (; ; ) {
@@ -14723,7 +15346,7 @@ var SessionCheckpointCas = class {
14723
15346
  return output;
14724
15347
  } catch (err) {
14725
15348
  if (err.code !== "ENOENT") throw err;
14726
- const parent = path13.dirname(probe);
15349
+ const parent = path16.dirname(probe);
14727
15350
  if (parent === probe) throw err;
14728
15351
  probe = parent;
14729
15352
  }
@@ -14731,14 +15354,14 @@ var SessionCheckpointCas = class {
14731
15354
  }
14732
15355
  };
14733
15356
  function defaultRunGit(args, cwd) {
14734
- return new Promise((resolve19) => {
15357
+ return new Promise((resolve21) => {
14735
15358
  const stdoutChunks = [];
14736
15359
  const stderrChunks = [];
14737
15360
  let stdoutBytes = 0;
14738
15361
  let stderrBytes = 0;
14739
15362
  let stdoutTruncated = false;
14740
15363
  let stderrTruncated = false;
14741
- const child = spawn("git", args, {
15364
+ const child = spawn2("git", args, {
14742
15365
  cwd,
14743
15366
  env: buildChildEnv(),
14744
15367
  stdio: ["ignore", "pipe", "pipe"],
@@ -14774,8 +15397,8 @@ function defaultRunGit(args, cwd) {
14774
15397
  stdoutTruncated,
14775
15398
  stderrTruncated
14776
15399
  });
14777
- child.on("error", (err) => resolve19(result(1, err.message)));
14778
- child.on("close", (code) => resolve19(result(code ?? 1)));
15400
+ child.on("error", (err) => resolve21(result(1, err.message)));
15401
+ child.on("close", (code) => resolve21(result(code ?? 1)));
14779
15402
  });
14780
15403
  }
14781
15404
 
@@ -14787,13 +15410,13 @@ function generateSessionId(startedAt, _model) {
14787
15410
  }
14788
15411
 
14789
15412
  // src/storage/session-id-resolver.ts
14790
- import * as path14 from "node:path";
15413
+ import * as path17 from "node:path";
14791
15414
  function resolveSessionId(query, candidateIds) {
14792
15415
  const normalized = query.trim();
14793
15416
  if (!normalized) return { status: "missing", query: normalized };
14794
15417
  const uniqueIds = [...new Set(candidateIds)];
14795
15418
  if (uniqueIds.includes(normalized)) return { status: "resolved", id: normalized };
14796
- const leaf = (id) => path14.posix.basename(id.replace(/\\/g, "/"));
15419
+ const leaf = (id) => path17.posix.basename(id.replace(/\\/g, "/"));
14797
15420
  const exactLeafMatches = uniqueIds.filter((id) => leaf(id) === normalized);
14798
15421
  if (exactLeafMatches.length === 1) {
14799
15422
  return { status: "resolved", id: exactLeafMatches[0] };
@@ -14845,15 +15468,15 @@ function scrubPersistedSessionSummary(summary, scrubber2) {
14845
15468
  }
14846
15469
 
14847
15470
  // src/storage/session-resume-validation.ts
14848
- import { createHash as createHash3 } from "node:crypto";
15471
+ import { createHash as createHash4 } from "node:crypto";
14849
15472
  import * as fsp9 from "node:fs/promises";
14850
- import * as path15 from "node:path";
15473
+ import * as path18 from "node:path";
14851
15474
  var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
14852
15475
  var VALIDATION_CONCURRENCY = 8;
14853
15476
  var NOTICE_PATH_LIMIT = 20;
14854
15477
  function isInside2(root, target) {
14855
- const relative8 = path15.relative(root, target);
14856
- return relative8 === "" || !relative8.startsWith("..") && !path15.isAbsolute(relative8);
15478
+ const relative8 = path18.relative(root, target);
15479
+ return relative8 === "" || !relative8.startsWith("..") && !path18.isAbsolute(relative8);
14857
15480
  }
14858
15481
  function errno(err) {
14859
15482
  return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
@@ -14864,7 +15487,7 @@ function latestObservations(events, projectRoot) {
14864
15487
  if (event.type !== "file_observation" || typeof event.path !== "string" || event.path.length === 0 || typeof event.hash !== "string" || !/^[a-f\d]{64}$/i.test(event.hash)) {
14865
15488
  continue;
14866
15489
  }
14867
- const normalized = path15.resolve(projectRoot, event.path);
15490
+ const normalized = path18.resolve(projectRoot, event.path);
14868
15491
  latest.set(normalized, {
14869
15492
  path: normalized,
14870
15493
  hash: event.hash.toLowerCase(),
@@ -14913,7 +15536,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
14913
15536
  };
14914
15537
  }
14915
15538
  const content = await fsp9.readFile(realFile, "utf8");
14916
- const actualHash = createHash3("sha256").update(content, "utf8").digest("hex");
15539
+ const actualHash = createHash4("sha256").update(content, "utf8").digest("hex");
14917
15540
  if (actualHash === observation.hash) return null;
14918
15541
  return { ...base, status: "modified", actualHash };
14919
15542
  } catch (err) {
@@ -14922,7 +15545,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
14922
15545
  }
14923
15546
  }
14924
15547
  async function validateResumeFileObservations(events, projectRoot) {
14925
- const lexicalRoot = path15.resolve(projectRoot);
15548
+ const lexicalRoot = path18.resolve(projectRoot);
14926
15549
  const realRoot = await fsp9.realpath(lexicalRoot).catch(() => lexicalRoot);
14927
15550
  const observations = latestObservations(events, lexicalRoot);
14928
15551
  const results = await mapWithConcurrency(
@@ -14938,9 +15561,9 @@ async function validateResumeFileObservations(events, projectRoot) {
14938
15561
  }
14939
15562
  function formatResumeValidationNotice(validation, projectRoot) {
14940
15563
  if (validation.staleFiles.length === 0) return null;
14941
- const root = path15.resolve(projectRoot);
15564
+ const root = path18.resolve(projectRoot);
14942
15565
  const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
14943
- const relative8 = path15.relative(root, entry.path);
15566
+ const relative8 = path18.relative(root, entry.path);
14944
15567
  const display = isInside2(root, entry.path) ? relative8 || "." : entry.path;
14945
15568
  return `- ${JSON.stringify(display)} [${entry.status}]`;
14946
15569
  });
@@ -14965,22 +15588,22 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
14965
15588
 
14966
15589
  // src/storage/session-store/delete-session-artifacts.ts
14967
15590
  import * as fsp10 from "node:fs/promises";
14968
- import * as path17 from "node:path";
15591
+ import * as path20 from "node:path";
14969
15592
 
14970
15593
  // src/storage/session-store/paths.ts
14971
- import * as path16 from "node:path";
15594
+ import * as path19 from "node:path";
14972
15595
  function sessionPath(storeDir, id, ext) {
14973
15596
  return sessionScopedPath(storeDir, id, ext);
14974
15597
  }
14975
15598
  function shardManifestPath(storeDir, shardKey) {
14976
- return shardKey ? path16.join(storeDir, shardKey, "_manifest.json") : path16.join(storeDir, "_manifest.json");
15599
+ return shardKey ? path19.join(storeDir, shardKey, "_manifest.json") : path19.join(storeDir, "_manifest.json");
14977
15600
  }
14978
15601
  function shardKeyForSessionId(id) {
14979
- const dirName = path16.dirname(id);
15602
+ const dirName = path19.dirname(id);
14980
15603
  return dirName === "." ? "" : dirName;
14981
15604
  }
14982
15605
  async function ensureShardDir(storeDir, id) {
14983
- const dirPath = path16.dirname(sessionScopedPath(storeDir, id, ""));
15606
+ const dirPath = path19.dirname(sessionScopedPath(storeDir, id, ""));
14984
15607
  await ensureDir(dirPath);
14985
15608
  return dirPath;
14986
15609
  }
@@ -14991,9 +15614,9 @@ async function deleteSessionArtifacts({
14991
15614
  id,
14992
15615
  jsonlPath
14993
15616
  }) {
14994
- const shardDir = path17.dirname(jsonlPath);
14995
- const base = path17.basename(id);
14996
- const sessDir = path17.join(shardDir, base);
15617
+ const shardDir = path20.dirname(jsonlPath);
15618
+ const base = path20.basename(id);
15619
+ const sessDir = path20.join(shardDir, base);
14997
15620
  const deletions = [
14998
15621
  fsp10.unlink(jsonlPath),
14999
15622
  fsp10.unlink(sessionPath(rootDir, id, ".summary.json")),
@@ -15052,7 +15675,7 @@ function isSessionJsonlFileName(name) {
15052
15675
 
15053
15676
  // src/storage/session-store/directory-session-files.ts
15054
15677
  import * as fsp11 from "node:fs/promises";
15055
- import * as path18 from "node:path";
15678
+ import * as path21 from "node:path";
15056
15679
  function sessionIdForFile(prefix, name) {
15057
15680
  const base = name.replace(/\.jsonl$/, "");
15058
15681
  return prefix ? `${prefix}/${base}` : base;
@@ -15074,12 +15697,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
15074
15697
  if (entry.isDirectory()) {
15075
15698
  dirEntries.push(entry);
15076
15699
  } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
15077
- files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path18.join(dir, entry.name) });
15700
+ files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path21.join(dir, entry.name) });
15078
15701
  }
15079
15702
  }
15080
15703
  const childFileArrays = await Promise.all(
15081
15704
  dirEntries.map(
15082
- (entry) => collectSessionFiles(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15705
+ (entry) => collectSessionFiles(path21.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15083
15706
  )
15084
15707
  );
15085
15708
  return [...childFileArrays.flat(), ...files];
@@ -15103,7 +15726,7 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
15103
15726
  }
15104
15727
  const childIdArrays = await Promise.all(
15105
15728
  dirEntries.map(
15106
- (entry) => collectSessionIds(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15729
+ (entry) => collectSessionIds(path21.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15107
15730
  )
15108
15731
  );
15109
15732
  return [...childIdArrays.flat(), ...fileIds];
@@ -15145,7 +15768,7 @@ function emitSessionStoreError(events, sessionId, filePath, operation, error2, r
15145
15768
  }
15146
15769
 
15147
15770
  // src/storage/session-store/fork-session.ts
15148
- import { createHash as createHash4 } from "node:crypto";
15771
+ import { createHash as createHash5 } from "node:crypto";
15149
15772
 
15150
15773
  // src/storage/session-store/replay.ts
15151
15774
  function isReplayableMessage(value) {
@@ -15217,7 +15840,7 @@ async function forkSession(host, id, opts = {}) {
15217
15840
  }
15218
15841
  const parentPrefix = parent.events.slice(0, boundary + 1);
15219
15842
  const workspaceCheckpoint = targetCheckpoint?.workspaceCheckpoint;
15220
- const checkpointHash = createHash4("sha256").update(parentPrefix.map((event) => JSON.stringify(event)).join("\n") + "\n", "utf8").digest("hex");
15843
+ const checkpointHash = createHash5("sha256").update(parentPrefix.map((event) => JSON.stringify(event)).join("\n") + "\n", "utf8").digest("hex");
15221
15844
  const inherited = parentPrefix.filter(inheritsIntoFork);
15222
15845
  const writer = await host.create({
15223
15846
  id: "",
@@ -15560,6 +16183,16 @@ function replaySessionEvent(params) {
15560
16183
  } else {
15561
16184
  emitDamaged(params, "Ignored malformed messages_replaced event");
15562
16185
  }
16186
+ } else if (ev.type === "messages_dropped" && ev.version === 1) {
16187
+ if (exactJournalActive && Number.isInteger(ev.count) && ev.count > 0) {
16188
+ messages.splice(0, Math.min(ev.count, messages.length));
16189
+ openToolUses.clear();
16190
+ for (const current of messages) trackMessageToolState(current, openToolUses);
16191
+ } else if (!exactJournalActive) {
16192
+ emitDamaged(params, "Ignored messages_dropped event outside the exact journal");
16193
+ } else {
16194
+ emitDamaged(params, `Ignored malformed messages_dropped event (count ${String(ev.count)})`);
16195
+ }
15563
16196
  } else if (ev.type === "context_snapshot") {
15564
16197
  if (!applyContextSnapshot(messages, openToolUses, ev.messages)) {
15565
16198
  emitDamaged(params, "Ignored malformed context_snapshot event");
@@ -15614,7 +16247,7 @@ function emitDamaged(params, detail) {
15614
16247
 
15615
16248
  // src/storage/session-store/prune-helpers.ts
15616
16249
  import * as fsp13 from "node:fs/promises";
15617
- import * as path19 from "node:path";
16250
+ import * as path22 from "node:path";
15618
16251
  function isPrunableSessionJsonl(name) {
15619
16252
  return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
15620
16253
  }
@@ -15622,7 +16255,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15622
16255
  const cutoff = Date.now() - maxAgeDays * 864e5;
15623
16256
  let deleted = 0;
15624
16257
  const pruneFile = async (dir, name, prefix) => {
15625
- const jsonlPath = path19.join(dir, name);
16258
+ const jsonlPath = path22.join(dir, name);
15626
16259
  try {
15627
16260
  const stat15 = await fsp13.stat(jsonlPath);
15628
16261
  if (stat15.mtimeMs >= cutoff) return;
@@ -15641,7 +16274,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15641
16274
  continue;
15642
16275
  }
15643
16276
  if (!entry.isDirectory()) continue;
15644
- const dateDir = path19.join(storeDir, entry.name);
16277
+ const dateDir = path22.join(storeDir, entry.name);
15645
16278
  const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
15646
16279
  for (const file of files) {
15647
16280
  if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
@@ -15650,7 +16283,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
15650
16283
  }
15651
16284
  for (const entry of entries) {
15652
16285
  if (!entry.isDirectory()) continue;
15653
- const dateDir = path19.join(storeDir, entry.name);
16286
+ const dateDir = path22.join(storeDir, entry.name);
15654
16287
  try {
15655
16288
  const remaining = await fsp13.readdir(dateDir);
15656
16289
  if (remaining.length === 0) {
@@ -15983,6 +16616,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
15983
16616
  logger;
15984
16617
  onAppend;
15985
16618
  onAppendBatch;
16619
+ /** Present in built production output; source-only tests retain the local compatibility path. */
16620
+ catalogClient;
16621
+ maintenanceHolderId = randomUUID10();
15986
16622
  /**
15987
16623
  * In-memory cache for load() results, keyed by session ID. The cache is
15988
16624
  * invalidated when the file's mtimeMs or size changes (indicating the
@@ -16000,9 +16636,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
16000
16636
  static LIST_SCAN_CONCURRENCY = 32;
16001
16637
  constructor(opts) {
16002
16638
  this.dir = opts.dir;
16003
- this.projectRoot = opts.projectRoot ? path20.resolve(opts.projectRoot) : void 0;
16639
+ this.projectRoot = opts.projectRoot ? path23.resolve(opts.projectRoot) : void 0;
16004
16640
  this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
16005
- rootDir: path20.join(this.dir, "_cas"),
16641
+ rootDir: path23.join(this.dir, "_cas"),
16006
16642
  projectRoot: this.projectRoot
16007
16643
  }) : void 0;
16008
16644
  this.events = opts.events;
@@ -16011,6 +16647,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
16011
16647
  this.logger = opts.logger;
16012
16648
  this.onAppend = opts.onAppend;
16013
16649
  this.onAppendBatch = opts.onAppendBatch;
16650
+ const builtRuntime = import.meta.url.includes("/dist/");
16651
+ this.catalogClient = this.projectRoot && (builtRuntime || process.env["WRONGSTACK_SESSION_CATALOG_FORCE"] === "1") && resolveSessionCatalogProjectServerUrl() ? new SessionCatalogProjectClient({
16652
+ projectDir: path23.dirname(this.dir),
16653
+ projectRoot: this.projectRoot
16654
+ }) : void 0;
16014
16655
  }
16015
16656
  /**
16016
16657
  * Emit a structured warning. Uses the configured Logger when available;
@@ -16033,10 +16674,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
16033
16674
  clearLoadCache(sessionId) {
16034
16675
  this.loadCache.clear(sessionId);
16035
16676
  }
16677
+ async dispose() {
16678
+ await this.catalogClient?.close();
16679
+ this.clearLoadCache();
16680
+ }
16036
16681
  // ── Storage event helpers ───────────────────────────────────────────────────
16037
16682
  /** Absolute path to the session index file. */
16038
16683
  get indexFile() {
16039
- return path20.join(this.dir, "_index.jsonl");
16684
+ return path23.join(this.dir, "_index.jsonl");
16040
16685
  }
16041
16686
  /** Join session ID to its absolute path within the store directory. */
16042
16687
  sessionPath(id, ext) {
@@ -16094,8 +16739,23 @@ var DefaultSessionStore = class _DefaultSessionStore {
16094
16739
  if (!current) return null;
16095
16740
  return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
16096
16741
  },
16097
- onClose: (s) => this.appendToIndex(s)
16742
+ onClose: (s) => this.persistCatalogSummary(s)
16098
16743
  });
16744
+ if (this.catalogClient) {
16745
+ await this.catalogClient.call("upsert_summary", {
16746
+ summary: {
16747
+ id,
16748
+ title: meta.title ?? "",
16749
+ startedAt,
16750
+ model: meta.model ?? "",
16751
+ provider: meta.provider ?? "",
16752
+ tokenTotal: 0,
16753
+ lastActivityAt: startedAt
16754
+ },
16755
+ transcriptRelativePath: `${id}.jsonl`,
16756
+ summaryRelativePath: `${id}.summary.json`
16757
+ });
16758
+ }
16099
16759
  emitSessionStoreWrite(this.events, id, file, "create", "success", Date.now() - t0);
16100
16760
  return writer;
16101
16761
  } catch (err) {
@@ -16125,6 +16785,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
16125
16785
  return materializeCheckpoint(this.checkpointCas, checkpoint, targetRoot);
16126
16786
  }
16127
16787
  async resolveId(query) {
16788
+ if (this.catalogClient) {
16789
+ return this.catalogClient.call("resolve_id", { query });
16790
+ }
16128
16791
  const normalized = query.trim();
16129
16792
  if (!normalized) throw new Error("Session not found: (empty query)");
16130
16793
  if (normalized) {
@@ -16231,7 +16894,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16231
16894
  // Shard directory (sessions/<date>/) — must match create() so the
16232
16895
  // .summary.json sidecar lands next to the JSONL instead of the
16233
16896
  // sessions root (where summaryFor() would never find it).
16234
- dir: path20.dirname(file),
16897
+ dir: path23.dirname(file),
16235
16898
  filePath: file,
16236
16899
  secretScrubber: this.secretScrubber,
16237
16900
  checkpointCas: this.checkpointCas,
@@ -16242,7 +16905,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16242
16905
  if (!current) return null;
16243
16906
  return current.name === void 0 ? {} : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };
16244
16907
  },
16245
- onClose: (s) => this.appendToIndex(s)
16908
+ onClose: (s) => this.persistCatalogSummary(s)
16246
16909
  }
16247
16910
  );
16248
16911
  emitSessionStoreWrite(this.events, canonicalId, file, "resume", "success", Date.now() - t0);
@@ -16355,6 +17018,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
16355
17018
  });
16356
17019
  }
16357
17020
  async list(limit = 20) {
17021
+ if (this.catalogClient) {
17022
+ const records = await this.catalogClient.call("list_catalog", { limit });
17023
+ return this.scrubSummaries(records);
17024
+ }
16358
17025
  try {
16359
17026
  const indexed = await this.readIndex();
16360
17027
  if (indexed.length > 0) {
@@ -16377,6 +17044,13 @@ var DefaultSessionStore = class _DefaultSessionStore {
16377
17044
  */
16378
17045
  async listFiltered(criteria) {
16379
17046
  const limit = criteria.limit ?? 100;
17047
+ if (this.catalogClient) {
17048
+ const records = await this.catalogClient.call("list_catalog", {
17049
+ limit: Math.min(1e3, Math.max(limit, 100)),
17050
+ ...criteria.titleContains ? { search: criteria.titleContains } : {}
17051
+ });
17052
+ return this.scrubSummaries(records).filter((summary) => matchesSessionFilter(summary, criteria)).slice(0, limit);
17053
+ }
16380
17054
  try {
16381
17055
  const indexed = await this.readIndex();
16382
17056
  if (indexed.length === 0) {
@@ -16426,6 +17100,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
16426
17100
  await this.appendToIndexStrict(summary).catch(() => {
16427
17101
  });
16428
17102
  }
17103
+ /** Final summary boundary: daemon is authoritative when available. */
17104
+ async persistCatalogSummary(summary) {
17105
+ if (!this.catalogClient) {
17106
+ await this.appendToIndex(summary);
17107
+ return;
17108
+ }
17109
+ await this.catalogClient.call("upsert_summary", {
17110
+ summary,
17111
+ transcriptRelativePath: `${summary.id}.jsonl`,
17112
+ summaryRelativePath: `${summary.id}.summary.json`
17113
+ });
17114
+ }
16429
17115
  /** Append a tombstone entry for a deleted session. */
16430
17116
  async writeTombstone(id) {
16431
17117
  try {
@@ -16535,6 +17221,10 @@ var DefaultSessionStore = class _DefaultSessionStore {
16535
17221
  * fresh _index.jsonl. Useful after manual cleanup or index corruption.
16536
17222
  */
16537
17223
  async rebuildIndex() {
17224
+ if (this.catalogClient) {
17225
+ const result = await this.catalogClient.call("rebuild_catalog", {}, { timeoutMs: 12e4 });
17226
+ return result.indexed;
17227
+ }
16538
17228
  const ids = await this.collectSessionIds(this.dir);
16539
17229
  const summaries = await Promise.all(
16540
17230
  ids.map((id) => this.summaryFor(id).catch(() => null))
@@ -16604,7 +17294,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16604
17294
  });
16605
17295
  }
16606
17296
  async collectSessionFilesInShard(shardKey) {
16607
- const dir = shardKey ? path20.join(this.dir, shardKey) : this.dir;
17297
+ const dir = shardKey ? path23.join(this.dir, shardKey) : this.dir;
16608
17298
  const entries = await this.collectSessionFiles(dir, shardKey);
16609
17299
  return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
16610
17300
  }
@@ -16700,10 +17390,35 @@ var DefaultSessionStore = class _DefaultSessionStore {
16700
17390
  await this.writeTombstone(id);
16701
17391
  }
16702
17392
  async delete(id) {
17393
+ if (this.catalogClient) {
17394
+ const canonical = await this.resolveId(id);
17395
+ const lease = await this.catalogClient.call("acquire_maintenance", {
17396
+ sessionId: canonical,
17397
+ operation: "delete",
17398
+ holderId: this.maintenanceHolderId
17399
+ });
17400
+ try {
17401
+ await this.catalogClient.call("delete", { sessionId: canonical, lease });
17402
+ } catch (error2) {
17403
+ await this.catalogClient.call("release_maintenance", { lease }).catch(() => void 0);
17404
+ throw error2;
17405
+ }
17406
+ this.clearLoadCache(canonical);
17407
+ return;
17408
+ }
16703
17409
  await assertSessionCanBeDeleted(id, this.isSessionInUse);
16704
17410
  await this.deleteSession(id);
16705
17411
  }
16706
17412
  async rename(id, name) {
17413
+ if (this.catalogClient) {
17414
+ const canonical = await this.resolveId(id);
17415
+ const summary = await this.catalogClient.call("rename", {
17416
+ sessionId: canonical,
17417
+ name: sessionContentText(this.secretScrubber.scrub(name))
17418
+ });
17419
+ this.clearLoadCache(canonical);
17420
+ return summary;
17421
+ }
16707
17422
  const trimmed = sessionContentText(this.secretScrubber.scrub(name));
16708
17423
  const manifest = this.sessionPath(id, ".summary.json");
16709
17424
  const jsonlPath = this.sessionPath(id, ".jsonl");
@@ -16755,6 +17470,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
16755
17470
  return updated;
16756
17471
  }
16757
17472
  async prune(maxAgeDays = 30) {
17473
+ if (this.catalogClient) {
17474
+ return this.catalogClient.call("prune", {
17475
+ maxAgeDays,
17476
+ holderId: this.maintenanceHolderId
17477
+ });
17478
+ }
16758
17479
  const deleted = await pruneSessionFiles(this.dir, maxAgeDays, (id) => this.deleteSession(id));
16759
17480
  if (deleted > 0) {
16760
17481
  await this.compactIndex().catch(() => void 0);
@@ -16762,20 +17483,85 @@ var DefaultSessionStore = class _DefaultSessionStore {
16762
17483
  return deleted;
16763
17484
  }
16764
17485
  async clearHistory(id) {
16765
- await this.ensureShardDir(id);
16766
- const file = this.sessionPath(id, ".jsonl");
16767
- const meta = this.sessionPath(id, ".summary.json");
17486
+ const canonical = this.catalogClient ? await this.resolveId(id) : id;
17487
+ const maintenance = this.catalogClient ? await this.catalogClient.call("acquire_maintenance", {
17488
+ sessionId: canonical,
17489
+ operation: "clear",
17490
+ holderId: this.maintenanceHolderId,
17491
+ // The catalog store runs inside the detached project daemon; without
17492
+ // our pid it cannot tell "this TUI clearing its own session" from
17493
+ // "another running wstack" and refuses every /clear as `is live`.
17494
+ holderPid: process.pid
17495
+ }) : void 0;
17496
+ await this.ensureShardDir(canonical);
17497
+ const file = this.sessionPath(canonical, ".jsonl");
17498
+ const meta = this.sessionPath(canonical, ".summary.json");
17499
+ const backupSuffix = maintenance ? `.${maintenance.leaseId}.clear-backup` : void 0;
17500
+ const fileBackup = backupSuffix ? `${file}${backupSuffix}` : void 0;
17501
+ const metaBackup = backupSuffix ? `${meta}${backupSuffix}` : void 0;
17502
+ let fileStaged = false;
17503
+ let metaStaged = false;
16768
17504
  const record = `${JSON.stringify({
16769
17505
  type: "session_start",
16770
17506
  ts: (/* @__PURE__ */ new Date()).toISOString(),
16771
- id,
17507
+ id: canonical,
16772
17508
  model: "unknown",
16773
17509
  provider: "unknown"
16774
17510
  })}
16775
17511
  `;
16776
- await atomicWrite(file, record);
16777
- await fsp17.unlink(meta).catch(() => void 0);
16778
- this.clearLoadCache(id);
17512
+ try {
17513
+ if (fileBackup) {
17514
+ try {
17515
+ await fsp17.rename(file, fileBackup);
17516
+ fileStaged = true;
17517
+ } catch (error2) {
17518
+ if (error2.code !== "ENOENT") throw error2;
17519
+ }
17520
+ }
17521
+ if (metaBackup) {
17522
+ try {
17523
+ await fsp17.rename(meta, metaBackup);
17524
+ metaStaged = true;
17525
+ } catch (error2) {
17526
+ if (error2.code !== "ENOENT") throw error2;
17527
+ }
17528
+ }
17529
+ await atomicWrite(file, record);
17530
+ if (!metaBackup) await fsp17.unlink(meta).catch(() => void 0);
17531
+ if (this.catalogClient) {
17532
+ const now = (/* @__PURE__ */ new Date()).toISOString();
17533
+ await this.catalogClient.call("upsert_summary", {
17534
+ summary: {
17535
+ id: canonical,
17536
+ title: "",
17537
+ startedAt: now,
17538
+ model: "unknown",
17539
+ provider: "unknown",
17540
+ tokenTotal: 0,
17541
+ lastActivityAt: now
17542
+ },
17543
+ transcriptRelativePath: `${canonical}.jsonl`,
17544
+ summaryRelativePath: `${canonical}.summary.json`
17545
+ });
17546
+ }
17547
+ if (fileStaged && fileBackup) await fsp17.unlink(fileBackup).catch(() => void 0);
17548
+ if (metaStaged && metaBackup) await fsp17.unlink(metaBackup).catch(() => void 0);
17549
+ } catch (error2) {
17550
+ if (fileStaged && fileBackup) {
17551
+ await fsp17.unlink(file).catch(() => void 0);
17552
+ await fsp17.rename(fileBackup, file).catch(() => void 0);
17553
+ }
17554
+ if (metaStaged && metaBackup) {
17555
+ await fsp17.unlink(meta).catch(() => void 0);
17556
+ await fsp17.rename(metaBackup, meta).catch(() => void 0);
17557
+ }
17558
+ throw error2;
17559
+ } finally {
17560
+ if (maintenance && this.catalogClient) {
17561
+ await this.catalogClient.call("release_maintenance", { lease: maintenance }).catch(() => void 0);
17562
+ }
17563
+ }
17564
+ this.clearLoadCache(canonical);
16779
17565
  }
16780
17566
  async summarize(id, mtime) {
16781
17567
  return summarizeSessionFile({
@@ -16795,9 +17581,9 @@ function makeDirectorSessionFactory(opts) {
16795
17581
  let dir;
16796
17582
  if (opts.store) {
16797
17583
  store = opts.store;
16798
- dir = opts.sessionsRoot ? path21.join(opts.sessionsRoot, runId) : "(caller-managed)";
17584
+ dir = opts.sessionsRoot ? path24.join(opts.sessionsRoot, runId) : "(caller-managed)";
16799
17585
  } else if (opts.sessionsRoot) {
16800
- dir = path21.join(opts.sessionsRoot, runId);
17586
+ dir = path24.join(opts.sessionsRoot, runId);
16801
17587
  store = new DefaultSessionStore({ dir });
16802
17588
  } else {
16803
17589
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -16821,7 +17607,7 @@ function makeDirectorSessionFactory(opts) {
16821
17607
  }
16822
17608
  async function readDirectorSubagentSession(args) {
16823
17609
  if (!args.sessionsRoot) return null;
16824
- const filePath = path21.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
17610
+ const filePath = path24.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
16825
17611
  let raw;
16826
17612
  try {
16827
17613
  raw = await fsp18.readFile(filePath, "utf8");
@@ -17393,7 +18179,7 @@ function formatRole(role) {
17393
18179
  }
17394
18180
 
17395
18181
  // src/coordination/fleet-spawn.ts
17396
- async function spawn2(host, config, priceLookup) {
18182
+ async function spawn3(host, config, priceLookup) {
17397
18183
  if (host.workCompleteFlag) {
17398
18184
  throw new FleetSpawnBudgetError(
17399
18185
  "max_spawns",
@@ -17677,7 +18463,7 @@ function hashStr(s) {
17677
18463
  }
17678
18464
 
17679
18465
  // src/coordination/multi-agent-coordinator.ts
17680
- import { randomUUID as randomUUID10 } from "node:crypto";
18466
+ import { randomUUID as randomUUID11 } from "node:crypto";
17681
18467
  import { EventEmitter as EventEmitter2 } from "node:events";
17682
18468
 
17683
18469
  // src/coordination/coordinator/error-classifier.ts
@@ -17816,12 +18602,12 @@ async function executeSubagentWithTimeout({
17816
18602
  }
17817
18603
  return new Promise((resolveDecision) => {
17818
18604
  let settled = false;
17819
- const resolve19 = (d) => {
18605
+ const resolve21 = (d) => {
17820
18606
  if (settled) return;
17821
18607
  settled = true;
17822
18608
  resolveDecision(d);
17823
18609
  };
17824
- const fallback = setTimeout(() => resolve19("stop"), DECISION_TIMEOUT_MS);
18610
+ const fallback = setTimeout(() => resolve21("stop"), DECISION_TIMEOUT_MS);
17825
18611
  const sessionId = currentSessionId();
17826
18612
  budget._events?.emit("budget.threshold_reached", {
17827
18613
  ...sessionId ? { sessionId } : {},
@@ -17831,11 +18617,11 @@ async function executeSubagentWithTimeout({
17831
18617
  timeoutMs: DECISION_TIMEOUT_MS,
17832
18618
  extend: (extra) => {
17833
18619
  clearTimeout(fallback);
17834
- queueMicrotask(() => resolve19({ extend: extra }));
18620
+ queueMicrotask(() => resolve21({ extend: extra }));
17835
18621
  },
17836
18622
  deny: () => {
17837
18623
  clearTimeout(fallback);
17838
- resolve19("stop");
18624
+ resolve21("stop");
17839
18625
  }
17840
18626
  });
17841
18627
  });
@@ -18047,7 +18833,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18047
18833
  return { ...subagent, name: display };
18048
18834
  }
18049
18835
  async spawn(subagent) {
18050
- const id = subagent.id || randomUUID10();
18836
+ const id = subagent.id || randomUUID11();
18051
18837
  const cfg = this.withNickname(subagent, id);
18052
18838
  if (this.subagents.has(id)) {
18053
18839
  throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
@@ -18222,7 +19008,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18222
19008
  taskIds.map((id) => {
18223
19009
  const cached = this.completedResults.find((r) => r.taskId === id);
18224
19010
  if (cached) return cached;
18225
- return new Promise((resolve19, reject) => {
19011
+ return new Promise((resolve21, reject) => {
18226
19012
  const timeout = setTimeout(() => {
18227
19013
  this.off("task.completed", handler);
18228
19014
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -18231,7 +19017,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18231
19017
  if (result.taskId === id) {
18232
19018
  clearTimeout(timeout);
18233
19019
  this.off("task.completed", handler);
18234
- resolve19(result);
19020
+ resolve21(result);
18235
19021
  }
18236
19022
  };
18237
19023
  this.on("task.completed", handler);
@@ -18256,13 +19042,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18256
19042
  const done = new Set(completed.map((r) => r.taskId));
18257
19043
  return { completed, pending: taskIds.filter((id) => !done.has(id)) };
18258
19044
  }
18259
- return new Promise((resolve19) => {
19045
+ return new Promise((resolve21) => {
18260
19046
  let timer;
18261
19047
  const handler = ({ result }) => {
18262
19048
  if (!ids.has(result.taskId)) return;
18263
19049
  if (timer) clearTimeout(timer);
18264
19050
  this.off("task.completed", handler);
18265
- resolve19({
19051
+ resolve21({
18266
19052
  completed: [result],
18267
19053
  pending: taskIds.filter((id) => id !== result.taskId)
18268
19054
  });
@@ -18270,7 +19056,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
18270
19056
  if (opts?.timeoutMs !== void 0) {
18271
19057
  timer = setTimeout(() => {
18272
19058
  this.off("task.completed", handler);
18273
- resolve19({ completed: [], pending: [...taskIds], timedOut: true });
19059
+ resolve21({ completed: [], pending: [...taskIds], timedOut: true });
18274
19060
  }, opts.timeoutMs);
18275
19061
  }
18276
19062
  this.on("task.completed", handler);
@@ -18932,7 +19718,7 @@ var Director = class _Director {
18932
19718
  sessionProvider;
18933
19719
  sessionModel;
18934
19720
  constructor(opts) {
18935
- this.id = opts.config.coordinatorId || randomUUID11();
19721
+ this.id = opts.config.coordinatorId || randomUUID12();
18936
19722
  this.manifestPath = opts.manifestPath;
18937
19723
  this.roster = opts.roster;
18938
19724
  this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
@@ -19194,7 +19980,7 @@ var Director = class _Director {
19194
19980
  }
19195
19981
  const config = { ...callerConfig };
19196
19982
  this.resolveSpawnModel(config);
19197
- const subagentId = await spawn2(this, config, priceLookup);
19983
+ const subagentId = await spawn3(this, config, priceLookup);
19198
19984
  const perSubagentIdleMs = typeof config.idleTimeoutMs === "number" && Number.isFinite(config.idleTimeoutMs) && config.idleTimeoutMs >= 0 ? config.idleTimeoutMs : this.subagentIdleTimeoutMs;
19199
19985
  this.armSubagentIdleRetirement(subagentId, perSubagentIdleMs);
19200
19986
  return subagentId;
@@ -19215,7 +20001,7 @@ var Director = class _Director {
19215
20001
  );
19216
20002
  }
19217
20003
  const msg = {
19218
- id: randomUUID11(),
20004
+ id: randomUUID12(),
19219
20005
  type: "task",
19220
20006
  from: this.id,
19221
20007
  to: subagentId,
@@ -21135,7 +21921,7 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
21135
21921
 
21136
21922
  // src/infrastructure/logger.ts
21137
21923
  import * as fsp20 from "node:fs/promises";
21138
- import * as path22 from "node:path";
21924
+ import * as path25 from "node:path";
21139
21925
  var LEVEL_RANK2 = {
21140
21926
  error: 0,
21141
21927
  warn: 1,
@@ -21206,7 +21992,7 @@ var DefaultLogger = class _DefaultLogger {
21206
21992
  this.stderr = opts.stderr !== false;
21207
21993
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
21208
21994
  if (this.file) {
21209
- const dir = path22.dirname(this.file);
21995
+ const dir = path25.dirname(this.file);
21210
21996
  this._tail = this._tail.then(async () => {
21211
21997
  await fsp20.mkdir(dir, { recursive: true });
21212
21998
  }).catch(() => void 0);
@@ -21964,10 +22750,10 @@ var KNOWN_TOKEN_GROUPS = {
21964
22750
  var KNOWN_TOKEN_NAMES = Object.values(KNOWN_TOKEN_GROUPS).flat();
21965
22751
 
21966
22752
  // src/execution/design-kit-loader.ts
21967
- import { existsSync as existsSync3 } from "node:fs";
21968
- import * as fs3 from "node:fs/promises";
21969
- import * as path23 from "node:path";
21970
- import { fileURLToPath as fileURLToPath3 } from "node:url";
22753
+ import { existsSync as existsSync4 } from "node:fs";
22754
+ import * as fs4 from "node:fs/promises";
22755
+ import * as path26 from "node:path";
22756
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
21971
22757
  var KIT_FILE = "KIT.md";
21972
22758
  var TOKENS_FILE = "tokens.json";
21973
22759
  var FOUNDATIONS_ID = "_foundations";
@@ -22066,15 +22852,15 @@ var DefaultDesignKitLoader = class {
22066
22852
  for (const { dir, source } of this.dirs) {
22067
22853
  let entries;
22068
22854
  try {
22069
- entries = await fs3.readdir(dir, { withFileTypes: true });
22855
+ entries = await fs4.readdir(dir, { withFileTypes: true });
22070
22856
  } catch {
22071
22857
  continue;
22072
22858
  }
22073
22859
  for (const e of entries) {
22074
22860
  if (!e.isDirectory()) continue;
22075
- const kitFile = path23.join(dir, e.name, KIT_FILE);
22861
+ const kitFile = path26.join(dir, e.name, KIT_FILE);
22076
22862
  try {
22077
- const raw = await fs3.readFile(kitFile, "utf8");
22863
+ const raw = await fs4.readFile(kitFile, "utf8");
22078
22864
  const fm = parseKitFrontmatter(raw);
22079
22865
  const id = fm.id ?? e.name;
22080
22866
  if (!fm.name) continue;
@@ -22135,7 +22921,7 @@ var DefaultDesignKitLoader = class {
22135
22921
  if (cached !== void 0) return cached;
22136
22922
  const m = await this.find(id);
22137
22923
  if (!m) throw new Error(`Design kit "${id}" not found`);
22138
- const raw = await fs3.readFile(m.path, "utf8");
22924
+ const raw = await fs4.readFile(m.path, "utf8");
22139
22925
  const body = narrowStackSections(stripFrontmatter(raw), stack);
22140
22926
  this.bodyCache.set(key, body);
22141
22927
  return body;
@@ -22147,9 +22933,9 @@ var DefaultDesignKitLoader = class {
22147
22933
  const m = await this.find(id);
22148
22934
  let tokens;
22149
22935
  if (m) {
22150
- const tokensPath = path23.join(path23.dirname(m.path), TOKENS_FILE);
22936
+ const tokensPath = path26.join(path26.dirname(m.path), TOKENS_FILE);
22151
22937
  try {
22152
- const raw = await fs3.readFile(tokensPath, "utf8");
22938
+ const raw = await fs4.readFile(tokensPath, "utf8");
22153
22939
  tokens = JSON.parse(raw);
22154
22940
  } catch {
22155
22941
  tokens = void 0;
@@ -22209,15 +22995,15 @@ function mergeKitTokens(base, own) {
22209
22995
  }
22210
22996
  function resolveBundledDesignKitsDir() {
22211
22997
  try {
22212
- const here = path23.dirname(fileURLToPath3(import.meta.url));
22998
+ const here = path26.dirname(fileURLToPath4(import.meta.url));
22213
22999
  const candidates = [
22214
- path23.join(here, "design-kits"),
22215
- path23.join(here, "..", "design-kits"),
22216
- path23.join(here, "..", "..", "design-kits"),
22217
- path23.join(here, "..", "..", "..", "design-kits")
23000
+ path26.join(here, "design-kits"),
23001
+ path26.join(here, "..", "design-kits"),
23002
+ path26.join(here, "..", "..", "design-kits"),
23003
+ path26.join(here, "..", "..", "..", "design-kits")
22218
23004
  ];
22219
23005
  for (const c of candidates) {
22220
- if (existsSync3(c)) return c;
23006
+ if (existsSync4(c)) return c;
22221
23007
  }
22222
23008
  } catch {
22223
23009
  }
@@ -22251,12 +23037,12 @@ function _resetDesignKitLoaderMemo() {
22251
23037
  }
22252
23038
 
22253
23039
  // src/execution/design-project-store.ts
22254
- import { existsSync as existsSync4 } from "node:fs";
22255
- import * as fs4 from "node:fs/promises";
22256
- import * as path24 from "node:path";
23040
+ import { existsSync as existsSync5 } from "node:fs";
23041
+ import * as fs5 from "node:fs/promises";
23042
+ import * as path27 from "node:path";
22257
23043
  var DESIGN_DIR = ".design";
22258
23044
  function designProjectDir(projectRoot) {
22259
- return path24.join(projectRoot, DESIGN_DIR);
23045
+ return path27.join(projectRoot, DESIGN_DIR);
22260
23046
  }
22261
23047
  var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
22262
23048
  var rulesCache = /* @__PURE__ */ new Map();
@@ -22271,7 +23057,7 @@ async function loadProjectDesignRules(projectRoot) {
22271
23057
  let rules;
22272
23058
  for (const name of RULE_FILES) {
22273
23059
  try {
22274
- const txt = await fs4.readFile(path24.join(designProjectDir(projectRoot), name), "utf8");
23060
+ const txt = await fs5.readFile(path27.join(designProjectDir(projectRoot), name), "utf8");
22275
23061
  if (txt.trim()) {
22276
23062
  rules = txt.trim();
22277
23063
  break;
@@ -22297,7 +23083,7 @@ function parseOverrides(value) {
22297
23083
  }
22298
23084
  async function loadActiveKit(projectRoot) {
22299
23085
  try {
22300
- const raw = await fs4.readFile(path24.join(designProjectDir(projectRoot), "active.json"), "utf8");
23086
+ const raw = await fs5.readFile(path27.join(designProjectDir(projectRoot), "active.json"), "utf8");
22301
23087
  const parsed = JSON.parse(raw);
22302
23088
  if (parsed && typeof parsed.kit === "string") {
22303
23089
  return {
@@ -22330,11 +23116,11 @@ function applyTokenOverrides(tokens, overrides) {
22330
23116
  }
22331
23117
  async function ensureDesignDir(projectRoot) {
22332
23118
  const dir = designProjectDir(projectRoot);
22333
- await fs4.mkdir(dir, { recursive: true });
22334
- const gi = path24.join(dir, ".gitignore");
22335
- if (!existsSync4(gi)) {
23119
+ await fs5.mkdir(dir, { recursive: true });
23120
+ const gi = path27.join(dir, ".gitignore");
23121
+ if (!existsSync5(gi)) {
22336
23122
  try {
22337
- await fs4.writeFile(gi, "*\n");
23123
+ await fs5.writeFile(gi, "*\n");
22338
23124
  } catch {
22339
23125
  }
22340
23126
  }
@@ -22345,11 +23131,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
22345
23131
  const dir = await ensureDesignDir(projectRoot);
22346
23132
  const record = { kit, stack: stack ?? null };
22347
23133
  if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
22348
- await fs4.writeFile(path24.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
23134
+ await fs5.writeFile(path27.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22349
23135
  `);
22350
23136
  const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
22351
23137
  `;
22352
- await fs4.appendFile(path24.join(dir, "decisions.md"), line);
23138
+ await fs5.appendFile(path27.join(dir, "decisions.md"), line);
22353
23139
  } catch {
22354
23140
  }
22355
23141
  }
@@ -22365,11 +23151,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22365
23151
  const dir = await ensureDesignDir(projectRoot);
22366
23152
  const record = { kit: active.kit, stack: active.stack ?? null };
22367
23153
  if (Object.keys(merged).length > 0) record.overrides = merged;
22368
- await fs4.writeFile(path24.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
23154
+ await fs5.writeFile(path27.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22369
23155
  `);
22370
23156
  const keys = Object.keys(patch).join(",");
22371
- await fs4.appendFile(
22372
- path24.join(dir, "decisions.md"),
23157
+ await fs5.appendFile(
23158
+ path27.join(dir, "decisions.md"),
22373
23159
  `- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
22374
23160
  `
22375
23161
  );
@@ -22379,7 +23165,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22379
23165
  }
22380
23166
  async function clearPersistedActiveKit(projectRoot) {
22381
23167
  try {
22382
- await fs4.rm(path24.join(designProjectDir(projectRoot), "active.json"), { force: true });
23168
+ await fs5.rm(path27.join(designProjectDir(projectRoot), "active.json"), { force: true });
22383
23169
  } catch {
22384
23170
  }
22385
23171
  }
@@ -22519,12 +23305,12 @@ function verifyFiles(tokens, files) {
22519
23305
  const violations = [];
22520
23306
  let onPalette = 0;
22521
23307
  let offPalette = 0;
22522
- for (const { path: path42, text } of files) {
23308
+ for (const { path: path45, text } of files) {
22523
23309
  const lines = text.split("\n");
22524
23310
  lines.forEach((lineText, i) => {
22525
23311
  const lineNo = i + 1;
22526
23312
  const flag = (snippet, reason, axis = "color") => {
22527
- violations.push({ file: path42, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
23313
+ violations.push({ file: path45, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
22528
23314
  };
22529
23315
  for (const re of [HEX_RE, FUNC_COLOR_RE]) {
22530
23316
  re.lastIndex = 0;
@@ -22591,14 +23377,14 @@ var SKIP_DIR = /* @__PURE__ */ new Set([
22591
23377
  ".design"
22592
23378
  ]);
22593
23379
  async function walkUiFiles(root, max = 200) {
22594
- const { default: fs15 } = await import("node:fs/promises");
23380
+ const { default: fs16 } = await import("node:fs/promises");
22595
23381
  const { default: nodePath } = await import("node:path");
22596
23382
  const found = [];
22597
23383
  async function rec(dir, depth) {
22598
23384
  if (found.length >= max || depth > 8) return;
22599
23385
  let entries;
22600
23386
  try {
22601
- entries = await fs15.readdir(dir, { withFileTypes: true });
23387
+ entries = await fs16.readdir(dir, { withFileTypes: true });
22602
23388
  } catch {
22603
23389
  return;
22604
23390
  }
@@ -22616,13 +23402,13 @@ async function walkUiFiles(root, max = 200) {
22616
23402
  return found;
22617
23403
  }
22618
23404
  async function runDesignVerify(projectRoot, tokens, explicitFiles) {
22619
- const { default: fs15 } = await import("node:fs/promises");
23405
+ const { default: fs16 } = await import("node:fs/promises");
22620
23406
  const { default: nodePath } = await import("node:path");
22621
23407
  const abs = explicitFiles && explicitFiles.length > 0 ? explicitFiles.map((f) => nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)) : await walkUiFiles(projectRoot);
22622
23408
  const files = [];
22623
23409
  for (const a of abs) {
22624
23410
  try {
22625
- files.push({ path: nodePath.relative(projectRoot, a), text: await fs15.readFile(a, "utf8") });
23411
+ files.push({ path: nodePath.relative(projectRoot, a), text: await fs16.readFile(a, "utf8") });
22626
23412
  } catch {
22627
23413
  }
22628
23414
  }
@@ -22753,10 +23539,10 @@ function makeDesignVerifyToolCallMiddleware() {
22753
23539
  const p = typeof input?.path === "string" ? input.path : "";
22754
23540
  if (!p || !detectFrontendFile(p)) return out;
22755
23541
  const ctx = out.ctx;
22756
- const { default: fs15 } = await import("node:fs/promises");
23542
+ const { default: fs16 } = await import("node:fs/promises");
22757
23543
  const { default: nodePath } = await import("node:path");
22758
23544
  const abs = nodePath.isAbsolute(p) ? p : nodePath.join(ctx.projectRoot, p);
22759
- const text = await fs15.readFile(abs, "utf8").catch(() => "");
23545
+ const text = await fs16.readFile(abs, "utf8").catch(() => "");
22760
23546
  if (!text) return out;
22761
23547
  const loader = getDesignKitLoader(ctx.projectRoot);
22762
23548
  const rawTokens = await loader.readTokens(state.activeKit);
@@ -23205,11 +23991,11 @@ function buildRecoveryStrategies(opts) {
23205
23991
  async attempt(err, ctx) {
23206
23992
  if (!(err instanceof ProviderError) || err.kind !== "rate_limit") return null;
23207
23993
  const delayMs = err.body?.retryAfterMs ?? 5e3;
23208
- const delay = Math.min(6e4, Math.max(1e3, delayMs));
23994
+ const delay2 = Math.min(6e4, Math.max(1e3, delayMs));
23209
23995
  if (ctx?.signal) {
23210
- await new Promise((resolve19) => {
23996
+ await new Promise((resolve21) => {
23211
23997
  if (ctx.signal.aborted) {
23212
- resolve19();
23998
+ resolve21();
23213
23999
  return;
23214
24000
  }
23215
24001
  let settled = false;
@@ -23220,13 +24006,13 @@ function buildRecoveryStrategies(opts) {
23220
24006
  settled = true;
23221
24007
  if (timer !== void 0) clearTimeout(timer);
23222
24008
  ctx.signal.removeEventListener("abort", onAbort);
23223
- resolve19();
24009
+ resolve21();
23224
24010
  };
23225
- timer = setTimeout(finish, delay);
24011
+ timer = setTimeout(finish, delay2);
23226
24012
  ctx.signal.addEventListener("abort", onAbort, { once: true });
23227
24013
  });
23228
24014
  } else {
23229
- await new Promise((r) => setTimeout(r, delay));
24015
+ await new Promise((r) => setTimeout(r, delay2));
23230
24016
  }
23231
24017
  return { action: "retry", reason: "rate_limit_backoff" };
23232
24018
  }
@@ -23856,10 +24642,10 @@ var EternalAutonomyEngine = class {
23856
24642
  if (status === "failure") {
23857
24643
  if (isTransientFailure) {
23858
24644
  this.consecutiveTransientRetries++;
23859
- const delay = this.computeTransientBackoffMs();
23860
- if (delay > 0) {
23861
- emit2({ phase: "sleep", ms: delay });
23862
- await this.sleepInterruptible(delay);
24645
+ const delay2 = this.computeTransientBackoffMs();
24646
+ if (delay2 > 0) {
24647
+ emit2({ phase: "sleep", ms: delay2 });
24648
+ await this.sleepInterruptible(delay2);
23863
24649
  }
23864
24650
  return false;
23865
24651
  }
@@ -24361,7 +25147,7 @@ function buildGoalPreamble(goal, deliverables) {
24361
25147
  }
24362
25148
 
24363
25149
  // src/execution/compaction-summary-cache.ts
24364
- import { createHash as createHash5 } from "node:crypto";
25150
+ import { createHash as createHash6 } from "node:crypto";
24365
25151
 
24366
25152
  // src/utils/lru-cache.ts
24367
25153
  var LruCache = class {
@@ -24499,7 +25285,7 @@ function semanticBlock(block) {
24499
25285
  }
24500
25286
  }
24501
25287
  function compactionSummaryKey(model, prompt, messages) {
24502
- const hash2 = createHash5("sha256");
25288
+ const hash2 = createHash6("sha256");
24503
25289
  hash2.update("wrongstack-compaction-summary-v2\0").update(model).update("\0").update(prompt);
24504
25290
  for (const message of messages) {
24505
25291
  hash2.update("\0").update(message.role).update("\0");
@@ -24713,7 +25499,7 @@ ${summaryText}` : summaryText;
24713
25499
  };
24714
25500
 
24715
25501
  // src/execution/parallel-eternal-engine.ts
24716
- import { randomUUID as randomUUID12 } from "node:crypto";
25502
+ import { randomUUID as randomUUID13 } from "node:crypto";
24717
25503
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
24718
25504
  var ParallelEternalEngine = class {
24719
25505
  constructor(opts) {
@@ -24790,7 +25576,7 @@ var ParallelEternalEngine = class {
24790
25576
  this.state = "running";
24791
25577
  await this.persistState("running");
24792
25578
  const config = {
24793
- coordinatorId: `parallel-${randomUUID12().slice(0, 8)}`,
25579
+ coordinatorId: `parallel-${randomUUID13().slice(0, 8)}`,
24794
25580
  maxConcurrent: this.slots,
24795
25581
  doneCondition: { type: "all_tasks_done" }
24796
25582
  };
@@ -24844,7 +25630,7 @@ var ParallelEternalEngine = class {
24844
25630
  }
24845
25631
  if (!this.coordinator) {
24846
25632
  const config = {
24847
- coordinatorId: `parallel-${randomUUID12().slice(0, 8)}`,
25633
+ coordinatorId: `parallel-${randomUUID13().slice(0, 8)}`,
24848
25634
  maxConcurrent: this.slots,
24849
25635
  doneCondition: { type: "all_tasks_done" }
24850
25636
  };
@@ -24930,7 +25716,7 @@ ${recentJournal}` : "No prior iterations.",
24930
25716
  const task = expectDefined(tasks[i]);
24931
25717
  const route = routes[i] ?? null;
24932
25718
  const subagentId = `parallel-${this.iterations}-${i}`;
24933
- const taskId = randomUUID12();
25719
+ const taskId = randomUUID13();
24934
25720
  const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
24935
25721
  ` : "";
24936
25722
  const spec = {
@@ -25145,7 +25931,7 @@ ${lastFew}` : "No prior iterations.",
25145
25931
  };
25146
25932
 
25147
25933
  // src/core/streaming-response-builder.ts
25148
- import { randomUUID as randomUUID13 } from "node:crypto";
25934
+ import { randomUUID as randomUUID14 } from "node:crypto";
25149
25935
  var STREAM_DRAIN_TIMEOUT_MS = 500;
25150
25936
  function buildResponse(state) {
25151
25937
  const content = [];
@@ -25205,7 +25991,7 @@ function handleContentBlockStart(state, ev) {
25205
25991
  state.textBuffers.push("");
25206
25992
  state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
25207
25993
  } else if (kind === "tool_use") {
25208
- const id = ev.id ?? randomUUID13();
25994
+ const id = ev.id ?? randomUUID14();
25209
25995
  state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
25210
25996
  state.blockOrder.push({ kind: "tool", id });
25211
25997
  state.currentTextIndex = -1;
@@ -25414,8 +26200,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
25414
26200
  });
25415
26201
  await Promise.race([
25416
26202
  drainPromise,
25417
- new Promise((resolve19) => {
25418
- drainTimer = setTimeout(resolve19, STREAM_DRAIN_TIMEOUT_MS);
26203
+ new Promise((resolve21) => {
26204
+ drainTimer = setTimeout(resolve21, STREAM_DRAIN_TIMEOUT_MS);
25419
26205
  })
25420
26206
  ]);
25421
26207
  } finally {
@@ -25439,7 +26225,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
25439
26225
  }
25440
26226
 
25441
26227
  // src/chronicle/prompt-manifest.ts
25442
- import { createHash as createHash6 } from "node:crypto";
26228
+ import { createHash as createHash7 } from "node:crypto";
25443
26229
  function createChroniclePromptManifest(request) {
25444
26230
  const roleCounts = {}, blockCounts = {};
25445
26231
  const messages = request.messages.map((message, index) => messageSummary(message, index, roleCounts, blockCounts));
@@ -25526,11 +26312,11 @@ function countBlocks(blocks) {
25526
26312
  return counts;
25527
26313
  }
25528
26314
  function hash(value) {
25529
- return createHash6("sha256").update(value).digest("hex");
26315
+ return createHash7("sha256").update(value).digest("hex");
25530
26316
  }
25531
26317
  function contentIdentity(content) {
25532
26318
  if (typeof content === "string") return { hash: hash(content), bytes: Buffer.byteLength(content) };
25533
- const digest = createHash6("sha256");
26319
+ const digest = createHash7("sha256");
25534
26320
  let bytes = 0;
25535
26321
  const add = (value) => {
25536
26322
  if (!value) return;
@@ -25612,7 +26398,7 @@ function replaceAllCaseInsensitive(haystack, needle, replacement) {
25612
26398
  }
25613
26399
 
25614
26400
  // src/core/provider-runner.ts
25615
- import { randomUUID as randomUUID14 } from "node:crypto";
26401
+ import { randomUUID as randomUUID15 } from "node:crypto";
25616
26402
  function scrubProviderBody(body) {
25617
26403
  if (!body) return void 0;
25618
26404
  return {
@@ -25634,11 +26420,11 @@ function providerLogCtx(p, r) {
25634
26420
  }
25635
26421
  async function runProviderWithRetry(opts) {
25636
26422
  const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
25637
- const logicalRequestId = randomUUID14();
26423
+ const logicalRequestId = randomUUID15();
25638
26424
  const promptManifest = createChroniclePromptManifest(request);
25639
26425
  let attempt = 0;
25640
26426
  for (; ; ) {
25641
- const attemptId = randomUUID14();
26427
+ const attemptId = randomUUID15();
25642
26428
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
25643
26429
  const startedNs = process.hrtime.bigint();
25644
26430
  const correlation = {
@@ -25711,7 +26497,7 @@ async function runProviderWithRetry(opts) {
25711
26497
  const description = scrubErrorText(
25712
26498
  isProviderErr ? err.describe() : errAsErr.message
25713
26499
  );
25714
- const delay = canRetry ? Math.round(retry.delayMs(attempt, isProviderErr ? err : errAsErr)) : void 0;
26500
+ const delay2 = canRetry ? Math.round(retry.delayMs(attempt, isProviderErr ? err : errAsErr)) : void 0;
25715
26501
  events.emit("provider.attempt.failed", {
25716
26502
  ...correlation,
25717
26503
  startedAt,
@@ -25722,7 +26508,7 @@ async function runProviderWithRetry(opts) {
25722
26508
  description,
25723
26509
  retryable: canRetry,
25724
26510
  retryScheduled: canRetry,
25725
- ...delay !== void 0 ? { retryDelayMs: delay } : {},
26511
+ ...delay2 !== void 0 ? { retryDelayMs: delay2 } : {},
25726
26512
  ...providerErrorBody?.requestId ? { providerRequestId: providerErrorBody.requestId } : {},
25727
26513
  ...providerErrorBody ? { errorBody: providerErrorBody } : {}
25728
26514
  });
@@ -25748,11 +26534,11 @@ async function runProviderWithRetry(opts) {
25748
26534
  }
25749
26535
  const attemptNum = attempt + 1;
25750
26536
  const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
25751
- logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
26537
+ logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay2}ms \u2014 ${description}`, {
25752
26538
  ...providerLogCtx(provider, request),
25753
26539
  attempt: attemptNum,
25754
26540
  maxAttempts,
25755
- delayMs: delay,
26541
+ delayMs: delay2,
25756
26542
  errorDescription: description,
25757
26543
  status: isProviderErr ? err.status : void 0,
25758
26544
  errorBody: providerErrorBody
@@ -25761,12 +26547,12 @@ async function runProviderWithRetry(opts) {
25761
26547
  sessionId: resolveEventSessionId(ctx),
25762
26548
  providerId: isProviderErr ? err.providerId : provider.id,
25763
26549
  attempt: attemptNum,
25764
- delayMs: delay,
26550
+ delayMs: delay2,
25765
26551
  status: isProviderErr ? err.status : 0,
25766
26552
  description,
25767
26553
  ...providerErrorBody ? { errorBody: providerErrorBody } : {}
25768
26554
  });
25769
- await new Promise((resolve19, reject) => {
26555
+ await new Promise((resolve21, reject) => {
25770
26556
  let settled = false;
25771
26557
  const cleanup = () => {
25772
26558
  clearTimeout(t);
@@ -25782,8 +26568,8 @@ async function runProviderWithRetry(opts) {
25782
26568
  if (settled) return;
25783
26569
  settled = true;
25784
26570
  cleanup();
25785
- resolve19();
25786
- }, delay);
26571
+ resolve21();
26572
+ }, delay2);
25787
26573
  if (signal.aborted) {
25788
26574
  onAbort();
25789
26575
  return;
@@ -26406,8 +27192,8 @@ Summarize the following message range:`;
26406
27192
  };
26407
27193
 
26408
27194
  // src/execution/skill-loader.ts
26409
- import * as fs5 from "node:fs/promises";
26410
- import * as path25 from "node:path";
27195
+ import * as fs6 from "node:fs/promises";
27196
+ import * as path28 from "node:path";
26411
27197
 
26412
27198
  // src/skills/foreign-sources.ts
26413
27199
  var FOREIGN_SKILL_TOOLS = [
@@ -26471,7 +27257,7 @@ async function entryIsDirectory(dir, entry) {
26471
27257
  if (entry.isDirectory()) return true;
26472
27258
  if (entry.isSymbolicLink()) {
26473
27259
  try {
26474
- return (await fs5.stat(path25.join(dir, entry.name))).isDirectory();
27260
+ return (await fs6.stat(path28.join(dir, entry.name))).isDirectory();
26475
27261
  } catch {
26476
27262
  return false;
26477
27263
  }
@@ -26495,7 +27281,7 @@ var DefaultSkillLoader = class {
26495
27281
  for (const tool of FOREIGN_SKILL_TOOLS) {
26496
27282
  if (!foreignIds.includes(tool.id)) continue;
26497
27283
  dirs.push({
26498
- dir: path25.join(root, "." + tool.id, tool.subdir),
27284
+ dir: path28.join(root, "." + tool.id, tool.subdir),
26499
27285
  source: "foreign",
26500
27286
  originTool: tool.id
26501
27287
  });
@@ -26520,15 +27306,15 @@ var DefaultSkillLoader = class {
26520
27306
  this.shadowed = [];
26521
27307
  for (const { dir, source, originTool } of this.dirs) {
26522
27308
  try {
26523
- const entries = (await fs5.readdir(dir, { withFileTypes: true })).sort(
27309
+ const entries = (await fs6.readdir(dir, { withFileTypes: true })).sort(
26524
27310
  (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0
26525
27311
  );
26526
27312
  for (const e of entries) {
26527
27313
  if (!await entryIsDirectory(dir, e)) continue;
26528
- const skillFile = path25.join(dir, e.name, "SKILL.md");
27314
+ const skillFile = path28.join(dir, e.name, "SKILL.md");
26529
27315
  let raw;
26530
27316
  try {
26531
- raw = await fs5.readFile(skillFile, "utf8");
27317
+ raw = await fs6.readFile(skillFile, "utf8");
26532
27318
  } catch {
26533
27319
  continue;
26534
27320
  }
@@ -26647,7 +27433,7 @@ var DefaultSkillLoader = class {
26647
27433
  if (cached !== void 0) return cached;
26648
27434
  const m = await this.find(name);
26649
27435
  if (!m) throw new Error(`Skill "${name}" not found`);
26650
- const body = await fs5.readFile(m.path, "utf8");
27436
+ const body = await fs6.readFile(m.path, "utf8");
26651
27437
  this.bodyCache.set(key, body);
26652
27438
  return body;
26653
27439
  }
@@ -26657,12 +27443,12 @@ var DefaultSkillLoader = class {
26657
27443
  if (cached !== void 0) return cached;
26658
27444
  const m = await this.find(name);
26659
27445
  if (!m) throw new Error(`Skill "${name}" not found`);
26660
- const savePath = path25.join(path25.dirname(m.path), "SKILL.save.md");
27446
+ const savePath = path28.join(path28.dirname(m.path), "SKILL.save.md");
26661
27447
  let result;
26662
27448
  try {
26663
- result = await fs5.readFile(savePath, "utf8");
27449
+ result = await fs6.readFile(savePath, "utf8");
26664
27450
  } catch {
26665
- const full = await fs5.readFile(m.path, "utf8");
27451
+ const full = await fs6.readFile(m.path, "utf8");
26666
27452
  const body = stripFrontmatter(full);
26667
27453
  const compact = compactSkillBody(body);
26668
27454
  if (compact) {
@@ -26690,12 +27476,12 @@ function parseDescriptionFromText(desc) {
26690
27476
  }
26691
27477
 
26692
27478
  // src/execution/prompt-loader.ts
26693
- import * as fs7 from "node:fs/promises";
26694
- import * as path27 from "node:path";
27479
+ import * as fs8 from "node:fs/promises";
27480
+ import * as path30 from "node:path";
26695
27481
 
26696
27482
  // src/storage/prompt-store.ts
26697
- import * as fs6 from "node:fs/promises";
26698
- import * as path26 from "node:path";
27483
+ import * as fs7 from "node:fs/promises";
27484
+ import * as path29 from "node:path";
26699
27485
  var SCHEMA_VERSION = 2;
26700
27486
  function migratePromptEntry(raw) {
26701
27487
  if (!raw || typeof raw !== "object") return null;
@@ -26756,12 +27542,12 @@ var DefaultPromptStore = class {
26756
27542
  await ensureDir(this.dir);
26757
27543
  const entries = [];
26758
27544
  try {
26759
- const files = await fs6.readdir(this.dir);
27545
+ const files = await fs7.readdir(this.dir);
26760
27546
  for (const file of files) {
26761
27547
  if (!file.endsWith(".json")) continue;
26762
27548
  try {
26763
27549
  const raw = JSON.parse(
26764
- await fs6.readFile(path26.join(this.dir, file), "utf8")
27550
+ await fs7.readFile(path29.join(this.dir, file), "utf8")
26765
27551
  );
26766
27552
  const migrated = migratePromptEntry(raw.entry);
26767
27553
  if (migrated) entries.push(migrated);
@@ -26775,9 +27561,9 @@ var DefaultPromptStore = class {
26775
27561
  );
26776
27562
  }
26777
27563
  async get(id) {
26778
- const file = path26.join(this.dir, `${id}.json`);
27564
+ const file = path29.join(this.dir, `${id}.json`);
26779
27565
  try {
26780
- const raw = JSON.parse(await fs6.readFile(file, "utf8"));
27566
+ const raw = JSON.parse(await fs7.readFile(file, "utf8"));
26781
27567
  return migratePromptEntry(raw.entry);
26782
27568
  } catch {
26783
27569
  return null;
@@ -26785,14 +27571,14 @@ var DefaultPromptStore = class {
26785
27571
  }
26786
27572
  async save(entry) {
26787
27573
  await ensureDir(this.dir);
26788
- const file = path26.join(this.dir, `${entry.id}.json`);
27574
+ const file = path29.join(this.dir, `${entry.id}.json`);
26789
27575
  const raw = { version: SCHEMA_VERSION, entry };
26790
27576
  await atomicWrite(file, JSON.stringify(raw, null, 2));
26791
27577
  }
26792
27578
  async delete(id) {
26793
- const file = path26.join(this.dir, `${id}.json`);
27579
+ const file = path29.join(this.dir, `${id}.json`);
26794
27580
  try {
26795
- await fs6.unlink(file);
27581
+ await fs7.unlink(file);
26796
27582
  return true;
26797
27583
  } catch {
26798
27584
  return false;
@@ -26879,7 +27665,7 @@ var DefaultPromptLoader = class {
26879
27665
  constructor(opts) {
26880
27666
  this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
26881
27667
  this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
26882
- this.builtinDir = opts.bundledDir ? path27.join(opts.bundledDir, "prompts") : void 0;
27668
+ this.builtinDir = opts.bundledDir ? path30.join(opts.bundledDir, "prompts") : void 0;
26883
27669
  }
26884
27670
  async list() {
26885
27671
  if (this.cache) return this.cache;
@@ -26992,7 +27778,7 @@ var DefaultPromptLoader = class {
26992
27778
  const files = await walkJson(dir);
26993
27779
  for (const file of files) {
26994
27780
  try {
26995
- const parsed = JSON.parse(await fs7.readFile(file, "utf8"));
27781
+ const parsed = JSON.parse(await fs8.readFile(file, "utf8"));
26996
27782
  const migrated = migratePromptEntry(parsed);
26997
27783
  if (migrated) out.push({ ...migrated, source: "builtin" });
26998
27784
  } catch {
@@ -27006,12 +27792,12 @@ async function walkJson(dir) {
27006
27792
  const out = [];
27007
27793
  let entries;
27008
27794
  try {
27009
- entries = await fs7.readdir(dir, { withFileTypes: true });
27795
+ entries = await fs8.readdir(dir, { withFileTypes: true });
27010
27796
  } catch {
27011
27797
  return out;
27012
27798
  }
27013
27799
  for (const e of entries) {
27014
- const full = path27.join(dir, e.name);
27800
+ const full = path30.join(dir, e.name);
27015
27801
  if (e.isDirectory()) {
27016
27802
  out.push(...await walkJson(full));
27017
27803
  } else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
@@ -27198,9 +27984,9 @@ function readPolicy(ctx) {
27198
27984
  }
27199
27985
 
27200
27986
  // src/execution/tool-executor.ts
27201
- import { randomUUID as randomUUID16 } from "node:crypto";
27202
- import * as fs9 from "node:fs/promises";
27203
- import * as path30 from "node:path";
27987
+ import { randomUUID as randomUUID17 } from "node:crypto";
27988
+ import * as fs10 from "node:fs/promises";
27989
+ import * as path33 from "node:path";
27204
27990
  import { isDeepStrictEqual } from "node:util";
27205
27991
 
27206
27992
  // src/observability/process-telemetry.ts
@@ -27212,13 +27998,15 @@ function runWithProcessTelemetry(context, run) {
27212
27998
 
27213
27999
  // src/security/kanban-boundary.ts
27214
28000
  import { realpath as realpath3 } from "node:fs/promises";
27215
- import * as path28 from "node:path";
28001
+ import * as path31 from "node:path";
27216
28002
  import {
28003
+ evaluateContractGraphReadiness,
27217
28004
  evaluateKanbanBoundaryOpaque,
27218
28005
  evaluateKanbanBoundaryPath,
27219
28006
  readBoard,
27220
28007
  resolveKanbanBoundaryLayers
27221
28008
  } from "@wrongstack/kanban";
28009
+ var GOVERNANCE_CONTROL_TOOLS = /* @__PURE__ */ new Set(["kanban", "plan", "task", "todo"]);
27222
28010
  var PATH_KEYS = /* @__PURE__ */ new Set([
27223
28011
  "path",
27224
28012
  "paths",
@@ -27243,12 +28031,60 @@ var TOOL_PATH_KEYS = {
27243
28031
  // filesystem target in language_info. Keep ambiguous names tool-specific.
27244
28032
  language_info: ["target"]
27245
28033
  };
27246
- async function evaluateToolKanbanBoundary(tool, input, ctx) {
28034
+ async function evaluateToolKanbanBoundary(tool, input, ctx, options = {}) {
28035
+ if (tool.name === "kanban") return { decision: "allow" };
27247
28036
  const identity = resolveKanbanIdentity(ctx);
27248
- if (!identity.boardId) return { decision: "allow" };
28037
+ const governanceRequired = options.requireGovernance && isGovernedMutation(tool, input);
28038
+ if (!identity.boardId) {
28039
+ return governanceRequired ? {
28040
+ decision: "block",
28041
+ reason: "Kanban governance is mandatory before product mutation. Create a managed card, add its required details and executable acceptance criteria, then call kanban start_task to bind it to this run."
28042
+ } : { decision: "allow" };
28043
+ }
27249
28044
  const board = await readBoard(identity.policyRoot ?? ctx.projectRoot, identity.boardId);
27250
- if (!board) return { decision: "allow" };
28045
+ if (!board) {
28046
+ return governanceRequired ? {
28047
+ decision: "block",
28048
+ reason: `Active Kanban board not found: ${identity.boardId}. Recreate or select a valid managed card before mutation.`,
28049
+ boardId: identity.boardId,
28050
+ ...identity.taskId ? { taskId: identity.taskId } : {}
28051
+ } : { decision: "allow" };
28052
+ }
27251
28053
  const task = identity.taskId ? board.tasks.find((candidate) => candidate.id === identity.taskId) : void 0;
28054
+ if (governanceRequired) {
28055
+ if (!identity.taskId) {
28056
+ return {
28057
+ decision: "block",
28058
+ reason: "Kanban governance requires an active task, not only a board. Complete the card details and call kanban start_task before mutation.",
28059
+ boardId: board.id
28060
+ };
28061
+ }
28062
+ if (!task) {
28063
+ return {
28064
+ decision: "block",
28065
+ reason: `Active Kanban task not found: ${identity.taskId}. Call kanban start_task with a valid card.`,
28066
+ boardId: board.id,
28067
+ taskId: identity.taskId
28068
+ };
28069
+ }
28070
+ const readiness = evaluateContractGraphReadiness(board, task.id);
28071
+ if (!readiness.ready) {
28072
+ return {
28073
+ decision: "block",
28074
+ reason: "Active card is not implementation-ready: " + readiness.issues.map((issue) => issue.message).join(" | "),
28075
+ boardId: board.id,
28076
+ taskId: task.id
28077
+ };
28078
+ }
28079
+ if (task.lifecycle?.currentStage !== "running" || task.assignment?.status !== "running") {
28080
+ return {
28081
+ decision: "block",
28082
+ reason: "Active card must be in Running with a live assignment before product mutation. Call kanban start_task after completing the required card details.",
28083
+ boardId: board.id,
28084
+ taskId: task.id
28085
+ };
28086
+ }
28087
+ }
27252
28088
  if (identity.leaseId && task?.assignment) {
27253
28089
  const caps2 = tool.capabilities ?? [];
27254
28090
  const isWrite = caps2.includes("fs.write") || caps2.includes("fs.write.outside-project") || caps2.some((c) => c.startsWith("shell."));
@@ -27284,6 +28120,19 @@ async function evaluateToolKanbanBoundary(tool, input, ctx) {
27284
28120
  ...task ? { taskId: task.id } : {}
27285
28121
  };
27286
28122
  }
28123
+ function isGovernedMutation(tool, input) {
28124
+ if (!tool.mutating || GOVERNANCE_CONTROL_TOOLS.has(tool.name)) return false;
28125
+ if (tool.capabilities?.includes("tool.meta")) return false;
28126
+ if (tool.name === "git") {
28127
+ const command = input["command"];
28128
+ if (command === "status" || command === "log" || command === "diff") return false;
28129
+ if (command === "worktree" && input["worktreeAction"] === "list") return false;
28130
+ }
28131
+ const capabilities = tool.capabilities ?? [];
28132
+ return capabilities.some(
28133
+ (capability) => capability === "fs.write" || capability === "fs.write.outside-project" || capability === "package.install" || capability === "tool.mutate.any" || capability.startsWith("shell.") || capability === "net.outbound"
28134
+ );
28135
+ }
27287
28136
  function resolveKanbanIdentity(ctx) {
27288
28137
  const metaKanban = ctx.meta["kanban"];
27289
28138
  const record = metaKanban && typeof metaKanban === "object" ? metaKanban : void 0;
@@ -27301,10 +28150,10 @@ function resolveKanbanIdentity(ctx) {
27301
28150
  async function extractCandidatePaths(toolName, input, ctx) {
27302
28151
  if (toolName === "patch" && typeof input["patch"] === "string") {
27303
28152
  const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
27304
- const directory = path28.isAbsolute(directoryInput) ? directoryInput : path28.resolve(ctx.workingDir, directoryInput);
28153
+ const directory = path31.isAbsolute(directoryInput) ? directoryInput : path31.resolve(ctx.workingDir, directoryInput);
27305
28154
  const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
27306
28155
  const targets = extractPatchTargets(input["patch"], strip).map(
27307
- (target) => relativeToProject(path28.resolve(directory, target), ctx.projectRoot)
28156
+ (target) => relativeToProject(path31.resolve(directory, target), ctx.projectRoot)
27308
28157
  );
27309
28158
  return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
27310
28159
  }
@@ -27314,7 +28163,7 @@ async function extractCandidatePaths(toolName, input, ctx) {
27314
28163
  collectPathValues(input, values, pathKeys);
27315
28164
  if (toolName === "scaffold" && typeof input["name"] === "string") {
27316
28165
  const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
27317
- values.push(path28.join(cwd, input["name"]));
28166
+ values.push(path31.join(cwd, input["name"]));
27318
28167
  }
27319
28168
  const candidates = [
27320
28169
  ...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
@@ -27322,21 +28171,21 @@ async function extractCandidatePaths(toolName, input, ctx) {
27322
28171
  return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
27323
28172
  }
27324
28173
  async function canonicalizeCandidatePath(candidate, ctx) {
27325
- if (path28.isAbsolute(candidate)) return candidate;
27326
- const absolute = path28.resolve(ctx.projectRoot, candidate);
28174
+ if (path31.isAbsolute(candidate)) return candidate;
28175
+ const absolute = path31.resolve(ctx.projectRoot, candidate);
27327
28176
  const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
27328
28177
  let probe = absolute;
27329
28178
  const missingSegments = [];
27330
28179
  while (true) {
27331
28180
  try {
27332
- const canonical = path28.join(await realpath3(probe), ...missingSegments);
28181
+ const canonical = path31.join(await realpath3(probe), ...missingSegments);
27333
28182
  return relativeToProject(canonical, canonicalRoot);
27334
28183
  } catch (cause) {
27335
28184
  const code = cause.code;
27336
28185
  if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
27337
- const parent = path28.dirname(probe);
28186
+ const parent = path31.dirname(probe);
27338
28187
  if (parent === probe) return absolute;
27339
- missingSegments.unshift(path28.basename(probe));
28188
+ missingSegments.unshift(path31.basename(probe));
27340
28189
  probe = parent;
27341
28190
  }
27342
28191
  }
@@ -27359,12 +28208,12 @@ function splitPathList(value) {
27359
28208
  return value.split(",").map((item) => item.trim()).filter(Boolean);
27360
28209
  }
27361
28210
  function resolveInputPath(value, ctx) {
27362
- const absolute = path28.isAbsolute(value) ? value : path28.resolve(ctx.workingDir, value);
28211
+ const absolute = path31.isAbsolute(value) ? value : path31.resolve(ctx.workingDir, value);
27363
28212
  return relativeToProject(absolute, ctx.projectRoot);
27364
28213
  }
27365
28214
  function relativeToProject(absolute, projectRoot) {
27366
- const relative8 = path28.relative(projectRoot, absolute).replace(/\\/g, "/");
27367
- return relative8.startsWith("../") || path28.isAbsolute(relative8) ? absolute : relative8 || ".";
28215
+ const relative8 = path31.relative(projectRoot, absolute).replace(/\\/g, "/");
28216
+ return relative8.startsWith("../") || path31.isAbsolute(relative8) ? absolute : relative8 || ".";
27368
28217
  }
27369
28218
  function extractPatchTargets(patchText, strip) {
27370
28219
  const targets = [];
@@ -27387,9 +28236,9 @@ function numericValue(value) {
27387
28236
  var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
27388
28237
 
27389
28238
  // src/execution/tool-executor-support.ts
27390
- import { createHash as createHash7, randomUUID as randomUUID15 } from "node:crypto";
27391
- import * as fs8 from "node:fs/promises";
27392
- import * as path29 from "node:path";
28239
+ import { createHash as createHash8, randomUUID as randomUUID16 } from "node:crypto";
28240
+ import * as fs9 from "node:fs/promises";
28241
+ import * as path32 from "node:path";
27393
28242
 
27394
28243
  // src/types/tool-markers.ts
27395
28244
  var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
@@ -27514,12 +28363,12 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
27514
28363
  return content;
27515
28364
  }
27516
28365
  try {
27517
- const dir = path29.join(wstackGlobalRoot(), "tool-output");
27518
- await fs8.mkdir(dir, { recursive: true });
28366
+ const dir = path32.join(wstackGlobalRoot(), "tool-output");
28367
+ await fs9.mkdir(dir, { recursive: true });
27519
28368
  const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
27520
28369
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27521
- const filePath = path29.join(dir, `${stamp}-${safeTool}-${randomUUID15()}.log`);
27522
- await fs8.writeFile(filePath, content, "utf8");
28370
+ const filePath = path32.join(dir, `${stamp}-${safeTool}-${randomUUID16()}.log`);
28371
+ await fs9.writeFile(filePath, content, "utf8");
27523
28372
  const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
27524
28373
  const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
27525
28374
  const previewBytes = Math.min(
@@ -27544,7 +28393,7 @@ function hashPermissionInput(input, scrubber2) {
27544
28393
  } catch {
27545
28394
  serialized = String(input);
27546
28395
  }
27547
- return createHash7("sha256").update(scrubber2.scrub(serialized), "utf8").digest("hex");
28396
+ return createHash8("sha256").update(scrubber2.scrub(serialized), "utf8").digest("hex");
27548
28397
  }
27549
28398
  function sliceUtf8Prefix(text, maxBytes) {
27550
28399
  if (maxBytes <= 0) return "";
@@ -27577,6 +28426,41 @@ function sliceUtf8Suffix(text, maxBytes) {
27577
28426
  return text.slice(start);
27578
28427
  }
27579
28428
 
28429
+ // src/execution/tool-error-taxonomy.ts
28430
+ function toolErrorResult(use, err, opts = {}) {
28431
+ const { category, retryable, detail } = classifyToolError(err);
28432
+ const rawMessage = err instanceof Error ? err.message : typeof err === "string" ? err : "Unknown error";
28433
+ const userMessage = opts.scrubber ? opts.scrubber(rawMessage) : rawMessage;
28434
+ const info = {
28435
+ category,
28436
+ retryable,
28437
+ userMessage,
28438
+ ...detail !== void 0 ? { detail } : {}
28439
+ };
28440
+ const prefix = formatErrorPrefix(category, retryable);
28441
+ const detailSuffix = detail ? ` [${detail}]` : "";
28442
+ return {
28443
+ type: "tool_result",
28444
+ tool_use_id: use.id,
28445
+ content: `${prefix}: ${userMessage}${detailSuffix}`,
28446
+ is_error: true,
28447
+ // Attach structured info for programmatic consumers (retry logic,
28448
+ // audit log, observability).
28449
+ _toolErrorInfo: info
28450
+ };
28451
+ }
28452
+ function formatErrorPrefix(category, retryable) {
28453
+ const label = CATEGORY_LABELS[category] ?? "Error";
28454
+ return retryable ? `${label} (retryable)` : label;
28455
+ }
28456
+ var CATEGORY_LABELS = {
28457
+ ["transient" /* TRANSIENT */]: "Transient error",
28458
+ ["not_found" /* NOT_FOUND */]: "Not found",
28459
+ ["permission" /* PERMISSION */]: "Permission denied",
28460
+ ["validation" /* VALIDATION */]: "Validation error",
28461
+ ["fatal" /* FATAL */]: "Error"
28462
+ };
28463
+
27580
28464
  // src/execution/tool-executor-logging.ts
27581
28465
  function toolLogBase(ctx, use, toolName, durationMs) {
27582
28466
  return {
@@ -27682,41 +28566,6 @@ function blockedByHookResult(use, reason) {
27682
28566
  };
27683
28567
  }
27684
28568
 
27685
- // src/execution/tool-error-taxonomy.ts
27686
- function toolErrorResult(use, err, opts = {}) {
27687
- const { category, retryable, detail } = classifyToolError(err);
27688
- const rawMessage = err instanceof Error ? err.message : typeof err === "string" ? err : "Unknown error";
27689
- const userMessage = opts.scrubber ? opts.scrubber(rawMessage) : rawMessage;
27690
- const info = {
27691
- category,
27692
- retryable,
27693
- userMessage,
27694
- ...detail !== void 0 ? { detail } : {}
27695
- };
27696
- const prefix = formatErrorPrefix(category, retryable);
27697
- const detailSuffix = detail ? ` [${detail}]` : "";
27698
- return {
27699
- type: "tool_result",
27700
- tool_use_id: use.id,
27701
- content: `${prefix}: ${userMessage}${detailSuffix}`,
27702
- is_error: true,
27703
- // Attach structured info for programmatic consumers (retry logic,
27704
- // audit log, observability).
27705
- _toolErrorInfo: info
27706
- };
27707
- }
27708
- function formatErrorPrefix(category, retryable) {
27709
- const label = CATEGORY_LABELS[category] ?? "Error";
27710
- return retryable ? `${label} (retryable)` : label;
27711
- }
27712
- var CATEGORY_LABELS = {
27713
- ["transient" /* TRANSIENT */]: "Transient error",
27714
- ["not_found" /* NOT_FOUND */]: "Not found",
27715
- ["permission" /* PERMISSION */]: "Permission denied",
27716
- ["validation" /* VALIDATION */]: "Validation error",
27717
- ["fatal" /* FATAL */]: "Error"
27718
- };
27719
-
27720
28569
  // src/execution/tool-executor-stream.ts
27721
28570
  async function executeStreamedTool({
27722
28571
  tool,
@@ -28005,7 +28854,9 @@ ${errorDetails}`,
28005
28854
  return { result, tool, durationMs: Date.now() - start };
28006
28855
  }
28007
28856
  }
28008
- const boundary = await evaluateToolKanbanBoundary(tool, use.input, ctx);
28857
+ const boundary = await evaluateToolKanbanBoundary(tool, use.input, ctx, {
28858
+ requireGovernance: this.opts.requireKanbanGovernance
28859
+ });
28009
28860
  if (boundary.decision === "block") {
28010
28861
  const result = {
28011
28862
  type: "tool_result",
@@ -28059,18 +28910,18 @@ ${errorDetails}`,
28059
28910
  if (this.opts.confirmAwaiter) {
28060
28911
  const awaiter = this.opts.confirmAwaiter;
28061
28912
  const choice = await new Promise(
28062
- (resolve19, reject) => {
28913
+ (resolve21, reject) => {
28063
28914
  const signal = ctx.signal;
28064
- const onAbort = () => resolve19("abort");
28915
+ const onAbort = () => resolve21("abort");
28065
28916
  if (signal.aborted) {
28066
- resolve19("abort");
28917
+ resolve21("abort");
28067
28918
  return;
28068
28919
  }
28069
28920
  signal.addEventListener("abort", onAbort, { once: true });
28070
28921
  awaiter(tool, use.input, use.id, suggestedPattern).then(
28071
28922
  (c) => {
28072
28923
  signal.removeEventListener("abort", onAbort);
28073
- resolve19(c);
28924
+ resolve21(c);
28074
28925
  },
28075
28926
  (e) => {
28076
28927
  signal.removeEventListener("abort", onAbort);
@@ -28116,10 +28967,10 @@ ${errorDetails}`,
28116
28967
  const inputPath = use.input && typeof use.input === "object" ? use.input.path : void 0;
28117
28968
  const caps = tool.capabilities ?? [];
28118
28969
  const hasFileCapability = caps.includes("fs.read") || caps.includes("fs.write");
28119
- const absPath = hasFileCapability && typeof inputPath === "string" ? path30.isAbsolute(inputPath) ? inputPath : path30.resolve(ctx.projectRoot, inputPath) : void 0;
28970
+ const absPath = hasFileCapability && typeof inputPath === "string" ? path33.isAbsolute(inputPath) ? inputPath : path33.resolve(ctx.projectRoot, inputPath) : void 0;
28120
28971
  let writeTargetExisted;
28121
28972
  if (tool.name === "write" && caps.includes("fs.write") && absPath) {
28122
- writeTargetExisted = await fs9.stat(absPath).then(
28973
+ writeTargetExisted = await fs10.stat(absPath).then(
28123
28974
  (stat15) => stat15.isFile(),
28124
28975
  (error2) => error2.code === "ENOENT" ? false : void 0
28125
28976
  );
@@ -28281,7 +29132,7 @@ ${post.additionalContext}`;
28281
29132
  const bridge = async (toolName, input) => {
28282
29133
  const nestedUse = {
28283
29134
  type: "tool_use",
28284
- id: `nested-${randomUUID16()}`,
29135
+ id: `nested-${randomUUID17()}`,
28285
29136
  name: toolName,
28286
29137
  input
28287
29138
  };
@@ -28404,7 +29255,7 @@ ${preToolContext.text}` : preToolContext.text;
28404
29255
  ]);
28405
29256
  let output;
28406
29257
  const execute = () => typeof tool.executeStream === "function" ? this.runStreamedTool(tool, input, ctx, combined, toolUseId) : (async () => tool.execute(input, ctx, { signal: combined }))();
28407
- const telemetryToolCallId = toolUseId ?? `nested-${randomUUID16()}`;
29258
+ const telemetryToolCallId = toolUseId ?? `nested-${randomUUID17()}`;
28408
29259
  const toolPromise = this.opts.events ? runWithNetworkTelemetry(
28409
29260
  {
28410
29261
  events: this.opts.events,
@@ -28430,7 +29281,7 @@ ${preToolContext.text}` : preToolContext.text;
28430
29281
  toolPromise.catch(() => {
28431
29282
  });
28432
29283
  try {
28433
- output = await new Promise((resolve19, reject) => {
29284
+ output = await new Promise((resolve21, reject) => {
28434
29285
  const onAbort = () => {
28435
29286
  setTimeout(() => reject(abortReasonToError(combined.reason)), 0);
28436
29287
  };
@@ -28438,7 +29289,7 @@ ${preToolContext.text}` : preToolContext.text;
28438
29289
  toolPromise.then(
28439
29290
  (v) => {
28440
29291
  combined.removeEventListener("abort", onAbort);
28441
- resolve19(v);
29292
+ resolve21(v);
28442
29293
  },
28443
29294
  (e) => {
28444
29295
  combined.removeEventListener("abort", onAbort);
@@ -28991,28 +29842,28 @@ function codexModelMeta(id) {
28991
29842
  }
28992
29843
 
28993
29844
  // src/models/mode-store.ts
28994
- import * as fs10 from "node:fs/promises";
28995
- import * as path32 from "node:path";
29845
+ import * as fs11 from "node:fs/promises";
29846
+ import * as path35 from "node:path";
28996
29847
 
28997
29848
  // src/types/mode-prompts.ts
28998
- import { readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
28999
- import * as path31 from "node:path";
29000
- import { fileURLToPath as fileURLToPath4 } from "node:url";
29849
+ import { readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
29850
+ import * as path34 from "node:path";
29851
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
29001
29852
  function modePrompt(id) {
29002
29853
  for (const dir of modePromptDirCandidates()) {
29003
29854
  try {
29004
- return readFileSync7(path31.join(dir, `${id}.md`), "utf8").trimEnd();
29855
+ return readFileSync9(path34.join(dir, `${id}.md`), "utf8").trimEnd();
29005
29856
  } catch {
29006
29857
  }
29007
29858
  }
29008
29859
  return "";
29009
29860
  }
29010
29861
  function modePromptDirCandidates() {
29011
- const here = path31.dirname(fileURLToPath4(import.meta.url));
29862
+ const here = path34.dirname(fileURLToPath5(import.meta.url));
29012
29863
  const candidates = [
29013
- path31.resolve(here, "../../instructions/modes"),
29014
- path31.resolve(here, "../instructions/modes"),
29015
- path31.resolve(here, "instructions/modes")
29864
+ path34.resolve(here, "../../instructions/modes"),
29865
+ path34.resolve(here, "../instructions/modes"),
29866
+ path34.resolve(here, "instructions/modes")
29016
29867
  ];
29017
29868
  return candidates.sort((a, b) => Number(!isDirectory3(a)) - Number(!isDirectory3(b)));
29018
29869
  }
@@ -29244,8 +30095,8 @@ var DefaultModeStore = class {
29244
30095
  }
29245
30096
  async loadActiveMode() {
29246
30097
  try {
29247
- const configPath = path32.join(this.configDir, "mode.json");
29248
- const content = await fs10.readFile(configPath, "utf8");
30098
+ const configPath = path35.join(this.configDir, "mode.json");
30099
+ const content = await fs11.readFile(configPath, "utf8");
29249
30100
  const data = JSON.parse(content);
29250
30101
  this.activeModeId = data.activeMode ?? null;
29251
30102
  } catch {
@@ -29254,8 +30105,8 @@ var DefaultModeStore = class {
29254
30105
  }
29255
30106
  async saveActiveMode() {
29256
30107
  try {
29257
- await fs10.mkdir(this.configDir, { recursive: true });
29258
- const configPath = path32.join(this.configDir, "mode.json");
30108
+ await fs11.mkdir(this.configDir, { recursive: true });
30109
+ const configPath = path35.join(this.configDir, "mode.json");
29259
30110
  await atomicWrite(
29260
30111
  configPath,
29261
30112
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -29267,14 +30118,14 @@ var DefaultModeStore = class {
29267
30118
  async function loadProjectModes(modesDir) {
29268
30119
  const modes = [];
29269
30120
  try {
29270
- const entries = await fs10.readdir(modesDir);
30121
+ const entries = await fs11.readdir(modesDir);
29271
30122
  for (const entry of entries) {
29272
30123
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
29273
- const filePath = path32.join(modesDir, entry);
29274
- const stat15 = await fs10.stat(filePath);
30124
+ const filePath = path35.join(modesDir, entry);
30125
+ const stat15 = await fs11.stat(filePath);
29275
30126
  if (!stat15.isFile()) continue;
29276
- const content = await fs10.readFile(filePath, "utf8");
29277
- const id = path32.basename(entry, path32.extname(entry));
30127
+ const content = await fs11.readFile(filePath, "utf8");
30128
+ const id = path35.basename(entry, path35.extname(entry));
29278
30129
  modes.push({
29279
30130
  id,
29280
30131
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -29290,8 +30141,8 @@ async function loadProjectModes(modesDir) {
29290
30141
  async function loadUserModes(modesDir) {
29291
30142
  const modes = [];
29292
30143
  try {
29293
- const manifestPath = path32.join(modesDir, "modes.json");
29294
- const content = await fs10.readFile(manifestPath, "utf8");
30144
+ const manifestPath = path35.join(modesDir, "modes.json");
30145
+ const content = await fs11.readFile(manifestPath, "utf8");
29295
30146
  const manifest = JSON.parse(content);
29296
30147
  for (const mode of manifest.modes) {
29297
30148
  modes.push(mode);
@@ -29302,8 +30153,8 @@ async function loadUserModes(modesDir) {
29302
30153
  }
29303
30154
 
29304
30155
  // src/models/models-registry.ts
29305
- import * as fs11 from "node:fs/promises";
29306
- import * as path33 from "node:path";
30156
+ import * as fs12 from "node:fs/promises";
30157
+ import * as path36 from "node:path";
29307
30158
  var DEFAULT_URL = "https://models.dev/api.json";
29308
30159
  var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
29309
30160
  var DEFAULT_TTL_SECONDS = 3 * 3600;
@@ -29380,7 +30231,7 @@ var DefaultModelsRegistry = class {
29380
30231
  this.overlay = opts.overlay;
29381
30232
  this.overlayUrl = opts.overlayUrl;
29382
30233
  this.overlayFile = opts.overlayFile;
29383
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path33.join(path33.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
30234
+ this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path36.join(path36.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
29384
30235
  this.logger = opts.logger ?? noOpLogger;
29385
30236
  }
29386
30237
  async load(opts = {}) {
@@ -29568,7 +30419,7 @@ var DefaultModelsRegistry = class {
29568
30419
  async readOverlayFile() {
29569
30420
  if (!this.overlayFile) return void 0;
29570
30421
  try {
29571
- const raw = await fs11.readFile(this.overlayFile, "utf8");
30422
+ const raw = await fs12.readFile(this.overlayFile, "utf8");
29572
30423
  return JSON.parse(raw);
29573
30424
  } catch {
29574
30425
  return void 0;
@@ -29647,7 +30498,7 @@ var DefaultModelsRegistry = class {
29647
30498
  }
29648
30499
  async readCacheAt(file) {
29649
30500
  try {
29650
- const raw = await fs11.readFile(file, "utf8");
30501
+ const raw = await fs12.readFile(file, "utf8");
29651
30502
  return JSON.parse(raw);
29652
30503
  } catch {
29653
30504
  return void 0;
@@ -29655,7 +30506,7 @@ var DefaultModelsRegistry = class {
29655
30506
  }
29656
30507
  /** Used by `wstack models refresh` to expose where the cache lives. */
29657
30508
  cacheLocation() {
29658
- return path33.resolve(this.cacheFile);
30509
+ return path36.resolve(this.cacheFile);
29659
30510
  }
29660
30511
  };
29661
30512
  var REASONING_EFFORTS = /* @__PURE__ */ new Set([
@@ -30006,9 +30857,9 @@ var DefaultHealthRegistry = class {
30006
30857
  }
30007
30858
  async runOne(check) {
30008
30859
  let timer = null;
30009
- const timeout = new Promise((resolve19) => {
30860
+ const timeout = new Promise((resolve21) => {
30010
30861
  timer = setTimeout(
30011
- () => resolve19({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
30862
+ () => resolve21({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
30012
30863
  this.timeoutMs
30013
30864
  );
30014
30865
  });
@@ -30206,7 +31057,7 @@ async function startMetricsServer(opts) {
30206
31057
  const tls = opts.tls;
30207
31058
  const useHttps = !!(tls?.cert && tls?.key);
30208
31059
  const host = opts.host ?? "127.0.0.1";
30209
- const path42 = opts.path ?? "/metrics";
31060
+ const path45 = opts.path ?? "/metrics";
30210
31061
  const healthPath = opts.healthPath ?? "/healthz";
30211
31062
  const healthRegistry = opts.healthRegistry;
30212
31063
  const token = opts.token;
@@ -30229,7 +31080,7 @@ async function startMetricsServer(opts) {
30229
31080
  return;
30230
31081
  }
30231
31082
  const url = req.url.split("?")[0];
30232
- if (url === path42) {
31083
+ if (url === path45) {
30233
31084
  let body;
30234
31085
  try {
30235
31086
  body = renderPrometheus(opts.sink.snapshot());
@@ -30266,23 +31117,23 @@ async function startMetricsServer(opts) {
30266
31117
  let server;
30267
31118
  if (useHttps && tls) {
30268
31119
  const { createServer } = await import("node:https");
30269
- const { readFileSync: readFileSync9 } = await import("node:fs");
31120
+ const { readFileSync: readFileSync11 } = await import("node:fs");
30270
31121
  server = createServer(
30271
- { cert: readFileSync9(tls.cert), key: readFileSync9(tls.key) },
31122
+ { cert: readFileSync11(tls.cert), key: readFileSync11(tls.key) },
30272
31123
  listener
30273
31124
  );
30274
31125
  } else {
30275
31126
  const { createServer } = await import("node:http");
30276
31127
  server = createServer(listener);
30277
31128
  }
30278
- await new Promise((resolve19, reject) => {
31129
+ await new Promise((resolve21, reject) => {
30279
31130
  const onError = (err) => {
30280
31131
  server.off("listening", onListening);
30281
31132
  reject(err);
30282
31133
  };
30283
31134
  const onListening = () => {
30284
31135
  server.off("error", onError);
30285
- resolve19();
31136
+ resolve21();
30286
31137
  };
30287
31138
  server.once("error", onError);
30288
31139
  server.once("listening", onListening);
@@ -30293,9 +31144,9 @@ async function startMetricsServer(opts) {
30293
31144
  const protocol = useHttps ? "https" : "http";
30294
31145
  return {
30295
31146
  port: boundPort,
30296
- url: `${protocol}://${host}:${boundPort}${path42}`,
30297
- close: () => new Promise((resolve19, reject) => {
30298
- server.close((err) => err ? reject(err) : resolve19());
31147
+ url: `${protocol}://${host}:${boundPort}${path45}`,
31148
+ close: () => new Promise((resolve21, reject) => {
31149
+ server.close((err) => err ? reject(err) : resolve21());
30299
31150
  })
30300
31151
  };
30301
31152
  }
@@ -30565,8 +31416,8 @@ function startOtlpTraceExporter(opts) {
30565
31416
  }
30566
31417
 
30567
31418
  // src/security/permission-policy.ts
30568
- import * as fs12 from "node:fs/promises";
30569
- import * as path36 from "node:path";
31419
+ import * as fs13 from "node:fs/promises";
31420
+ import * as path39 from "node:path";
30570
31421
 
30571
31422
  // src/security/permission-policy-schema.ts
30572
31423
  var TRUST_POLICY_LIMITS = Object.freeze({
@@ -30611,19 +31462,19 @@ var UNSAFE_PROPERTY_NAMES = /* @__PURE__ */ new Set(["__proto__", "prototype", "
30611
31462
  function isRecord5(value) {
30612
31463
  return typeof value === "object" && value !== null && !Array.isArray(value);
30613
31464
  }
30614
- function error(diagnostics, code, path42, message) {
30615
- diagnostics.push({ severity: "error", code, path: path42, message });
31465
+ function error(diagnostics, code, path45, message) {
31466
+ diagnostics.push({ severity: "error", code, path: path45, message });
30616
31467
  }
30617
- function validatePatterns(value, path42, diagnostics) {
31468
+ function validatePatterns(value, path45, diagnostics) {
30618
31469
  if (!Array.isArray(value)) {
30619
- error(diagnostics, "invalid_pattern_list", path42, "must be an array of strings");
31470
+ error(diagnostics, "invalid_pattern_list", path45, "must be an array of strings");
30620
31471
  return void 0;
30621
31472
  }
30622
31473
  if (value.length > TRUST_POLICY_LIMITS.maxPatternsPerRule) {
30623
31474
  error(
30624
31475
  diagnostics,
30625
31476
  "too_many_patterns",
30626
- path42,
31477
+ path45,
30627
31478
  `must contain at most ${TRUST_POLICY_LIMITS.maxPatternsPerRule} patterns`
30628
31479
  );
30629
31480
  return void 0;
@@ -30631,7 +31482,7 @@ function validatePatterns(value, path42, diagnostics) {
30631
31482
  const patterns = [];
30632
31483
  const seen = /* @__PURE__ */ new Set();
30633
31484
  for (const [index, pattern] of value.entries()) {
30634
- const itemPath = `${path42}[${index}]`;
31485
+ const itemPath = `${path45}[${index}]`;
30635
31486
  if (typeof pattern !== "string" || pattern.length === 0 || pattern.length > TRUST_POLICY_LIMITS.maxPatternChars) {
30636
31487
  error(
30637
31488
  diagnostics,
@@ -30735,7 +31586,7 @@ function validateTrustPolicy(value) {
30735
31586
  }
30736
31587
 
30737
31588
  // src/security/yolo-risk.ts
30738
- import * as path34 from "node:path";
31589
+ import * as path37 from "node:path";
30739
31590
  var CATASTROPHIC_PATTERNS = [
30740
31591
  /\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
30741
31592
  // make a filesystem — wipes a partition
@@ -30795,9 +31646,9 @@ function getInputString(input, key) {
30795
31646
  function pathLooksInsideProject(rawPath, projectRoot) {
30796
31647
  if (!projectRoot) return false;
30797
31648
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
30798
- const resolved = path34.resolve(projectRoot, rawPath);
30799
- const relative8 = path34.relative(projectRoot, resolved);
30800
- return !!relative8 && !relative8.startsWith("..") && !path34.isAbsolute(relative8);
31649
+ const resolved = path37.resolve(projectRoot, rawPath);
31650
+ const relative8 = path37.relative(projectRoot, resolved);
31651
+ return !!relative8 && !relative8.startsWith("..") && !path37.isAbsolute(relative8);
30801
31652
  }
30802
31653
  function tokenizeShell(command) {
30803
31654
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -31047,7 +31898,7 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
31047
31898
 
31048
31899
  // src/security/permission-helpers.ts
31049
31900
  import { realpathSync } from "node:fs";
31050
- import * as path35 from "node:path";
31901
+ import * as path38 from "node:path";
31051
31902
  function matchesTrust(patterns, subject) {
31052
31903
  return patterns.includes(subject) || matchAny(patterns, subject);
31053
31904
  }
@@ -31131,24 +31982,24 @@ function realpathOfNearestExisting(p) {
31131
31982
  const tail = [];
31132
31983
  for (; ; ) {
31133
31984
  try {
31134
- return tail.length === 0 ? realpathSync(probe) : path35.join(realpathSync(probe), ...tail);
31985
+ return tail.length === 0 ? realpathSync(probe) : path38.join(realpathSync(probe), ...tail);
31135
31986
  } catch {
31136
- const parent = path35.dirname(probe);
31987
+ const parent = path38.dirname(probe);
31137
31988
  if (parent === probe) return p;
31138
- tail.unshift(path35.basename(probe));
31989
+ tail.unshift(path38.basename(probe));
31139
31990
  probe = parent;
31140
31991
  }
31141
31992
  }
31142
31993
  }
31143
31994
  var agentStateRootRealCache = /* @__PURE__ */ new Map();
31144
31995
  function isInsideAgentStateRoot(absPath) {
31145
- const lexicalRoot = normalizeForCompare(path35.resolve(wstackGlobalRoot()));
31996
+ const lexicalRoot = normalizeForCompare(path38.resolve(wstackGlobalRoot()));
31146
31997
  if (!lexicalRoot) return false;
31147
31998
  const target = normalizeForCompare(absPath);
31148
31999
  if (target === lexicalRoot || target.startsWith(`${lexicalRoot}/`)) return true;
31149
32000
  let realRoot = agentStateRootRealCache.get(lexicalRoot);
31150
32001
  if (realRoot === void 0) {
31151
- realRoot = normalizeForCompare(realpathOfNearestExisting(path35.resolve(wstackGlobalRoot())));
32002
+ realRoot = normalizeForCompare(realpathOfNearestExisting(path38.resolve(wstackGlobalRoot())));
31152
32003
  agentStateRootRealCache.set(lexicalRoot, realRoot);
31153
32004
  }
31154
32005
  if (!realRoot || realRoot === lexicalRoot) return false;
@@ -31157,7 +32008,7 @@ function isInsideAgentStateRoot(absPath) {
31157
32008
  }
31158
32009
  function isProtectedAgentStatePath(absPath) {
31159
32010
  if (!isInsideAgentStateRoot(absPath)) return false;
31160
- return AGENT_STATE_SENSITIVE_BASENAMES.test(path35.basename(normalizeForCompare(absPath)));
32011
+ return AGENT_STATE_SENSITIVE_BASENAMES.test(path38.basename(normalizeForCompare(absPath)));
31161
32012
  }
31162
32013
  function pathLooksSensitive(rawPath) {
31163
32014
  const normalized = stripShellQuotes(rawPath).replace(/\\/g, "/");
@@ -31318,7 +32169,7 @@ var DefaultPermissionPolicy = class {
31318
32169
  if (!hasCapability(tool, ToolCapabilities.FS_WRITE)) return false;
31319
32170
  for (const targetPath of fsWriteTargetPaths(input)) {
31320
32171
  const base = ctx.workingDir ?? ctx.cwd;
31321
- const resolved = base ? path36.resolve(base, targetPath) : path36.resolve(targetPath);
32172
+ const resolved = base ? path39.resolve(base, targetPath) : path39.resolve(targetPath);
31322
32173
  if (isInsideAgentStateRoot(resolved)) return true;
31323
32174
  }
31324
32175
  return false;
@@ -31363,7 +32214,7 @@ var DefaultPermissionPolicy = class {
31363
32214
  this.policyDiagnostics = [];
31364
32215
  this.policyInvalid = false;
31365
32216
  try {
31366
- const raw = await fs12.readFile(this.trustFile, "utf8");
32217
+ const raw = await fs13.readFile(this.trustFile, "utf8");
31367
32218
  const parsed = safeParse(raw);
31368
32219
  if (!parsed.ok) {
31369
32220
  this.policy = {};
@@ -31985,9 +32836,9 @@ var DefaultPermissionPolicy = class {
31985
32836
 
31986
32837
  // src/security/secret-vault.ts
31987
32838
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scryptSync } from "node:crypto";
31988
- import * as fs13 from "node:fs";
32839
+ import * as fs14 from "node:fs";
31989
32840
  import * as fsp22 from "node:fs/promises";
31990
- import * as path37 from "node:path";
32841
+ import * as path40 from "node:path";
31991
32842
 
31992
32843
  // src/types/secret-vault.ts
31993
32844
  var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
@@ -32127,7 +32978,7 @@ function checkKeyFilePermissions(keyFile, opts) {
32127
32978
  if (process.platform === "win32") return;
32128
32979
  const warn = opts?.warn ?? ((msg) => console.warn(msg));
32129
32980
  try {
32130
- const stat15 = fs13.statSync(keyFile);
32981
+ const stat15 = fs14.statSync(keyFile);
32131
32982
  const actualMode = stat15.mode & 511;
32132
32983
  if (actualMode !== KEY_FILE_MODE) {
32133
32984
  void restrictFilePermissions(keyFile, {
@@ -32143,21 +32994,21 @@ function checkKeyFilePermissions(keyFile, opts) {
32143
32994
  }
32144
32995
  function writeKeyFileAtomicSync(keyFile, content) {
32145
32996
  const tmp = `${keyFile}.${randomBytes3(4).toString("hex")}.tmp`;
32146
- const fd = fs13.openSync(tmp, "w", 384);
32997
+ const fd = fs14.openSync(tmp, "w", 384);
32147
32998
  try {
32148
- fs13.writeFileSync(fd, content);
32999
+ fs14.writeFileSync(fd, content);
32149
33000
  try {
32150
- fs13.fsyncSync(fd);
33001
+ fs14.fsyncSync(fd);
32151
33002
  } catch {
32152
33003
  }
32153
33004
  } finally {
32154
- fs13.closeSync(fd);
33005
+ fs14.closeSync(fd);
32155
33006
  }
32156
33007
  try {
32157
- fs13.renameSync(tmp, keyFile);
33008
+ fs14.renameSync(tmp, keyFile);
32158
33009
  } catch (err) {
32159
33010
  try {
32160
- fs13.unlinkSync(tmp);
33011
+ fs14.unlinkSync(tmp);
32161
33012
  } catch {
32162
33013
  }
32163
33014
  throw err;
@@ -32280,7 +33131,7 @@ var DefaultSecretVault = class {
32280
33131
  const oldVersion = this._keyVersion;
32281
33132
  const newKey = randomBytes3(KEY_BYTES);
32282
33133
  const newVersion = oldVersion + 1;
32283
- fs13.mkdirSync(path37.dirname(this.keyFile), { recursive: true });
33134
+ fs14.mkdirSync(path40.dirname(this.keyFile), { recursive: true });
32284
33135
  const passphrase = getVaultPassphrase();
32285
33136
  if (passphrase) {
32286
33137
  writeKeyFileAtomicSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase));
@@ -32320,7 +33171,7 @@ var DefaultSecretVault = class {
32320
33171
  loadOrCreateKey() {
32321
33172
  if (this.key) return this.key;
32322
33173
  try {
32323
- const buf = fs13.readFileSync(this.keyFile);
33174
+ const buf = fs14.readFileSync(this.keyFile);
32324
33175
  if (isWrappedKeyFile(buf)) {
32325
33176
  const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
32326
33177
  this.key = key2;
@@ -32367,17 +33218,17 @@ var DefaultSecretVault = class {
32367
33218
  } catch (err) {
32368
33219
  if (err.code !== "ENOENT") throw err;
32369
33220
  }
32370
- fs13.mkdirSync(path37.dirname(this.keyFile), { recursive: true });
33221
+ fs14.mkdirSync(path40.dirname(this.keyFile), { recursive: true });
32371
33222
  const key = randomBytes3(KEY_BYTES);
32372
33223
  const passphrase = getVaultPassphrase();
32373
33224
  const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
32374
33225
  try {
32375
- fs13.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
33226
+ fs14.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
32376
33227
  this.scheduleKeyHardening();
32377
33228
  } catch (err) {
32378
33229
  if (err.code !== "EEXIST") throw err;
32379
33230
  this.scheduleKeyHardening();
32380
- const buf = fs13.readFileSync(this.keyFile);
33231
+ const buf = fs14.readFileSync(this.keyFile);
32381
33232
  if (isWrappedKeyFile(buf)) {
32382
33233
  const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
32383
33234
  this.key = winnerKey;
@@ -32427,7 +33278,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
32427
33278
  }
32428
33279
  const merged = deepMerge(current, patch ?? {});
32429
33280
  const encrypted = encryptConfigSecrets(merged, vault);
32430
- await fsp22.mkdir(path37.dirname(configPath), { recursive: true });
33281
+ await fsp22.mkdir(path40.dirname(configPath), { recursive: true });
32431
33282
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
32432
33283
  await restrictFilePermissions2(configPath);
32433
33284
  await vault.flushHardening?.();
@@ -32482,7 +33333,7 @@ function walkCount(node, vault, counter) {
32482
33333
  // src/storage/attachment-store.ts
32483
33334
  import { randomBytes as randomBytes4 } from "node:crypto";
32484
33335
  import * as fsp23 from "node:fs/promises";
32485
- import * as path38 from "node:path";
33336
+ import * as path41 from "node:path";
32486
33337
  var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
32487
33338
  var PLACEHOLDER_RE2 = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
32488
33339
  var DefaultAttachmentStore = class {
@@ -32503,7 +33354,7 @@ var DefaultAttachmentStore = class {
32503
33354
  let data = input.data;
32504
33355
  if (this.spoolDir && bytes >= this.spoolThreshold) {
32505
33356
  await fsp23.mkdir(this.spoolDir, { recursive: true });
32506
- spooledPath = path38.join(this.spoolDir, `${id}.bin`);
33357
+ spooledPath = path41.join(this.spoolDir, `${id}.bin`);
32507
33358
  await atomicWrite(spooledPath, input.data, {
32508
33359
  encoding: input.kind === "image" ? "base64" : "utf8"
32509
33360
  });
@@ -32622,7 +33473,7 @@ function mergeAdjacentText(blocks) {
32622
33473
  }
32623
33474
 
32624
33475
  // src/storage/config-loader.ts
32625
- import * as fs14 from "node:fs/promises";
33476
+ import * as fs15 from "node:fs/promises";
32626
33477
 
32627
33478
  // src/storage/config-loader/bootstrap.ts
32628
33479
  var BOOTSTRAP_KEYS = /* @__PURE__ */ new Set(["version", "activeProfile"]);
@@ -33041,8 +33892,8 @@ var IN_PROJECT_DENIED_PATHS = [
33041
33892
  reason: "The other half of the filesystem confinement switch."
33042
33893
  }
33043
33894
  ];
33044
- function deleteNestedPath(target, path42) {
33045
- const segments = path42.split(".");
33895
+ function deleteNestedPath(target, path45) {
33896
+ const segments = path45.split(".");
33046
33897
  const last = segments[segments.length - 1];
33047
33898
  if (last === void 0) return false;
33048
33899
  let cursor = target;
@@ -33099,16 +33950,16 @@ function assertInProjectAllowListComplete() {
33099
33950
  );
33100
33951
  }
33101
33952
  const orphanedPaths = IN_PROJECT_DENIED_PATHS.filter(
33102
- ({ path: path42 }) => !IN_PROJECT_ALLOWED_KEYS.has(path42.split(".")[0] ?? "")
33103
- ).map(({ path: path42 }) => path42);
33953
+ ({ path: path45 }) => !IN_PROJECT_ALLOWED_KEYS.has(path45.split(".")[0] ?? "")
33954
+ ).map(({ path: path45 }) => path45);
33104
33955
  if (orphanedPaths.length > 0) {
33105
33956
  problems.push(
33106
33957
  `IN_PROJECT_DENIED_PATHS entr(ies) whose top-level parent is not allowed: ` + orphanedPaths.join(", ") + ". The parent is already stripped wholesale, so the nested denial is dead \u2014 remove it."
33107
33958
  );
33108
33959
  }
33109
33960
  const malformedPaths = IN_PROJECT_DENIED_PATHS.filter(
33110
- ({ path: path42 }) => path42.split(".").length < 2 || path42.split(".").some((s) => s.length === 0)
33111
- ).map(({ path: path42 }) => path42);
33961
+ ({ path: path45 }) => path45.split(".").length < 2 || path45.split(".").some((s) => s.length === 0)
33962
+ ).map(({ path: path45 }) => path45);
33112
33963
  if (malformedPaths.length > 0) {
33113
33964
  problems.push(
33114
33965
  `IN_PROJECT_DENIED_PATHS entr(ies) are not dotted nested paths: ` + malformedPaths.join(", ") + ". Top-level keys belong in KNOWN_DENIED_IN_PROJECT instead."
@@ -33136,8 +33987,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
33136
33987
  }
33137
33988
  stripped.push(k);
33138
33989
  }
33139
- for (const { path: path42 } of IN_PROJECT_DENIED_PATHS) {
33140
- if (deleteNestedPath(out, path42)) stripped.push(path42);
33990
+ for (const { path: path45 } of IN_PROJECT_DENIED_PATHS) {
33991
+ if (deleteNestedPath(out, path45)) stripped.push(path45);
33141
33992
  }
33142
33993
  if (stripped.length > 0) {
33143
33994
  warn(
@@ -33326,10 +34177,10 @@ function removeLegacySageEngine(config) {
33326
34177
  }
33327
34178
 
33328
34179
  // src/storage/config-loader/path-identity.ts
33329
- import * as path39 from "node:path";
34180
+ import * as path42 from "node:path";
33330
34181
  function samePath(a, b) {
33331
- let ra = path39.resolve(a);
33332
- let rb = path39.resolve(b);
34182
+ let ra = path42.resolve(a);
34183
+ let rb = path42.resolve(b);
33333
34184
  if (process.platform === "win32" || process.platform === "darwin") {
33334
34185
  ra = ra.toLowerCase();
33335
34186
  rb = rb.toLowerCase();
@@ -33489,7 +34340,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33489
34340
  let parsed;
33490
34341
  let fileExisted = true;
33491
34342
  try {
33492
- const raw = await fs14.readFile(fp, "utf8");
34343
+ const raw = await fs15.readFile(fp, "utf8");
33493
34344
  const result = safeParse(raw);
33494
34345
  if (!result.ok || !isPlainRecord(result.value)) {
33495
34346
  return;
@@ -33585,7 +34436,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33585
34436
  let parsed;
33586
34437
  let existed = true;
33587
34438
  try {
33588
- const raw = await fs14.readFile(profileFp, "utf8");
34439
+ const raw = await fs15.readFile(profileFp, "utf8");
33589
34440
  const result = safeParse(raw);
33590
34441
  if (!result.ok || !isPlainRecord(result.value)) {
33591
34442
  this.logWarn("Profile config parse failed \u2014 falling back to defaults", {
@@ -33684,7 +34535,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33684
34535
  const fp = this.paths.syncConfig;
33685
34536
  const t0 = Date.now();
33686
34537
  try {
33687
- const raw = await fs14.readFile(fp, "utf8");
34538
+ const raw = await fs15.readFile(fp, "utf8");
33688
34539
  const parsed = safeParse(raw);
33689
34540
  if (!parsed.ok || !parsed.value) {
33690
34541
  this.events?.emit("storage.read", {
@@ -33746,7 +34597,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33746
34597
  const t0 = Date.now();
33747
34598
  let mtimeMs = null;
33748
34599
  try {
33749
- const stat15 = await fs14.stat(file);
34600
+ const stat15 = await fs15.stat(file);
33750
34601
  mtimeMs = stat15.mtimeMs;
33751
34602
  const cached = this.jsonCache.get(file);
33752
34603
  if (cached && cached.mtimeMs === mtimeMs) {
@@ -33776,7 +34627,7 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
33776
34627
  }
33777
34628
  let raw;
33778
34629
  try {
33779
- raw = await fs14.readFile(file, "utf8");
34630
+ raw = await fs15.readFile(file, "utf8");
33780
34631
  } catch (err) {
33781
34632
  if (err.code !== "ENOENT") {
33782
34633
  this.events?.emit("storage.read", {
@@ -34011,8 +34862,8 @@ function deepFreeze(obj) {
34011
34862
  }
34012
34863
 
34013
34864
  // src/storage/plan-store.ts
34865
+ import { randomUUID as randomUUID18 } from "node:crypto";
34014
34866
  import * as fsp24 from "node:fs/promises";
34015
- import { randomUUID as randomUUID17 } from "node:crypto";
34016
34867
  async function loadPlan(filePath, events) {
34017
34868
  const t0 = Date.now();
34018
34869
  let raw;
@@ -34110,7 +34961,7 @@ function emptyPlan(sessionId, title) {
34110
34961
  function addPlanItem(plan, title, details) {
34111
34962
  const now = (/* @__PURE__ */ new Date()).toISOString();
34112
34963
  const item = {
34113
- id: `plan_${Date.now()}_${randomUUID17().slice(0, 6)}`,
34964
+ id: `plan_${Date.now()}_${randomUUID18().slice(0, 6)}`,
34114
34965
  title,
34115
34966
  details,
34116
34967
  status: "open",
@@ -34182,7 +35033,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
34182
35033
  if (subtasks && subtasks.length > 0) {
34183
35034
  for (const st of subtasks) {
34184
35035
  todos.push({
34185
- id: `todo_${Date.now()}_${randomUUID17().slice(0, 6)}`,
35036
+ id: `todo_${Date.now()}_${randomUUID18().slice(0, 6)}`,
34186
35037
  content: st,
34187
35038
  status: "pending",
34188
35039
  promotedFromPlan: item.id
@@ -34310,7 +35161,7 @@ ${cat}:`);
34310
35161
 
34311
35162
  // src/storage/queue-store.ts
34312
35163
  import * as fsp25 from "node:fs/promises";
34313
- import * as path40 from "node:path";
35164
+ import * as path43 from "node:path";
34314
35165
  var QUEUE_MAX_ITEMS = 100;
34315
35166
  var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
34316
35167
  var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
@@ -34343,7 +35194,7 @@ var QueueStore = class {
34343
35194
  traceId;
34344
35195
  logger;
34345
35196
  constructor(opts) {
34346
- this.file = path40.join(opts.dir, "queue.json");
35197
+ this.file = path43.join(opts.dir, "queue.json");
34347
35198
  this.events = opts.events;
34348
35199
  this.traceId = opts.traceId;
34349
35200
  this.logger = opts.logger;
@@ -34558,8 +35409,8 @@ function isPersistedQueueItem(v) {
34558
35409
 
34559
35410
  // src/storage/recovery-lock.ts
34560
35411
  import * as fsp26 from "node:fs/promises";
34561
- import * as os2 from "node:os";
34562
- import * as path41 from "node:path";
35412
+ import * as os3 from "node:os";
35413
+ import * as path44 from "node:path";
34563
35414
  var LOCK_FILE = "active.json";
34564
35415
  var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
34565
35416
  var RecoveryLock = class {
@@ -34570,9 +35421,9 @@ var RecoveryLock = class {
34570
35421
  sessionStore;
34571
35422
  probe;
34572
35423
  constructor(opts) {
34573
- this.file = path41.join(opts.dir, LOCK_FILE);
35424
+ this.file = path44.join(opts.dir, LOCK_FILE);
34574
35425
  this.pid = opts.pid ?? process.pid;
34575
- this.hostname = opts.hostname ?? os2.hostname();
35426
+ this.hostname = opts.hostname ?? os3.hostname();
34576
35427
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
34577
35428
  this.sessionStore = opts.sessionStore;
34578
35429
  this.probe = opts.isPidAlive ?? defaultIsPidAlive;
@@ -34644,7 +35495,7 @@ var RecoveryLock = class {
34644
35495
  * null return before calling this.
34645
35496
  */
34646
35497
  async write(sessionId) {
34647
- await ensureDir(path41.dirname(this.file));
35498
+ await ensureDir(path44.dirname(this.file));
34648
35499
  const lock = {
34649
35500
  v: 1,
34650
35501
  sessionId,
@@ -34812,6 +35663,7 @@ var CORE_RECONSTRUCT_EVENTS = /* @__PURE__ */ new Set([
34812
35663
  "message_appended",
34813
35664
  "message_updated",
34814
35665
  "messages_replaced",
35666
+ "messages_dropped",
34815
35667
  "context_snapshot",
34816
35668
  "checkpoint",
34817
35669
  "file_snapshot",