@wrongstack/core 0.302.2 → 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 (52) hide show
  1. package/README.md +1 -1
  2. package/dist/chronicle/index.js +117 -30
  3. package/dist/chronicle/project-server.js +12 -5
  4. package/dist/coordination/agents/index.js +4313 -3516
  5. package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
  6. package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
  7. package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
  8. package/dist/coordination/agents/project-agent-consolidation.d.ts +29 -2
  9. package/dist/coordination/agents/project-agent-files.d.ts +12 -3
  10. package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
  11. package/dist/coordination/agents/project-agent-identity.d.ts +22 -9
  12. package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
  13. package/dist/coordination/agents/project-agent-learning-structured.d.ts +27 -1
  14. package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
  15. package/dist/coordination/agents/project-agent-skill-layer.d.ts +101 -0
  16. package/dist/coordination/agents/role-skills.d.ts +11 -1
  17. package/dist/coordination/index.d.ts +1 -1
  18. package/dist/coordination/index.js +2322 -1526
  19. package/dist/coordination/mail-tools.d.ts +1 -1
  20. package/dist/core/context.d.ts +4 -0
  21. package/dist/core/index.js +9 -0
  22. package/dist/defaults/index.js +895 -597
  23. package/dist/execution/index.js +2915 -2621
  24. package/dist/goal/index.js +7 -0
  25. package/dist/index.js +2406 -1331
  26. package/dist/kernel/events/agent-events.d.ts +28 -0
  27. package/dist/plugin/index.js +10 -4
  28. package/dist/security/index.js +69 -3
  29. package/dist/security/kanban-boundary.d.ts +5 -1
  30. package/dist/session-catalog/index.js +24 -2
  31. package/dist/session-catalog/project-server.js +27 -4
  32. package/dist/session-catalog/protocol.d.ts +9 -0
  33. package/dist/session-catalog/store.d.ts +17 -1
  34. package/dist/storage/index.js +81 -2
  35. package/dist/storage/plan-store.d.ts +1 -1
  36. package/dist/tasking/index.js +5 -0
  37. package/dist/tools/index.js +2824 -2604
  38. package/dist/types/config/root.d.ts +11 -1
  39. package/dist/types/config/skills-fleet-brain.d.ts +34 -0
  40. package/dist/types/config/ui.d.ts +14 -0
  41. package/dist/types/config.d.ts +1 -0
  42. package/dist/types/index.d.ts +2 -2
  43. package/dist/types/index.js +20 -0
  44. package/dist/types/multi-agent.d.ts +7 -0
  45. package/dist/types/task-graph.d.ts +2 -0
  46. package/dist/types/tool-executor.d.ts +2 -0
  47. package/dist/utils/index.js +3 -0
  48. package/instructions/system-lite.md +14 -8
  49. package/instructions/system-pro.md +17 -11
  50. package/instructions/system.md +17 -11
  51. package/package.json +3 -3
  52. package/skills/wrongstack-kanban/SKILL.md +39 -8
@@ -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) {
@@ -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 {
@@ -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
  }
@@ -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,12 +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 path23 from "node:path";
9775
+ import * as path24 from "node:path";
9551
9776
 
9552
9777
  // src/storage/session-store.ts
9553
9778
  import { randomUUID as randomUUID10 } from "node:crypto";
9554
9779
  import * as fsp17 from "node:fs/promises";
9555
- import * as path22 from "node:path";
9780
+ import * as path23 from "node:path";
9556
9781
 
9557
9782
  // src/security/file-permissions.ts
9558
9783
  import { chmod } from "node:fs/promises";
@@ -9886,13 +10111,13 @@ var DefaultSecretScrubber = class {
9886
10111
  import { spawn } from "node:child_process";
9887
10112
  import * as fs2 from "node:fs";
9888
10113
  import * as net from "node:net";
9889
- import * as path10 from "node:path";
10114
+ import * as path11 from "node:path";
9890
10115
  import { fileURLToPath as fileURLToPath3 } from "node:url";
9891
10116
 
9892
10117
  // src/session-catalog/endpoint.ts
9893
10118
  import { createHash as createHash2 } from "node:crypto";
9894
10119
  import * as os2 from "node:os";
9895
- import * as path9 from "node:path";
10120
+ import * as path10 from "node:path";
9896
10121
 
9897
10122
  // src/session-catalog/protocol.ts
9898
10123
  var SESSION_CATALOG_PROTOCOL_VERSION = 1;
@@ -9905,7 +10130,7 @@ function encodeSessionCatalogMessage(message) {
9905
10130
  // src/session-catalog/endpoint.ts
9906
10131
  var SESSION_CATALOG_METADATA_FILE = ".session-catalog-server.json";
9907
10132
  function normalizedPath(value) {
9908
- const resolved = path9.resolve(value);
10133
+ const resolved = path10.resolve(value);
9909
10134
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
9910
10135
  }
9911
10136
  function sessionCatalogProjectServerKey(projectDir) {
@@ -9916,10 +10141,10 @@ function sessionCatalogProjectServerEndpoint(projectDir) {
9916
10141
  if (process.platform === "win32") {
9917
10142
  return `\\\\.\\pipe\\wrongstack-session-catalog-v${SESSION_CATALOG_PROTOCOL_VERSION}-${key}`;
9918
10143
  }
9919
- return path9.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
10144
+ return path10.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
9920
10145
  }
9921
10146
  function sessionCatalogProjectServerMetadataPath(projectDir) {
9922
- return path9.join(projectDir, SESSION_CATALOG_METADATA_FILE);
10147
+ return path10.join(projectDir, SESSION_CATALOG_METADATA_FILE);
9923
10148
  }
9924
10149
 
9925
10150
  // src/session-catalog/client.ts
@@ -9954,7 +10179,7 @@ function resolveSessionCatalogProjectServerUrl(moduleUrl = import.meta.url, exis
9954
10179
  return availability.kind === "available" ? availability.url : null;
9955
10180
  }
9956
10181
  function normalize2(value) {
9957
- const resolved = path10.resolve(value);
10182
+ const resolved = path11.resolve(value);
9958
10183
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
9959
10184
  }
9960
10185
  function delay(ms) {
@@ -10471,12 +10696,12 @@ var color = {
10471
10696
 
10472
10697
  // src/utils/config-backup.ts
10473
10698
  import * as fs3 from "node:fs/promises";
10474
- import * as path11 from "node:path";
10699
+ import * as path12 from "node:path";
10475
10700
  function configHistoryDir(globalRoot) {
10476
- return path11.join(globalRoot, "config-history");
10701
+ return path12.join(globalRoot, "config-history");
10477
10702
  }
10478
10703
  function configSlug(absolutePath, globalRoot) {
10479
- const rel = path11.relative(globalRoot, absolutePath);
10704
+ const rel = path12.relative(globalRoot, absolutePath);
10480
10705
  const normalized = rel.replace(/\\/g, "/").replace(/\.json$/i, "");
10481
10706
  return normalized.replace(/\//g, "-");
10482
10707
  }
@@ -10492,7 +10717,7 @@ async function backupConfigFile(filePath, paths) {
10492
10717
  const ts = now.toISOString().replace(/[:.]/g, "-").replace(/Z$/, "");
10493
10718
  const slug = configSlug(filePath, paths.globalRoot);
10494
10719
  const backupDir = configHistoryDir(paths.globalRoot);
10495
- const backupFile = path11.join(backupDir, `${slug}-${ts}.json`);
10720
+ const backupFile = path12.join(backupDir, `${slug}-${ts}.json`);
10496
10721
  try {
10497
10722
  await fs3.mkdir(backupDir, { recursive: true });
10498
10723
  await fs3.writeFile(backupFile, currentContent, { mode: 384, encoding: "utf8" });
@@ -10559,11 +10784,6 @@ function deepMerge(base, patch, options = {}) {
10559
10784
  return out;
10560
10785
  }
10561
10786
 
10562
- // src/types/blocks.ts
10563
- function isTextBlock(b) {
10564
- return b.type === "text";
10565
- }
10566
-
10567
10787
  // src/utils/context-evidence.ts
10568
10788
  var MAX_DIGEST_CHARS = 4e3;
10569
10789
  function createContextEvidenceState() {
@@ -10790,7 +11010,7 @@ function isEmptyMessage(msg) {
10790
11010
  }
10791
11011
 
10792
11012
  // src/core/context.ts
10793
- import * as path12 from "node:path";
11013
+ import * as path13 from "node:path";
10794
11014
 
10795
11015
  // src/utils/tool-wire-compact.ts
10796
11016
  var TOOL_DESCRIPTION_MAX_CHARS = 400;
@@ -11893,6 +12113,12 @@ var Context = class _Context {
11893
12113
  setCurrentKanbanTask(taskId, boardId) {
11894
12114
  this.currentKanbanTaskId = taskId;
11895
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
+ });
11896
12122
  }
11897
12123
  /**
11898
12124
  * Record a comprehensive file event for the audit trail.
@@ -11986,11 +12212,11 @@ var Context = class _Context {
11986
12212
  * Returns the resolved absolute path.
11987
12213
  */
11988
12214
  setWorkingDir(dir) {
11989
- const resolved = path12.isAbsolute(dir) ? path12.resolve(dir) : path12.resolve(this.projectRoot, dir);
12215
+ const resolved = path13.isAbsolute(dir) ? path13.resolve(dir) : path13.resolve(this.projectRoot, dir);
11990
12216
  if (!this.allowOutsideProjectRoot) {
11991
- const root = path12.resolve(this.projectRoot);
11992
- const rel = path12.relative(root, resolved);
11993
- if (rel.startsWith("..") || path12.isAbsolute(rel)) {
12217
+ const root = path13.resolve(this.projectRoot);
12218
+ const rel = path13.relative(root, resolved);
12219
+ if (rel.startsWith("..") || path13.isAbsolute(rel)) {
11994
12220
  throw new Error(`Working directory "${resolved}" is outside project root "${root}"`);
11995
12221
  }
11996
12222
  }
@@ -12419,10 +12645,10 @@ function validateAgainstSchema(value, schema) {
12419
12645
  return { ok: errors.length === 0, errors };
12420
12646
  }
12421
12647
  var MAX_SCHEMA_DEPTH = 64;
12422
- function walk(value, schema, path44, errors, depth) {
12648
+ function walk(value, schema, path45, errors, depth) {
12423
12649
  if (depth > MAX_SCHEMA_DEPTH) {
12424
12650
  errors.push({
12425
- path: path44 || "<root>",
12651
+ path: path45 || "<root>",
12426
12652
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
12427
12653
  });
12428
12654
  return;
@@ -12430,7 +12656,7 @@ function walk(value, schema, path44, errors, depth) {
12430
12656
  if (schema.enum !== void 0) {
12431
12657
  if (!enumIncludes(schema.enum, value)) {
12432
12658
  errors.push({
12433
- path: path44 || "<root>",
12659
+ path: path45 || "<root>",
12434
12660
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
12435
12661
  });
12436
12662
  return;
@@ -12439,7 +12665,7 @@ function walk(value, schema, path44, errors, depth) {
12439
12665
  if (typeof schema.type === "string") {
12440
12666
  if (!checkType(value, schema.type)) {
12441
12667
  errors.push({
12442
- path: path44 || "<root>",
12668
+ path: path45 || "<root>",
12443
12669
  message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
12444
12670
  });
12445
12671
  return;
@@ -12451,7 +12677,7 @@ function walk(value, schema, path44, errors, depth) {
12451
12677
  if (!(req in obj)) {
12452
12678
  const expected = schema.properties?.[req]?.type;
12453
12679
  errors.push({
12454
- path: joinPath(path44, req),
12680
+ path: joinPath(path45, req),
12455
12681
  message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
12456
12682
  });
12457
12683
  }
@@ -12459,14 +12685,14 @@ function walk(value, schema, path44, errors, depth) {
12459
12685
  if (schema.properties) {
12460
12686
  for (const [key, subSchema] of Object.entries(schema.properties)) {
12461
12687
  if (key in obj) {
12462
- walk(obj[key], subSchema, joinPath(path44, key), errors, depth + 1);
12688
+ walk(obj[key], subSchema, joinPath(path45, key), errors, depth + 1);
12463
12689
  }
12464
12690
  }
12465
12691
  }
12466
12692
  }
12467
12693
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
12468
12694
  for (let i = 0; i < value.length; i++) {
12469
- walk(value[i], schema.items, `${path44}[${i}]`, errors, depth + 1);
12695
+ walk(value[i], schema.items, `${path45}[${i}]`, errors, depth + 1);
12470
12696
  }
12471
12697
  }
12472
12698
  }
@@ -12826,14 +13052,14 @@ function compileUserRegex(pattern, flags) {
12826
13052
  var MAX_SUBJECT_LEN = 64 * 1024;
12827
13053
 
12828
13054
  // src/utils/session-scoped-path.ts
12829
- import * as path13 from "node:path";
13055
+ import * as path14 from "node:path";
12830
13056
  function sessionScopedPath(dir, sessionId, suffix) {
12831
13057
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
12832
13058
  throw invalid(sessionId);
12833
13059
  }
12834
- const resolved = path13.resolve(dir, `${sessionId}${suffix}`);
12835
- const rel = path13.relative(path13.resolve(dir), resolved);
12836
- if (rel.startsWith("..") || path13.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)) {
12837
13063
  throw invalid(sessionId);
12838
13064
  }
12839
13065
  return resolved;
@@ -13588,6 +13814,9 @@ function subjectForToolInput(toolName, input, subjectKey) {
13588
13814
  const obj = input;
13589
13815
  if (subjectKey) {
13590
13816
  const value = obj[subjectKey];
13817
+ if (Array.isArray(value)) {
13818
+ return escapeGlobSubject(JSON.stringify(value));
13819
+ }
13591
13820
  if (typeof value === "string") {
13592
13821
  if (isPathSubjectKey(subjectKey)) {
13593
13822
  const normalized = normalizePathSubject(value);
@@ -13632,7 +13861,7 @@ function subjectForToolInput(toolName, input, subjectKey) {
13632
13861
  // src/storage/file-session-writer.ts
13633
13862
  import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
13634
13863
  import * as fsp7 from "node:fs/promises";
13635
- import * as path14 from "node:path";
13864
+ import * as path15 from "node:path";
13636
13865
  import { createInterface } from "node:readline";
13637
13866
 
13638
13867
  // src/storage/session-workspace-checkpoints.ts
@@ -13898,7 +14127,7 @@ var FileSessionWriter = class _FileSessionWriter {
13898
14127
  this.meta = meta;
13899
14128
  this.events = events;
13900
14129
  this.resumed = opts.resumed ?? false;
13901
- this.manifestFile = opts.dir ? path14.join(opts.dir, `${path14.basename(id)}.summary.json`) : "";
14130
+ this.manifestFile = opts.dir ? path15.join(opts.dir, `${path15.basename(id)}.summary.json`) : "";
13902
14131
  this.filePath = opts.filePath ?? "";
13903
14132
  this.secretScrubber = opts.secretScrubber;
13904
14133
  this.checkpointCas = opts.checkpointCas;
@@ -14790,7 +15019,7 @@ var FileSessionWriter = class _FileSessionWriter {
14790
15019
  import { spawn as spawn2 } from "node:child_process";
14791
15020
  import { createHash as createHash3, randomUUID as randomUUID9 } from "node:crypto";
14792
15021
  import * as fsp8 from "node:fs/promises";
14793
- import * as path15 from "node:path";
15022
+ import * as path16 from "node:path";
14794
15023
 
14795
15024
  // src/storage/storage-concurrency.ts
14796
15025
  async function mapWithConcurrency(items, concurrency, mapper) {
@@ -14821,13 +15050,13 @@ function sha256(content) {
14821
15050
  return createHash3("sha256").update(content).digest("hex");
14822
15051
  }
14823
15052
  function isInside(root, target) {
14824
- const relative8 = path15.relative(root, target);
14825
- return relative8 === "" || !relative8.startsWith("..") && !path15.isAbsolute(relative8);
15053
+ const relative8 = path16.relative(root, target);
15054
+ return relative8 === "" || !relative8.startsWith("..") && !path16.isAbsolute(relative8);
14826
15055
  }
14827
15056
  function normalizeRelative(input) {
14828
- if (!input || path15.isAbsolute(input)) return null;
15057
+ if (!input || path16.isAbsolute(input)) return null;
14829
15058
  const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
14830
- const resolved = path15.posix.normalize(normalized);
15059
+ const resolved = path16.posix.normalize(normalized);
14831
15060
  if (!resolved || resolved === "." || resolved === ".." || resolved.startsWith("../")) return null;
14832
15061
  return resolved;
14833
15062
  }
@@ -14842,8 +15071,8 @@ var SessionCheckpointCas = class {
14842
15071
  projectRoot;
14843
15072
  runGit;
14844
15073
  constructor(opts) {
14845
- this.rootDir = path15.resolve(opts.rootDir);
14846
- this.projectRoot = path15.resolve(opts.projectRoot);
15074
+ this.rootDir = path16.resolve(opts.rootDir);
15075
+ this.projectRoot = path16.resolve(opts.projectRoot);
14847
15076
  this.runGit = opts.runGit ?? defaultRunGit;
14848
15077
  }
14849
15078
  async capture(_sessionId, _promptIndex) {
@@ -14868,7 +15097,7 @@ var SessionCheckpointCas = class {
14868
15097
  relativePaths,
14869
15098
  CAPTURE_CONCURRENCY,
14870
15099
  async (relative8) => {
14871
- const absolute = path15.resolve(this.projectRoot, ...relative8.split("/"));
15100
+ const absolute = path16.resolve(this.projectRoot, ...relative8.split("/"));
14872
15101
  if (!isInside(this.projectRoot, absolute)) {
14873
15102
  unresolved.push({ path: relative8, reason: "path escapes project root" });
14874
15103
  return null;
@@ -14877,8 +15106,8 @@ var SessionCheckpointCas = class {
14877
15106
  const stat15 = await fsp8.lstat(absolute);
14878
15107
  if (stat15.isSymbolicLink()) {
14879
15108
  const linkTarget = await fsp8.readlink(absolute);
14880
- const resolvedLink = path15.resolve(path15.dirname(absolute), linkTarget);
14881
- if (path15.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
15109
+ const resolvedLink = path16.resolve(path16.dirname(absolute), linkTarget);
15110
+ if (path16.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
14882
15111
  unresolved.push({
14883
15112
  path: relative8,
14884
15113
  reason: "symlink target escapes project root"
@@ -14940,7 +15169,7 @@ var SessionCheckpointCas = class {
14940
15169
  };
14941
15170
  }
14942
15171
  async materialize(checkpoint, targetRoot) {
14943
- const target = path15.resolve(targetRoot);
15172
+ const target = path16.resolve(targetRoot);
14944
15173
  if (target === this.projectRoot) {
14945
15174
  throw new Error("Refusing to materialize a workspace checkpoint over the parent project root");
14946
15175
  }
@@ -14980,10 +15209,10 @@ var SessionCheckpointCas = class {
14980
15209
  try {
14981
15210
  const output = await this.safeOutputPath(target, realTarget, entry.path);
14982
15211
  if (entry.state === "symlink") {
14983
- if (path15.isAbsolute(entry.linkTarget)) {
15212
+ if (path16.isAbsolute(entry.linkTarget)) {
14984
15213
  throw new Error("absolute symlink target refused");
14985
15214
  }
14986
- const resolvedLink = path15.resolve(path15.dirname(output), entry.linkTarget);
15215
+ const resolvedLink = path16.resolve(path16.dirname(output), entry.linkTarget);
14987
15216
  if (!isInside(target, resolvedLink)) throw new Error("symlink target escapes checkpoint root");
14988
15217
  }
14989
15218
  prepared.push({
@@ -15011,7 +15240,7 @@ var SessionCheckpointCas = class {
15011
15240
  await fsp8.unlink(output).catch((err) => {
15012
15241
  if (err.code !== "ENOENT") throw err;
15013
15242
  });
15014
- await fsp8.mkdir(path15.dirname(output), { recursive: true });
15243
+ await fsp8.mkdir(path16.dirname(output), { recursive: true });
15015
15244
  await fsp8.symlink(entry.linkTarget, output);
15016
15245
  writtenFiles.push(entry.path);
15017
15246
  continue;
@@ -15028,15 +15257,15 @@ var SessionCheckpointCas = class {
15028
15257
  }
15029
15258
  objectPath(hash2) {
15030
15259
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid CAS object hash: ${hash2}`);
15031
- return path15.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));
15032
15261
  }
15033
15262
  manifestPath(hash2) {
15034
15263
  if (!HASH_RE.test(hash2)) throw new Error(`Invalid checkpoint manifest hash: ${hash2}`);
15035
- return path15.join(this.rootDir, "manifests", `${hash2}.json`);
15264
+ return path16.join(this.rootDir, "manifests", `${hash2}.json`);
15036
15265
  }
15037
15266
  async putBlob(hash2, content) {
15038
15267
  const target = this.objectPath(hash2);
15039
- await fsp8.mkdir(path15.dirname(target), { recursive: true });
15268
+ await fsp8.mkdir(path16.dirname(target), { recursive: true });
15040
15269
  try {
15041
15270
  const existing = await fsp8.readFile(target);
15042
15271
  if (sha256(existing) !== hash2) throw new Error(`Corrupt CAS object collision: ${hash2}`);
@@ -15044,9 +15273,9 @@ var SessionCheckpointCas = class {
15044
15273
  } catch (err) {
15045
15274
  if (err.code !== "ENOENT") throw err;
15046
15275
  }
15047
- const temp = path15.join(
15048
- path15.dirname(target),
15049
- `.${path15.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
15276
+ const temp = path16.join(
15277
+ path16.dirname(target),
15278
+ `.${path16.basename(target)}.${process.pid}.${randomUUID9()}.tmp`
15050
15279
  );
15051
15280
  let handle;
15052
15281
  try {
@@ -15107,7 +15336,7 @@ var SessionCheckpointCas = class {
15107
15336
  async safeOutputPath(target, realTarget, relative8) {
15108
15337
  const normalized = normalizeRelative(relative8);
15109
15338
  if (!normalized) throw new Error("invalid relative path");
15110
- const output = path15.resolve(target, ...normalized.split("/"));
15339
+ const output = path16.resolve(target, ...normalized.split("/"));
15111
15340
  if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
15112
15341
  let probe = output;
15113
15342
  for (; ; ) {
@@ -15117,7 +15346,7 @@ var SessionCheckpointCas = class {
15117
15346
  return output;
15118
15347
  } catch (err) {
15119
15348
  if (err.code !== "ENOENT") throw err;
15120
- const parent = path15.dirname(probe);
15349
+ const parent = path16.dirname(probe);
15121
15350
  if (parent === probe) throw err;
15122
15351
  probe = parent;
15123
15352
  }
@@ -15181,13 +15410,13 @@ function generateSessionId(startedAt, _model) {
15181
15410
  }
15182
15411
 
15183
15412
  // src/storage/session-id-resolver.ts
15184
- import * as path16 from "node:path";
15413
+ import * as path17 from "node:path";
15185
15414
  function resolveSessionId(query, candidateIds) {
15186
15415
  const normalized = query.trim();
15187
15416
  if (!normalized) return { status: "missing", query: normalized };
15188
15417
  const uniqueIds = [...new Set(candidateIds)];
15189
15418
  if (uniqueIds.includes(normalized)) return { status: "resolved", id: normalized };
15190
- const leaf = (id) => path16.posix.basename(id.replace(/\\/g, "/"));
15419
+ const leaf = (id) => path17.posix.basename(id.replace(/\\/g, "/"));
15191
15420
  const exactLeafMatches = uniqueIds.filter((id) => leaf(id) === normalized);
15192
15421
  if (exactLeafMatches.length === 1) {
15193
15422
  return { status: "resolved", id: exactLeafMatches[0] };
@@ -15241,13 +15470,13 @@ function scrubPersistedSessionSummary(summary, scrubber2) {
15241
15470
  // src/storage/session-resume-validation.ts
15242
15471
  import { createHash as createHash4 } from "node:crypto";
15243
15472
  import * as fsp9 from "node:fs/promises";
15244
- import * as path17 from "node:path";
15473
+ import * as path18 from "node:path";
15245
15474
  var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
15246
15475
  var VALIDATION_CONCURRENCY = 8;
15247
15476
  var NOTICE_PATH_LIMIT = 20;
15248
15477
  function isInside2(root, target) {
15249
- const relative8 = path17.relative(root, target);
15250
- return relative8 === "" || !relative8.startsWith("..") && !path17.isAbsolute(relative8);
15478
+ const relative8 = path18.relative(root, target);
15479
+ return relative8 === "" || !relative8.startsWith("..") && !path18.isAbsolute(relative8);
15251
15480
  }
15252
15481
  function errno(err) {
15253
15482
  return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
@@ -15258,7 +15487,7 @@ function latestObservations(events, projectRoot) {
15258
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)) {
15259
15488
  continue;
15260
15489
  }
15261
- const normalized = path17.resolve(projectRoot, event.path);
15490
+ const normalized = path18.resolve(projectRoot, event.path);
15262
15491
  latest.set(normalized, {
15263
15492
  path: normalized,
15264
15493
  hash: event.hash.toLowerCase(),
@@ -15316,7 +15545,7 @@ async function validateOne(observation, lexicalRoot, realRoot) {
15316
15545
  }
15317
15546
  }
15318
15547
  async function validateResumeFileObservations(events, projectRoot) {
15319
- const lexicalRoot = path17.resolve(projectRoot);
15548
+ const lexicalRoot = path18.resolve(projectRoot);
15320
15549
  const realRoot = await fsp9.realpath(lexicalRoot).catch(() => lexicalRoot);
15321
15550
  const observations = latestObservations(events, lexicalRoot);
15322
15551
  const results = await mapWithConcurrency(
@@ -15332,9 +15561,9 @@ async function validateResumeFileObservations(events, projectRoot) {
15332
15561
  }
15333
15562
  function formatResumeValidationNotice(validation, projectRoot) {
15334
15563
  if (validation.staleFiles.length === 0) return null;
15335
- const root = path17.resolve(projectRoot);
15564
+ const root = path18.resolve(projectRoot);
15336
15565
  const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
15337
- const relative8 = path17.relative(root, entry.path);
15566
+ const relative8 = path18.relative(root, entry.path);
15338
15567
  const display = isInside2(root, entry.path) ? relative8 || "." : entry.path;
15339
15568
  return `- ${JSON.stringify(display)} [${entry.status}]`;
15340
15569
  });
@@ -15359,22 +15588,22 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
15359
15588
 
15360
15589
  // src/storage/session-store/delete-session-artifacts.ts
15361
15590
  import * as fsp10 from "node:fs/promises";
15362
- import * as path19 from "node:path";
15591
+ import * as path20 from "node:path";
15363
15592
 
15364
15593
  // src/storage/session-store/paths.ts
15365
- import * as path18 from "node:path";
15594
+ import * as path19 from "node:path";
15366
15595
  function sessionPath(storeDir, id, ext) {
15367
15596
  return sessionScopedPath(storeDir, id, ext);
15368
15597
  }
15369
15598
  function shardManifestPath(storeDir, shardKey) {
15370
- return shardKey ? path18.join(storeDir, shardKey, "_manifest.json") : path18.join(storeDir, "_manifest.json");
15599
+ return shardKey ? path19.join(storeDir, shardKey, "_manifest.json") : path19.join(storeDir, "_manifest.json");
15371
15600
  }
15372
15601
  function shardKeyForSessionId(id) {
15373
- const dirName = path18.dirname(id);
15602
+ const dirName = path19.dirname(id);
15374
15603
  return dirName === "." ? "" : dirName;
15375
15604
  }
15376
15605
  async function ensureShardDir(storeDir, id) {
15377
- const dirPath = path18.dirname(sessionScopedPath(storeDir, id, ""));
15606
+ const dirPath = path19.dirname(sessionScopedPath(storeDir, id, ""));
15378
15607
  await ensureDir(dirPath);
15379
15608
  return dirPath;
15380
15609
  }
@@ -15385,9 +15614,9 @@ async function deleteSessionArtifacts({
15385
15614
  id,
15386
15615
  jsonlPath
15387
15616
  }) {
15388
- const shardDir = path19.dirname(jsonlPath);
15389
- const base = path19.basename(id);
15390
- const sessDir = path19.join(shardDir, base);
15617
+ const shardDir = path20.dirname(jsonlPath);
15618
+ const base = path20.basename(id);
15619
+ const sessDir = path20.join(shardDir, base);
15391
15620
  const deletions = [
15392
15621
  fsp10.unlink(jsonlPath),
15393
15622
  fsp10.unlink(sessionPath(rootDir, id, ".summary.json")),
@@ -15446,7 +15675,7 @@ function isSessionJsonlFileName(name) {
15446
15675
 
15447
15676
  // src/storage/session-store/directory-session-files.ts
15448
15677
  import * as fsp11 from "node:fs/promises";
15449
- import * as path20 from "node:path";
15678
+ import * as path21 from "node:path";
15450
15679
  function sessionIdForFile(prefix, name) {
15451
15680
  const base = name.replace(/\.jsonl$/, "");
15452
15681
  return prefix ? `${prefix}/${base}` : base;
@@ -15468,12 +15697,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
15468
15697
  if (entry.isDirectory()) {
15469
15698
  dirEntries.push(entry);
15470
15699
  } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
15471
- files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path20.join(dir, entry.name) });
15700
+ files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path21.join(dir, entry.name) });
15472
15701
  }
15473
15702
  }
15474
15703
  const childFileArrays = await Promise.all(
15475
15704
  dirEntries.map(
15476
- (entry) => collectSessionFiles(path20.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15705
+ (entry) => collectSessionFiles(path21.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15477
15706
  )
15478
15707
  );
15479
15708
  return [...childFileArrays.flat(), ...files];
@@ -15497,7 +15726,7 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
15497
15726
  }
15498
15727
  const childIdArrays = await Promise.all(
15499
15728
  dirEntries.map(
15500
- (entry) => collectSessionIds(path20.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15729
+ (entry) => collectSessionIds(path21.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
15501
15730
  )
15502
15731
  );
15503
15732
  return [...childIdArrays.flat(), ...fileIds];
@@ -16018,7 +16247,7 @@ function emitDamaged(params, detail) {
16018
16247
 
16019
16248
  // src/storage/session-store/prune-helpers.ts
16020
16249
  import * as fsp13 from "node:fs/promises";
16021
- import * as path21 from "node:path";
16250
+ import * as path22 from "node:path";
16022
16251
  function isPrunableSessionJsonl(name) {
16023
16252
  return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
16024
16253
  }
@@ -16026,7 +16255,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
16026
16255
  const cutoff = Date.now() - maxAgeDays * 864e5;
16027
16256
  let deleted = 0;
16028
16257
  const pruneFile = async (dir, name, prefix) => {
16029
- const jsonlPath = path21.join(dir, name);
16258
+ const jsonlPath = path22.join(dir, name);
16030
16259
  try {
16031
16260
  const stat15 = await fsp13.stat(jsonlPath);
16032
16261
  if (stat15.mtimeMs >= cutoff) return;
@@ -16045,7 +16274,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
16045
16274
  continue;
16046
16275
  }
16047
16276
  if (!entry.isDirectory()) continue;
16048
- const dateDir = path21.join(storeDir, entry.name);
16277
+ const dateDir = path22.join(storeDir, entry.name);
16049
16278
  const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
16050
16279
  for (const file of files) {
16051
16280
  if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
@@ -16054,7 +16283,7 @@ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
16054
16283
  }
16055
16284
  for (const entry of entries) {
16056
16285
  if (!entry.isDirectory()) continue;
16057
- const dateDir = path21.join(storeDir, entry.name);
16286
+ const dateDir = path22.join(storeDir, entry.name);
16058
16287
  try {
16059
16288
  const remaining = await fsp13.readdir(dateDir);
16060
16289
  if (remaining.length === 0) {
@@ -16407,9 +16636,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
16407
16636
  static LIST_SCAN_CONCURRENCY = 32;
16408
16637
  constructor(opts) {
16409
16638
  this.dir = opts.dir;
16410
- this.projectRoot = opts.projectRoot ? path22.resolve(opts.projectRoot) : void 0;
16639
+ this.projectRoot = opts.projectRoot ? path23.resolve(opts.projectRoot) : void 0;
16411
16640
  this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
16412
- rootDir: path22.join(this.dir, "_cas"),
16641
+ rootDir: path23.join(this.dir, "_cas"),
16413
16642
  projectRoot: this.projectRoot
16414
16643
  }) : void 0;
16415
16644
  this.events = opts.events;
@@ -16420,7 +16649,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16420
16649
  this.onAppendBatch = opts.onAppendBatch;
16421
16650
  const builtRuntime = import.meta.url.includes("/dist/");
16422
16651
  this.catalogClient = this.projectRoot && (builtRuntime || process.env["WRONGSTACK_SESSION_CATALOG_FORCE"] === "1") && resolveSessionCatalogProjectServerUrl() ? new SessionCatalogProjectClient({
16423
- projectDir: path22.dirname(this.dir),
16652
+ projectDir: path23.dirname(this.dir),
16424
16653
  projectRoot: this.projectRoot
16425
16654
  }) : void 0;
16426
16655
  }
@@ -16452,7 +16681,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16452
16681
  // ── Storage event helpers ───────────────────────────────────────────────────
16453
16682
  /** Absolute path to the session index file. */
16454
16683
  get indexFile() {
16455
- return path22.join(this.dir, "_index.jsonl");
16684
+ return path23.join(this.dir, "_index.jsonl");
16456
16685
  }
16457
16686
  /** Join session ID to its absolute path within the store directory. */
16458
16687
  sessionPath(id, ext) {
@@ -16665,7 +16894,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
16665
16894
  // Shard directory (sessions/<date>/) — must match create() so the
16666
16895
  // .summary.json sidecar lands next to the JSONL instead of the
16667
16896
  // sessions root (where summaryFor() would never find it).
16668
- dir: path22.dirname(file),
16897
+ dir: path23.dirname(file),
16669
16898
  filePath: file,
16670
16899
  secretScrubber: this.secretScrubber,
16671
16900
  checkpointCas: this.checkpointCas,
@@ -17065,7 +17294,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
17065
17294
  });
17066
17295
  }
17067
17296
  async collectSessionFilesInShard(shardKey) {
17068
- const dir = shardKey ? path22.join(this.dir, shardKey) : this.dir;
17297
+ const dir = shardKey ? path23.join(this.dir, shardKey) : this.dir;
17069
17298
  const entries = await this.collectSessionFiles(dir, shardKey);
17070
17299
  return shardKey ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`)) : entries.filter((entry) => !entry.id.includes("/"));
17071
17300
  }
@@ -17258,7 +17487,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
17258
17487
  const maintenance = this.catalogClient ? await this.catalogClient.call("acquire_maintenance", {
17259
17488
  sessionId: canonical,
17260
17489
  operation: "clear",
17261
- holderId: this.maintenanceHolderId
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
17262
17495
  }) : void 0;
17263
17496
  await this.ensureShardDir(canonical);
17264
17497
  const file = this.sessionPath(canonical, ".jsonl");
@@ -17348,9 +17581,9 @@ function makeDirectorSessionFactory(opts) {
17348
17581
  let dir;
17349
17582
  if (opts.store) {
17350
17583
  store = opts.store;
17351
- dir = opts.sessionsRoot ? path23.join(opts.sessionsRoot, runId) : "(caller-managed)";
17584
+ dir = opts.sessionsRoot ? path24.join(opts.sessionsRoot, runId) : "(caller-managed)";
17352
17585
  } else if (opts.sessionsRoot) {
17353
- dir = path23.join(opts.sessionsRoot, runId);
17586
+ dir = path24.join(opts.sessionsRoot, runId);
17354
17587
  store = new DefaultSessionStore({ dir });
17355
17588
  } else {
17356
17589
  throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
@@ -17374,7 +17607,7 @@ function makeDirectorSessionFactory(opts) {
17374
17607
  }
17375
17608
  async function readDirectorSubagentSession(args) {
17376
17609
  if (!args.sessionsRoot) return null;
17377
- const filePath = path23.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
17610
+ const filePath = path24.join(args.sessionsRoot, args.directorRunId, `${args.subagentId}.jsonl`);
17378
17611
  let raw;
17379
17612
  try {
17380
17613
  raw = await fsp18.readFile(filePath, "utf8");
@@ -21688,7 +21921,7 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
21688
21921
 
21689
21922
  // src/infrastructure/logger.ts
21690
21923
  import * as fsp20 from "node:fs/promises";
21691
- import * as path24 from "node:path";
21924
+ import * as path25 from "node:path";
21692
21925
  var LEVEL_RANK2 = {
21693
21926
  error: 0,
21694
21927
  warn: 1,
@@ -21759,7 +21992,7 @@ var DefaultLogger = class _DefaultLogger {
21759
21992
  this.stderr = opts.stderr !== false;
21760
21993
  this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
21761
21994
  if (this.file) {
21762
- const dir = path24.dirname(this.file);
21995
+ const dir = path25.dirname(this.file);
21763
21996
  this._tail = this._tail.then(async () => {
21764
21997
  await fsp20.mkdir(dir, { recursive: true });
21765
21998
  }).catch(() => void 0);
@@ -22519,7 +22752,7 @@ var KNOWN_TOKEN_NAMES = Object.values(KNOWN_TOKEN_GROUPS).flat();
22519
22752
  // src/execution/design-kit-loader.ts
22520
22753
  import { existsSync as existsSync4 } from "node:fs";
22521
22754
  import * as fs4 from "node:fs/promises";
22522
- import * as path25 from "node:path";
22755
+ import * as path26 from "node:path";
22523
22756
  import { fileURLToPath as fileURLToPath4 } from "node:url";
22524
22757
  var KIT_FILE = "KIT.md";
22525
22758
  var TOKENS_FILE = "tokens.json";
@@ -22625,7 +22858,7 @@ var DefaultDesignKitLoader = class {
22625
22858
  }
22626
22859
  for (const e of entries) {
22627
22860
  if (!e.isDirectory()) continue;
22628
- const kitFile = path25.join(dir, e.name, KIT_FILE);
22861
+ const kitFile = path26.join(dir, e.name, KIT_FILE);
22629
22862
  try {
22630
22863
  const raw = await fs4.readFile(kitFile, "utf8");
22631
22864
  const fm = parseKitFrontmatter(raw);
@@ -22700,7 +22933,7 @@ var DefaultDesignKitLoader = class {
22700
22933
  const m = await this.find(id);
22701
22934
  let tokens;
22702
22935
  if (m) {
22703
- const tokensPath = path25.join(path25.dirname(m.path), TOKENS_FILE);
22936
+ const tokensPath = path26.join(path26.dirname(m.path), TOKENS_FILE);
22704
22937
  try {
22705
22938
  const raw = await fs4.readFile(tokensPath, "utf8");
22706
22939
  tokens = JSON.parse(raw);
@@ -22762,12 +22995,12 @@ function mergeKitTokens(base, own) {
22762
22995
  }
22763
22996
  function resolveBundledDesignKitsDir() {
22764
22997
  try {
22765
- const here = path25.dirname(fileURLToPath4(import.meta.url));
22998
+ const here = path26.dirname(fileURLToPath4(import.meta.url));
22766
22999
  const candidates = [
22767
- path25.join(here, "design-kits"),
22768
- path25.join(here, "..", "design-kits"),
22769
- path25.join(here, "..", "..", "design-kits"),
22770
- path25.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")
22771
23004
  ];
22772
23005
  for (const c of candidates) {
22773
23006
  if (existsSync4(c)) return c;
@@ -22806,10 +23039,10 @@ function _resetDesignKitLoaderMemo() {
22806
23039
  // src/execution/design-project-store.ts
22807
23040
  import { existsSync as existsSync5 } from "node:fs";
22808
23041
  import * as fs5 from "node:fs/promises";
22809
- import * as path26 from "node:path";
23042
+ import * as path27 from "node:path";
22810
23043
  var DESIGN_DIR = ".design";
22811
23044
  function designProjectDir(projectRoot) {
22812
- return path26.join(projectRoot, DESIGN_DIR);
23045
+ return path27.join(projectRoot, DESIGN_DIR);
22813
23046
  }
22814
23047
  var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
22815
23048
  var rulesCache = /* @__PURE__ */ new Map();
@@ -22824,7 +23057,7 @@ async function loadProjectDesignRules(projectRoot) {
22824
23057
  let rules;
22825
23058
  for (const name of RULE_FILES) {
22826
23059
  try {
22827
- const txt = await fs5.readFile(path26.join(designProjectDir(projectRoot), name), "utf8");
23060
+ const txt = await fs5.readFile(path27.join(designProjectDir(projectRoot), name), "utf8");
22828
23061
  if (txt.trim()) {
22829
23062
  rules = txt.trim();
22830
23063
  break;
@@ -22850,7 +23083,7 @@ function parseOverrides(value) {
22850
23083
  }
22851
23084
  async function loadActiveKit(projectRoot) {
22852
23085
  try {
22853
- const raw = await fs5.readFile(path26.join(designProjectDir(projectRoot), "active.json"), "utf8");
23086
+ const raw = await fs5.readFile(path27.join(designProjectDir(projectRoot), "active.json"), "utf8");
22854
23087
  const parsed = JSON.parse(raw);
22855
23088
  if (parsed && typeof parsed.kit === "string") {
22856
23089
  return {
@@ -22884,7 +23117,7 @@ function applyTokenOverrides(tokens, overrides) {
22884
23117
  async function ensureDesignDir(projectRoot) {
22885
23118
  const dir = designProjectDir(projectRoot);
22886
23119
  await fs5.mkdir(dir, { recursive: true });
22887
- const gi = path26.join(dir, ".gitignore");
23120
+ const gi = path27.join(dir, ".gitignore");
22888
23121
  if (!existsSync5(gi)) {
22889
23122
  try {
22890
23123
  await fs5.writeFile(gi, "*\n");
@@ -22898,11 +23131,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
22898
23131
  const dir = await ensureDesignDir(projectRoot);
22899
23132
  const record = { kit, stack: stack ?? null };
22900
23133
  if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
22901
- await fs5.writeFile(path26.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
23134
+ await fs5.writeFile(path27.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22902
23135
  `);
22903
23136
  const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
22904
23137
  `;
22905
- await fs5.appendFile(path26.join(dir, "decisions.md"), line);
23138
+ await fs5.appendFile(path27.join(dir, "decisions.md"), line);
22906
23139
  } catch {
22907
23140
  }
22908
23141
  }
@@ -22918,11 +23151,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22918
23151
  const dir = await ensureDesignDir(projectRoot);
22919
23152
  const record = { kit: active.kit, stack: active.stack ?? null };
22920
23153
  if (Object.keys(merged).length > 0) record.overrides = merged;
22921
- await fs5.writeFile(path26.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
23154
+ await fs5.writeFile(path27.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
22922
23155
  `);
22923
23156
  const keys = Object.keys(patch).join(",");
22924
23157
  await fs5.appendFile(
22925
- path26.join(dir, "decisions.md"),
23158
+ path27.join(dir, "decisions.md"),
22926
23159
  `- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
22927
23160
  `
22928
23161
  );
@@ -22932,7 +23165,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
22932
23165
  }
22933
23166
  async function clearPersistedActiveKit(projectRoot) {
22934
23167
  try {
22935
- await fs5.rm(path26.join(designProjectDir(projectRoot), "active.json"), { force: true });
23168
+ await fs5.rm(path27.join(designProjectDir(projectRoot), "active.json"), { force: true });
22936
23169
  } catch {
22937
23170
  }
22938
23171
  }
@@ -23072,12 +23305,12 @@ function verifyFiles(tokens, files) {
23072
23305
  const violations = [];
23073
23306
  let onPalette = 0;
23074
23307
  let offPalette = 0;
23075
- for (const { path: path44, text } of files) {
23308
+ for (const { path: path45, text } of files) {
23076
23309
  const lines = text.split("\n");
23077
23310
  lines.forEach((lineText, i) => {
23078
23311
  const lineNo = i + 1;
23079
23312
  const flag = (snippet, reason, axis = "color") => {
23080
- violations.push({ file: path44, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
23313
+ violations.push({ file: path45, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
23081
23314
  };
23082
23315
  for (const re of [HEX_RE, FUNC_COLOR_RE]) {
23083
23316
  re.lastIndex = 0;
@@ -26960,7 +27193,7 @@ Summarize the following message range:`;
26960
27193
 
26961
27194
  // src/execution/skill-loader.ts
26962
27195
  import * as fs6 from "node:fs/promises";
26963
- import * as path27 from "node:path";
27196
+ import * as path28 from "node:path";
26964
27197
 
26965
27198
  // src/skills/foreign-sources.ts
26966
27199
  var FOREIGN_SKILL_TOOLS = [
@@ -27024,7 +27257,7 @@ async function entryIsDirectory(dir, entry) {
27024
27257
  if (entry.isDirectory()) return true;
27025
27258
  if (entry.isSymbolicLink()) {
27026
27259
  try {
27027
- return (await fs6.stat(path27.join(dir, entry.name))).isDirectory();
27260
+ return (await fs6.stat(path28.join(dir, entry.name))).isDirectory();
27028
27261
  } catch {
27029
27262
  return false;
27030
27263
  }
@@ -27048,7 +27281,7 @@ var DefaultSkillLoader = class {
27048
27281
  for (const tool of FOREIGN_SKILL_TOOLS) {
27049
27282
  if (!foreignIds.includes(tool.id)) continue;
27050
27283
  dirs.push({
27051
- dir: path27.join(root, "." + tool.id, tool.subdir),
27284
+ dir: path28.join(root, "." + tool.id, tool.subdir),
27052
27285
  source: "foreign",
27053
27286
  originTool: tool.id
27054
27287
  });
@@ -27078,7 +27311,7 @@ var DefaultSkillLoader = class {
27078
27311
  );
27079
27312
  for (const e of entries) {
27080
27313
  if (!await entryIsDirectory(dir, e)) continue;
27081
- const skillFile = path27.join(dir, e.name, "SKILL.md");
27314
+ const skillFile = path28.join(dir, e.name, "SKILL.md");
27082
27315
  let raw;
27083
27316
  try {
27084
27317
  raw = await fs6.readFile(skillFile, "utf8");
@@ -27210,7 +27443,7 @@ var DefaultSkillLoader = class {
27210
27443
  if (cached !== void 0) return cached;
27211
27444
  const m = await this.find(name);
27212
27445
  if (!m) throw new Error(`Skill "${name}" not found`);
27213
- const savePath = path27.join(path27.dirname(m.path), "SKILL.save.md");
27446
+ const savePath = path28.join(path28.dirname(m.path), "SKILL.save.md");
27214
27447
  let result;
27215
27448
  try {
27216
27449
  result = await fs6.readFile(savePath, "utf8");
@@ -27244,11 +27477,11 @@ function parseDescriptionFromText(desc) {
27244
27477
 
27245
27478
  // src/execution/prompt-loader.ts
27246
27479
  import * as fs8 from "node:fs/promises";
27247
- import * as path29 from "node:path";
27480
+ import * as path30 from "node:path";
27248
27481
 
27249
27482
  // src/storage/prompt-store.ts
27250
27483
  import * as fs7 from "node:fs/promises";
27251
- import * as path28 from "node:path";
27484
+ import * as path29 from "node:path";
27252
27485
  var SCHEMA_VERSION = 2;
27253
27486
  function migratePromptEntry(raw) {
27254
27487
  if (!raw || typeof raw !== "object") return null;
@@ -27314,7 +27547,7 @@ var DefaultPromptStore = class {
27314
27547
  if (!file.endsWith(".json")) continue;
27315
27548
  try {
27316
27549
  const raw = JSON.parse(
27317
- await fs7.readFile(path28.join(this.dir, file), "utf8")
27550
+ await fs7.readFile(path29.join(this.dir, file), "utf8")
27318
27551
  );
27319
27552
  const migrated = migratePromptEntry(raw.entry);
27320
27553
  if (migrated) entries.push(migrated);
@@ -27328,7 +27561,7 @@ var DefaultPromptStore = class {
27328
27561
  );
27329
27562
  }
27330
27563
  async get(id) {
27331
- const file = path28.join(this.dir, `${id}.json`);
27564
+ const file = path29.join(this.dir, `${id}.json`);
27332
27565
  try {
27333
27566
  const raw = JSON.parse(await fs7.readFile(file, "utf8"));
27334
27567
  return migratePromptEntry(raw.entry);
@@ -27338,12 +27571,12 @@ var DefaultPromptStore = class {
27338
27571
  }
27339
27572
  async save(entry) {
27340
27573
  await ensureDir(this.dir);
27341
- const file = path28.join(this.dir, `${entry.id}.json`);
27574
+ const file = path29.join(this.dir, `${entry.id}.json`);
27342
27575
  const raw = { version: SCHEMA_VERSION, entry };
27343
27576
  await atomicWrite(file, JSON.stringify(raw, null, 2));
27344
27577
  }
27345
27578
  async delete(id) {
27346
- const file = path28.join(this.dir, `${id}.json`);
27579
+ const file = path29.join(this.dir, `${id}.json`);
27347
27580
  try {
27348
27581
  await fs7.unlink(file);
27349
27582
  return true;
@@ -27432,7 +27665,7 @@ var DefaultPromptLoader = class {
27432
27665
  constructor(opts) {
27433
27666
  this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
27434
27667
  this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
27435
- this.builtinDir = opts.bundledDir ? path29.join(opts.bundledDir, "prompts") : void 0;
27668
+ this.builtinDir = opts.bundledDir ? path30.join(opts.bundledDir, "prompts") : void 0;
27436
27669
  }
27437
27670
  async list() {
27438
27671
  if (this.cache) return this.cache;
@@ -27564,7 +27797,7 @@ async function walkJson(dir) {
27564
27797
  return out;
27565
27798
  }
27566
27799
  for (const e of entries) {
27567
- const full = path29.join(dir, e.name);
27800
+ const full = path30.join(dir, e.name);
27568
27801
  if (e.isDirectory()) {
27569
27802
  out.push(...await walkJson(full));
27570
27803
  } else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
@@ -27753,7 +27986,7 @@ function readPolicy(ctx) {
27753
27986
  // src/execution/tool-executor.ts
27754
27987
  import { randomUUID as randomUUID17 } from "node:crypto";
27755
27988
  import * as fs10 from "node:fs/promises";
27756
- import * as path32 from "node:path";
27989
+ import * as path33 from "node:path";
27757
27990
  import { isDeepStrictEqual } from "node:util";
27758
27991
 
27759
27992
  // src/observability/process-telemetry.ts
@@ -27765,13 +27998,15 @@ function runWithProcessTelemetry(context, run) {
27765
27998
 
27766
27999
  // src/security/kanban-boundary.ts
27767
28000
  import { realpath as realpath3 } from "node:fs/promises";
27768
- import * as path30 from "node:path";
28001
+ import * as path31 from "node:path";
27769
28002
  import {
28003
+ evaluateContractGraphReadiness,
27770
28004
  evaluateKanbanBoundaryOpaque,
27771
28005
  evaluateKanbanBoundaryPath,
27772
28006
  readBoard,
27773
28007
  resolveKanbanBoundaryLayers
27774
28008
  } from "@wrongstack/kanban";
28009
+ var GOVERNANCE_CONTROL_TOOLS = /* @__PURE__ */ new Set(["kanban", "plan", "task", "todo"]);
27775
28010
  var PATH_KEYS = /* @__PURE__ */ new Set([
27776
28011
  "path",
27777
28012
  "paths",
@@ -27796,12 +28031,60 @@ var TOOL_PATH_KEYS = {
27796
28031
  // filesystem target in language_info. Keep ambiguous names tool-specific.
27797
28032
  language_info: ["target"]
27798
28033
  };
27799
- async function evaluateToolKanbanBoundary(tool, input, ctx) {
28034
+ async function evaluateToolKanbanBoundary(tool, input, ctx, options = {}) {
28035
+ if (tool.name === "kanban") return { decision: "allow" };
27800
28036
  const identity = resolveKanbanIdentity(ctx);
27801
- 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
+ }
27802
28044
  const board = await readBoard(identity.policyRoot ?? ctx.projectRoot, identity.boardId);
27803
- 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
+ }
27804
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
+ }
27805
28088
  if (identity.leaseId && task?.assignment) {
27806
28089
  const caps2 = tool.capabilities ?? [];
27807
28090
  const isWrite = caps2.includes("fs.write") || caps2.includes("fs.write.outside-project") || caps2.some((c) => c.startsWith("shell."));
@@ -27837,6 +28120,19 @@ async function evaluateToolKanbanBoundary(tool, input, ctx) {
27837
28120
  ...task ? { taskId: task.id } : {}
27838
28121
  };
27839
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
+ }
27840
28136
  function resolveKanbanIdentity(ctx) {
27841
28137
  const metaKanban = ctx.meta["kanban"];
27842
28138
  const record = metaKanban && typeof metaKanban === "object" ? metaKanban : void 0;
@@ -27854,10 +28150,10 @@ function resolveKanbanIdentity(ctx) {
27854
28150
  async function extractCandidatePaths(toolName, input, ctx) {
27855
28151
  if (toolName === "patch" && typeof input["patch"] === "string") {
27856
28152
  const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
27857
- const directory = path30.isAbsolute(directoryInput) ? directoryInput : path30.resolve(ctx.workingDir, directoryInput);
28153
+ const directory = path31.isAbsolute(directoryInput) ? directoryInput : path31.resolve(ctx.workingDir, directoryInput);
27858
28154
  const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
27859
28155
  const targets = extractPatchTargets(input["patch"], strip).map(
27860
- (target) => relativeToProject(path30.resolve(directory, target), ctx.projectRoot)
28156
+ (target) => relativeToProject(path31.resolve(directory, target), ctx.projectRoot)
27861
28157
  );
27862
28158
  return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
27863
28159
  }
@@ -27867,7 +28163,7 @@ async function extractCandidatePaths(toolName, input, ctx) {
27867
28163
  collectPathValues(input, values, pathKeys);
27868
28164
  if (toolName === "scaffold" && typeof input["name"] === "string") {
27869
28165
  const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
27870
- values.push(path30.join(cwd, input["name"]));
28166
+ values.push(path31.join(cwd, input["name"]));
27871
28167
  }
27872
28168
  const candidates = [
27873
28169
  ...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
@@ -27875,21 +28171,21 @@ async function extractCandidatePaths(toolName, input, ctx) {
27875
28171
  return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
27876
28172
  }
27877
28173
  async function canonicalizeCandidatePath(candidate, ctx) {
27878
- if (path30.isAbsolute(candidate)) return candidate;
27879
- const absolute = path30.resolve(ctx.projectRoot, candidate);
28174
+ if (path31.isAbsolute(candidate)) return candidate;
28175
+ const absolute = path31.resolve(ctx.projectRoot, candidate);
27880
28176
  const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
27881
28177
  let probe = absolute;
27882
28178
  const missingSegments = [];
27883
28179
  while (true) {
27884
28180
  try {
27885
- const canonical = path30.join(await realpath3(probe), ...missingSegments);
28181
+ const canonical = path31.join(await realpath3(probe), ...missingSegments);
27886
28182
  return relativeToProject(canonical, canonicalRoot);
27887
28183
  } catch (cause) {
27888
28184
  const code = cause.code;
27889
28185
  if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
27890
- const parent = path30.dirname(probe);
28186
+ const parent = path31.dirname(probe);
27891
28187
  if (parent === probe) return absolute;
27892
- missingSegments.unshift(path30.basename(probe));
28188
+ missingSegments.unshift(path31.basename(probe));
27893
28189
  probe = parent;
27894
28190
  }
27895
28191
  }
@@ -27912,12 +28208,12 @@ function splitPathList(value) {
27912
28208
  return value.split(",").map((item) => item.trim()).filter(Boolean);
27913
28209
  }
27914
28210
  function resolveInputPath(value, ctx) {
27915
- const absolute = path30.isAbsolute(value) ? value : path30.resolve(ctx.workingDir, value);
28211
+ const absolute = path31.isAbsolute(value) ? value : path31.resolve(ctx.workingDir, value);
27916
28212
  return relativeToProject(absolute, ctx.projectRoot);
27917
28213
  }
27918
28214
  function relativeToProject(absolute, projectRoot) {
27919
- const relative8 = path30.relative(projectRoot, absolute).replace(/\\/g, "/");
27920
- return relative8.startsWith("../") || path30.isAbsolute(relative8) ? absolute : relative8 || ".";
28215
+ const relative8 = path31.relative(projectRoot, absolute).replace(/\\/g, "/");
28216
+ return relative8.startsWith("../") || path31.isAbsolute(relative8) ? absolute : relative8 || ".";
27921
28217
  }
27922
28218
  function extractPatchTargets(patchText, strip) {
27923
28219
  const targets = [];
@@ -27942,7 +28238,7 @@ var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
27942
28238
  // src/execution/tool-executor-support.ts
27943
28239
  import { createHash as createHash8, randomUUID as randomUUID16 } from "node:crypto";
27944
28240
  import * as fs9 from "node:fs/promises";
27945
- import * as path31 from "node:path";
28241
+ import * as path32 from "node:path";
27946
28242
 
27947
28243
  // src/types/tool-markers.ts
27948
28244
  var MALFORMED_ARG_MARKERS = ["__raw", "__raw_arguments", "_raw"];
@@ -28067,11 +28363,11 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
28067
28363
  return content;
28068
28364
  }
28069
28365
  try {
28070
- const dir = path31.join(wstackGlobalRoot(), "tool-output");
28366
+ const dir = path32.join(wstackGlobalRoot(), "tool-output");
28071
28367
  await fs9.mkdir(dir, { recursive: true });
28072
28368
  const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
28073
28369
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
28074
- const filePath = path31.join(dir, `${stamp}-${safeTool}-${randomUUID16()}.log`);
28370
+ const filePath = path32.join(dir, `${stamp}-${safeTool}-${randomUUID16()}.log`);
28075
28371
  await fs9.writeFile(filePath, content, "utf8");
28076
28372
  const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
28077
28373
  const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
@@ -28130,6 +28426,41 @@ function sliceUtf8Suffix(text, maxBytes) {
28130
28426
  return text.slice(start);
28131
28427
  }
28132
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
+
28133
28464
  // src/execution/tool-executor-logging.ts
28134
28465
  function toolLogBase(ctx, use, toolName, durationMs) {
28135
28466
  return {
@@ -28235,41 +28566,6 @@ function blockedByHookResult(use, reason) {
28235
28566
  };
28236
28567
  }
28237
28568
 
28238
- // src/execution/tool-error-taxonomy.ts
28239
- function toolErrorResult(use, err, opts = {}) {
28240
- const { category, retryable, detail } = classifyToolError(err);
28241
- const rawMessage = err instanceof Error ? err.message : typeof err === "string" ? err : "Unknown error";
28242
- const userMessage = opts.scrubber ? opts.scrubber(rawMessage) : rawMessage;
28243
- const info = {
28244
- category,
28245
- retryable,
28246
- userMessage,
28247
- ...detail !== void 0 ? { detail } : {}
28248
- };
28249
- const prefix = formatErrorPrefix(category, retryable);
28250
- const detailSuffix = detail ? ` [${detail}]` : "";
28251
- return {
28252
- type: "tool_result",
28253
- tool_use_id: use.id,
28254
- content: `${prefix}: ${userMessage}${detailSuffix}`,
28255
- is_error: true,
28256
- // Attach structured info for programmatic consumers (retry logic,
28257
- // audit log, observability).
28258
- _toolErrorInfo: info
28259
- };
28260
- }
28261
- function formatErrorPrefix(category, retryable) {
28262
- const label = CATEGORY_LABELS[category] ?? "Error";
28263
- return retryable ? `${label} (retryable)` : label;
28264
- }
28265
- var CATEGORY_LABELS = {
28266
- ["transient" /* TRANSIENT */]: "Transient error",
28267
- ["not_found" /* NOT_FOUND */]: "Not found",
28268
- ["permission" /* PERMISSION */]: "Permission denied",
28269
- ["validation" /* VALIDATION */]: "Validation error",
28270
- ["fatal" /* FATAL */]: "Error"
28271
- };
28272
-
28273
28569
  // src/execution/tool-executor-stream.ts
28274
28570
  async function executeStreamedTool({
28275
28571
  tool,
@@ -28558,7 +28854,9 @@ ${errorDetails}`,
28558
28854
  return { result, tool, durationMs: Date.now() - start };
28559
28855
  }
28560
28856
  }
28561
- const boundary = await evaluateToolKanbanBoundary(tool, use.input, ctx);
28857
+ const boundary = await evaluateToolKanbanBoundary(tool, use.input, ctx, {
28858
+ requireGovernance: this.opts.requireKanbanGovernance
28859
+ });
28562
28860
  if (boundary.decision === "block") {
28563
28861
  const result = {
28564
28862
  type: "tool_result",
@@ -28669,7 +28967,7 @@ ${errorDetails}`,
28669
28967
  const inputPath = use.input && typeof use.input === "object" ? use.input.path : void 0;
28670
28968
  const caps = tool.capabilities ?? [];
28671
28969
  const hasFileCapability = caps.includes("fs.read") || caps.includes("fs.write");
28672
- const absPath = hasFileCapability && typeof inputPath === "string" ? path32.isAbsolute(inputPath) ? inputPath : path32.resolve(ctx.projectRoot, inputPath) : void 0;
28970
+ const absPath = hasFileCapability && typeof inputPath === "string" ? path33.isAbsolute(inputPath) ? inputPath : path33.resolve(ctx.projectRoot, inputPath) : void 0;
28673
28971
  let writeTargetExisted;
28674
28972
  if (tool.name === "write" && caps.includes("fs.write") && absPath) {
28675
28973
  writeTargetExisted = await fs10.stat(absPath).then(
@@ -29545,27 +29843,27 @@ function codexModelMeta(id) {
29545
29843
 
29546
29844
  // src/models/mode-store.ts
29547
29845
  import * as fs11 from "node:fs/promises";
29548
- import * as path34 from "node:path";
29846
+ import * as path35 from "node:path";
29549
29847
 
29550
29848
  // src/types/mode-prompts.ts
29551
- import { readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
29552
- import * as path33 from "node:path";
29849
+ import { readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
29850
+ import * as path34 from "node:path";
29553
29851
  import { fileURLToPath as fileURLToPath5 } from "node:url";
29554
29852
  function modePrompt(id) {
29555
29853
  for (const dir of modePromptDirCandidates()) {
29556
29854
  try {
29557
- return readFileSync8(path33.join(dir, `${id}.md`), "utf8").trimEnd();
29855
+ return readFileSync9(path34.join(dir, `${id}.md`), "utf8").trimEnd();
29558
29856
  } catch {
29559
29857
  }
29560
29858
  }
29561
29859
  return "";
29562
29860
  }
29563
29861
  function modePromptDirCandidates() {
29564
- const here = path33.dirname(fileURLToPath5(import.meta.url));
29862
+ const here = path34.dirname(fileURLToPath5(import.meta.url));
29565
29863
  const candidates = [
29566
- path33.resolve(here, "../../instructions/modes"),
29567
- path33.resolve(here, "../instructions/modes"),
29568
- path33.resolve(here, "instructions/modes")
29864
+ path34.resolve(here, "../../instructions/modes"),
29865
+ path34.resolve(here, "../instructions/modes"),
29866
+ path34.resolve(here, "instructions/modes")
29569
29867
  ];
29570
29868
  return candidates.sort((a, b) => Number(!isDirectory3(a)) - Number(!isDirectory3(b)));
29571
29869
  }
@@ -29797,7 +30095,7 @@ var DefaultModeStore = class {
29797
30095
  }
29798
30096
  async loadActiveMode() {
29799
30097
  try {
29800
- const configPath = path34.join(this.configDir, "mode.json");
30098
+ const configPath = path35.join(this.configDir, "mode.json");
29801
30099
  const content = await fs11.readFile(configPath, "utf8");
29802
30100
  const data = JSON.parse(content);
29803
30101
  this.activeModeId = data.activeMode ?? null;
@@ -29808,7 +30106,7 @@ var DefaultModeStore = class {
29808
30106
  async saveActiveMode() {
29809
30107
  try {
29810
30108
  await fs11.mkdir(this.configDir, { recursive: true });
29811
- const configPath = path34.join(this.configDir, "mode.json");
30109
+ const configPath = path35.join(this.configDir, "mode.json");
29812
30110
  await atomicWrite(
29813
30111
  configPath,
29814
30112
  JSON.stringify({ activeMode: this.activeModeId }, null, 2)
@@ -29823,11 +30121,11 @@ async function loadProjectModes(modesDir) {
29823
30121
  const entries = await fs11.readdir(modesDir);
29824
30122
  for (const entry of entries) {
29825
30123
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
29826
- const filePath = path34.join(modesDir, entry);
30124
+ const filePath = path35.join(modesDir, entry);
29827
30125
  const stat15 = await fs11.stat(filePath);
29828
30126
  if (!stat15.isFile()) continue;
29829
30127
  const content = await fs11.readFile(filePath, "utf8");
29830
- const id = path34.basename(entry, path34.extname(entry));
30128
+ const id = path35.basename(entry, path35.extname(entry));
29831
30129
  modes.push({
29832
30130
  id,
29833
30131
  name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
@@ -29843,7 +30141,7 @@ async function loadProjectModes(modesDir) {
29843
30141
  async function loadUserModes(modesDir) {
29844
30142
  const modes = [];
29845
30143
  try {
29846
- const manifestPath = path34.join(modesDir, "modes.json");
30144
+ const manifestPath = path35.join(modesDir, "modes.json");
29847
30145
  const content = await fs11.readFile(manifestPath, "utf8");
29848
30146
  const manifest = JSON.parse(content);
29849
30147
  for (const mode of manifest.modes) {
@@ -29856,7 +30154,7 @@ async function loadUserModes(modesDir) {
29856
30154
 
29857
30155
  // src/models/models-registry.ts
29858
30156
  import * as fs12 from "node:fs/promises";
29859
- import * as path35 from "node:path";
30157
+ import * as path36 from "node:path";
29860
30158
  var DEFAULT_URL = "https://models.dev/api.json";
29861
30159
  var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
29862
30160
  var DEFAULT_TTL_SECONDS = 3 * 3600;
@@ -29933,7 +30231,7 @@ var DefaultModelsRegistry = class {
29933
30231
  this.overlay = opts.overlay;
29934
30232
  this.overlayUrl = opts.overlayUrl;
29935
30233
  this.overlayFile = opts.overlayFile;
29936
- this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path35.join(path35.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);
29937
30235
  this.logger = opts.logger ?? noOpLogger;
29938
30236
  }
29939
30237
  async load(opts = {}) {
@@ -30208,7 +30506,7 @@ var DefaultModelsRegistry = class {
30208
30506
  }
30209
30507
  /** Used by `wstack models refresh` to expose where the cache lives. */
30210
30508
  cacheLocation() {
30211
- return path35.resolve(this.cacheFile);
30509
+ return path36.resolve(this.cacheFile);
30212
30510
  }
30213
30511
  };
30214
30512
  var REASONING_EFFORTS = /* @__PURE__ */ new Set([
@@ -30759,7 +31057,7 @@ async function startMetricsServer(opts) {
30759
31057
  const tls = opts.tls;
30760
31058
  const useHttps = !!(tls?.cert && tls?.key);
30761
31059
  const host = opts.host ?? "127.0.0.1";
30762
- const path44 = opts.path ?? "/metrics";
31060
+ const path45 = opts.path ?? "/metrics";
30763
31061
  const healthPath = opts.healthPath ?? "/healthz";
30764
31062
  const healthRegistry = opts.healthRegistry;
30765
31063
  const token = opts.token;
@@ -30782,7 +31080,7 @@ async function startMetricsServer(opts) {
30782
31080
  return;
30783
31081
  }
30784
31082
  const url = req.url.split("?")[0];
30785
- if (url === path44) {
31083
+ if (url === path45) {
30786
31084
  let body;
30787
31085
  try {
30788
31086
  body = renderPrometheus(opts.sink.snapshot());
@@ -30819,9 +31117,9 @@ async function startMetricsServer(opts) {
30819
31117
  let server;
30820
31118
  if (useHttps && tls) {
30821
31119
  const { createServer } = await import("node:https");
30822
- const { readFileSync: readFileSync10 } = await import("node:fs");
31120
+ const { readFileSync: readFileSync11 } = await import("node:fs");
30823
31121
  server = createServer(
30824
- { cert: readFileSync10(tls.cert), key: readFileSync10(tls.key) },
31122
+ { cert: readFileSync11(tls.cert), key: readFileSync11(tls.key) },
30825
31123
  listener
30826
31124
  );
30827
31125
  } else {
@@ -30846,7 +31144,7 @@ async function startMetricsServer(opts) {
30846
31144
  const protocol = useHttps ? "https" : "http";
30847
31145
  return {
30848
31146
  port: boundPort,
30849
- url: `${protocol}://${host}:${boundPort}${path44}`,
31147
+ url: `${protocol}://${host}:${boundPort}${path45}`,
30850
31148
  close: () => new Promise((resolve21, reject) => {
30851
31149
  server.close((err) => err ? reject(err) : resolve21());
30852
31150
  })
@@ -31119,7 +31417,7 @@ function startOtlpTraceExporter(opts) {
31119
31417
 
31120
31418
  // src/security/permission-policy.ts
31121
31419
  import * as fs13 from "node:fs/promises";
31122
- import * as path38 from "node:path";
31420
+ import * as path39 from "node:path";
31123
31421
 
31124
31422
  // src/security/permission-policy-schema.ts
31125
31423
  var TRUST_POLICY_LIMITS = Object.freeze({
@@ -31164,19 +31462,19 @@ var UNSAFE_PROPERTY_NAMES = /* @__PURE__ */ new Set(["__proto__", "prototype", "
31164
31462
  function isRecord5(value) {
31165
31463
  return typeof value === "object" && value !== null && !Array.isArray(value);
31166
31464
  }
31167
- function error(diagnostics, code, path44, message) {
31168
- diagnostics.push({ severity: "error", code, path: path44, message });
31465
+ function error(diagnostics, code, path45, message) {
31466
+ diagnostics.push({ severity: "error", code, path: path45, message });
31169
31467
  }
31170
- function validatePatterns(value, path44, diagnostics) {
31468
+ function validatePatterns(value, path45, diagnostics) {
31171
31469
  if (!Array.isArray(value)) {
31172
- error(diagnostics, "invalid_pattern_list", path44, "must be an array of strings");
31470
+ error(diagnostics, "invalid_pattern_list", path45, "must be an array of strings");
31173
31471
  return void 0;
31174
31472
  }
31175
31473
  if (value.length > TRUST_POLICY_LIMITS.maxPatternsPerRule) {
31176
31474
  error(
31177
31475
  diagnostics,
31178
31476
  "too_many_patterns",
31179
- path44,
31477
+ path45,
31180
31478
  `must contain at most ${TRUST_POLICY_LIMITS.maxPatternsPerRule} patterns`
31181
31479
  );
31182
31480
  return void 0;
@@ -31184,7 +31482,7 @@ function validatePatterns(value, path44, diagnostics) {
31184
31482
  const patterns = [];
31185
31483
  const seen = /* @__PURE__ */ new Set();
31186
31484
  for (const [index, pattern] of value.entries()) {
31187
- const itemPath = `${path44}[${index}]`;
31485
+ const itemPath = `${path45}[${index}]`;
31188
31486
  if (typeof pattern !== "string" || pattern.length === 0 || pattern.length > TRUST_POLICY_LIMITS.maxPatternChars) {
31189
31487
  error(
31190
31488
  diagnostics,
@@ -31288,7 +31586,7 @@ function validateTrustPolicy(value) {
31288
31586
  }
31289
31587
 
31290
31588
  // src/security/yolo-risk.ts
31291
- import * as path36 from "node:path";
31589
+ import * as path37 from "node:path";
31292
31590
  var CATASTROPHIC_PATTERNS = [
31293
31591
  /\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
31294
31592
  // make a filesystem — wipes a partition
@@ -31348,9 +31646,9 @@ function getInputString(input, key) {
31348
31646
  function pathLooksInsideProject(rawPath, projectRoot) {
31349
31647
  if (!projectRoot) return false;
31350
31648
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
31351
- const resolved = path36.resolve(projectRoot, rawPath);
31352
- const relative8 = path36.relative(projectRoot, resolved);
31353
- return !!relative8 && !relative8.startsWith("..") && !path36.isAbsolute(relative8);
31649
+ const resolved = path37.resolve(projectRoot, rawPath);
31650
+ const relative8 = path37.relative(projectRoot, resolved);
31651
+ return !!relative8 && !relative8.startsWith("..") && !path37.isAbsolute(relative8);
31354
31652
  }
31355
31653
  function tokenizeShell(command) {
31356
31654
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -31600,7 +31898,7 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
31600
31898
 
31601
31899
  // src/security/permission-helpers.ts
31602
31900
  import { realpathSync } from "node:fs";
31603
- import * as path37 from "node:path";
31901
+ import * as path38 from "node:path";
31604
31902
  function matchesTrust(patterns, subject) {
31605
31903
  return patterns.includes(subject) || matchAny(patterns, subject);
31606
31904
  }
@@ -31684,24 +31982,24 @@ function realpathOfNearestExisting(p) {
31684
31982
  const tail = [];
31685
31983
  for (; ; ) {
31686
31984
  try {
31687
- return tail.length === 0 ? realpathSync(probe) : path37.join(realpathSync(probe), ...tail);
31985
+ return tail.length === 0 ? realpathSync(probe) : path38.join(realpathSync(probe), ...tail);
31688
31986
  } catch {
31689
- const parent = path37.dirname(probe);
31987
+ const parent = path38.dirname(probe);
31690
31988
  if (parent === probe) return p;
31691
- tail.unshift(path37.basename(probe));
31989
+ tail.unshift(path38.basename(probe));
31692
31990
  probe = parent;
31693
31991
  }
31694
31992
  }
31695
31993
  }
31696
31994
  var agentStateRootRealCache = /* @__PURE__ */ new Map();
31697
31995
  function isInsideAgentStateRoot(absPath) {
31698
- const lexicalRoot = normalizeForCompare(path37.resolve(wstackGlobalRoot()));
31996
+ const lexicalRoot = normalizeForCompare(path38.resolve(wstackGlobalRoot()));
31699
31997
  if (!lexicalRoot) return false;
31700
31998
  const target = normalizeForCompare(absPath);
31701
31999
  if (target === lexicalRoot || target.startsWith(`${lexicalRoot}/`)) return true;
31702
32000
  let realRoot = agentStateRootRealCache.get(lexicalRoot);
31703
32001
  if (realRoot === void 0) {
31704
- realRoot = normalizeForCompare(realpathOfNearestExisting(path37.resolve(wstackGlobalRoot())));
32002
+ realRoot = normalizeForCompare(realpathOfNearestExisting(path38.resolve(wstackGlobalRoot())));
31705
32003
  agentStateRootRealCache.set(lexicalRoot, realRoot);
31706
32004
  }
31707
32005
  if (!realRoot || realRoot === lexicalRoot) return false;
@@ -31710,7 +32008,7 @@ function isInsideAgentStateRoot(absPath) {
31710
32008
  }
31711
32009
  function isProtectedAgentStatePath(absPath) {
31712
32010
  if (!isInsideAgentStateRoot(absPath)) return false;
31713
- return AGENT_STATE_SENSITIVE_BASENAMES.test(path37.basename(normalizeForCompare(absPath)));
32011
+ return AGENT_STATE_SENSITIVE_BASENAMES.test(path38.basename(normalizeForCompare(absPath)));
31714
32012
  }
31715
32013
  function pathLooksSensitive(rawPath) {
31716
32014
  const normalized = stripShellQuotes(rawPath).replace(/\\/g, "/");
@@ -31871,7 +32169,7 @@ var DefaultPermissionPolicy = class {
31871
32169
  if (!hasCapability(tool, ToolCapabilities.FS_WRITE)) return false;
31872
32170
  for (const targetPath of fsWriteTargetPaths(input)) {
31873
32171
  const base = ctx.workingDir ?? ctx.cwd;
31874
- const resolved = base ? path38.resolve(base, targetPath) : path38.resolve(targetPath);
32172
+ const resolved = base ? path39.resolve(base, targetPath) : path39.resolve(targetPath);
31875
32173
  if (isInsideAgentStateRoot(resolved)) return true;
31876
32174
  }
31877
32175
  return false;
@@ -32540,7 +32838,7 @@ var DefaultPermissionPolicy = class {
32540
32838
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scryptSync } from "node:crypto";
32541
32839
  import * as fs14 from "node:fs";
32542
32840
  import * as fsp22 from "node:fs/promises";
32543
- import * as path39 from "node:path";
32841
+ import * as path40 from "node:path";
32544
32842
 
32545
32843
  // src/types/secret-vault.ts
32546
32844
  var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
@@ -32833,7 +33131,7 @@ var DefaultSecretVault = class {
32833
33131
  const oldVersion = this._keyVersion;
32834
33132
  const newKey = randomBytes3(KEY_BYTES);
32835
33133
  const newVersion = oldVersion + 1;
32836
- fs14.mkdirSync(path39.dirname(this.keyFile), { recursive: true });
33134
+ fs14.mkdirSync(path40.dirname(this.keyFile), { recursive: true });
32837
33135
  const passphrase = getVaultPassphrase();
32838
33136
  if (passphrase) {
32839
33137
  writeKeyFileAtomicSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase));
@@ -32920,7 +33218,7 @@ var DefaultSecretVault = class {
32920
33218
  } catch (err) {
32921
33219
  if (err.code !== "ENOENT") throw err;
32922
33220
  }
32923
- fs14.mkdirSync(path39.dirname(this.keyFile), { recursive: true });
33221
+ fs14.mkdirSync(path40.dirname(this.keyFile), { recursive: true });
32924
33222
  const key = randomBytes3(KEY_BYTES);
32925
33223
  const passphrase = getVaultPassphrase();
32926
33224
  const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
@@ -32980,7 +33278,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
32980
33278
  }
32981
33279
  const merged = deepMerge(current, patch ?? {});
32982
33280
  const encrypted = encryptConfigSecrets(merged, vault);
32983
- await fsp22.mkdir(path39.dirname(configPath), { recursive: true });
33281
+ await fsp22.mkdir(path40.dirname(configPath), { recursive: true });
32984
33282
  await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
32985
33283
  await restrictFilePermissions2(configPath);
32986
33284
  await vault.flushHardening?.();
@@ -33035,7 +33333,7 @@ function walkCount(node, vault, counter) {
33035
33333
  // src/storage/attachment-store.ts
33036
33334
  import { randomBytes as randomBytes4 } from "node:crypto";
33037
33335
  import * as fsp23 from "node:fs/promises";
33038
- import * as path40 from "node:path";
33336
+ import * as path41 from "node:path";
33039
33337
  var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
33040
33338
  var PLACEHOLDER_RE2 = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
33041
33339
  var DefaultAttachmentStore = class {
@@ -33056,7 +33354,7 @@ var DefaultAttachmentStore = class {
33056
33354
  let data = input.data;
33057
33355
  if (this.spoolDir && bytes >= this.spoolThreshold) {
33058
33356
  await fsp23.mkdir(this.spoolDir, { recursive: true });
33059
- spooledPath = path40.join(this.spoolDir, `${id}.bin`);
33357
+ spooledPath = path41.join(this.spoolDir, `${id}.bin`);
33060
33358
  await atomicWrite(spooledPath, input.data, {
33061
33359
  encoding: input.kind === "image" ? "base64" : "utf8"
33062
33360
  });
@@ -33594,8 +33892,8 @@ var IN_PROJECT_DENIED_PATHS = [
33594
33892
  reason: "The other half of the filesystem confinement switch."
33595
33893
  }
33596
33894
  ];
33597
- function deleteNestedPath(target, path44) {
33598
- const segments = path44.split(".");
33895
+ function deleteNestedPath(target, path45) {
33896
+ const segments = path45.split(".");
33599
33897
  const last = segments[segments.length - 1];
33600
33898
  if (last === void 0) return false;
33601
33899
  let cursor = target;
@@ -33652,16 +33950,16 @@ function assertInProjectAllowListComplete() {
33652
33950
  );
33653
33951
  }
33654
33952
  const orphanedPaths = IN_PROJECT_DENIED_PATHS.filter(
33655
- ({ path: path44 }) => !IN_PROJECT_ALLOWED_KEYS.has(path44.split(".")[0] ?? "")
33656
- ).map(({ path: path44 }) => path44);
33953
+ ({ path: path45 }) => !IN_PROJECT_ALLOWED_KEYS.has(path45.split(".")[0] ?? "")
33954
+ ).map(({ path: path45 }) => path45);
33657
33955
  if (orphanedPaths.length > 0) {
33658
33956
  problems.push(
33659
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."
33660
33958
  );
33661
33959
  }
33662
33960
  const malformedPaths = IN_PROJECT_DENIED_PATHS.filter(
33663
- ({ path: path44 }) => path44.split(".").length < 2 || path44.split(".").some((s) => s.length === 0)
33664
- ).map(({ path: path44 }) => path44);
33961
+ ({ path: path45 }) => path45.split(".").length < 2 || path45.split(".").some((s) => s.length === 0)
33962
+ ).map(({ path: path45 }) => path45);
33665
33963
  if (malformedPaths.length > 0) {
33666
33964
  problems.push(
33667
33965
  `IN_PROJECT_DENIED_PATHS entr(ies) are not dotted nested paths: ` + malformedPaths.join(", ") + ". Top-level keys belong in KNOWN_DENIED_IN_PROJECT instead."
@@ -33689,8 +33987,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
33689
33987
  }
33690
33988
  stripped.push(k);
33691
33989
  }
33692
- for (const { path: path44 } of IN_PROJECT_DENIED_PATHS) {
33693
- if (deleteNestedPath(out, path44)) stripped.push(path44);
33990
+ for (const { path: path45 } of IN_PROJECT_DENIED_PATHS) {
33991
+ if (deleteNestedPath(out, path45)) stripped.push(path45);
33694
33992
  }
33695
33993
  if (stripped.length > 0) {
33696
33994
  warn(
@@ -33879,10 +34177,10 @@ function removeLegacySageEngine(config) {
33879
34177
  }
33880
34178
 
33881
34179
  // src/storage/config-loader/path-identity.ts
33882
- import * as path41 from "node:path";
34180
+ import * as path42 from "node:path";
33883
34181
  function samePath(a, b) {
33884
- let ra = path41.resolve(a);
33885
- let rb = path41.resolve(b);
34182
+ let ra = path42.resolve(a);
34183
+ let rb = path42.resolve(b);
33886
34184
  if (process.platform === "win32" || process.platform === "darwin") {
33887
34185
  ra = ra.toLowerCase();
33888
34186
  rb = rb.toLowerCase();
@@ -34564,8 +34862,8 @@ function deepFreeze(obj) {
34564
34862
  }
34565
34863
 
34566
34864
  // src/storage/plan-store.ts
34567
- import * as fsp24 from "node:fs/promises";
34568
34865
  import { randomUUID as randomUUID18 } from "node:crypto";
34866
+ import * as fsp24 from "node:fs/promises";
34569
34867
  async function loadPlan(filePath, events) {
34570
34868
  const t0 = Date.now();
34571
34869
  let raw;
@@ -34863,7 +35161,7 @@ ${cat}:`);
34863
35161
 
34864
35162
  // src/storage/queue-store.ts
34865
35163
  import * as fsp25 from "node:fs/promises";
34866
- import * as path42 from "node:path";
35164
+ import * as path43 from "node:path";
34867
35165
  var QUEUE_MAX_ITEMS = 100;
34868
35166
  var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
34869
35167
  var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
@@ -34896,7 +35194,7 @@ var QueueStore = class {
34896
35194
  traceId;
34897
35195
  logger;
34898
35196
  constructor(opts) {
34899
- this.file = path42.join(opts.dir, "queue.json");
35197
+ this.file = path43.join(opts.dir, "queue.json");
34900
35198
  this.events = opts.events;
34901
35199
  this.traceId = opts.traceId;
34902
35200
  this.logger = opts.logger;
@@ -35112,7 +35410,7 @@ function isPersistedQueueItem(v) {
35112
35410
  // src/storage/recovery-lock.ts
35113
35411
  import * as fsp26 from "node:fs/promises";
35114
35412
  import * as os3 from "node:os";
35115
- import * as path43 from "node:path";
35413
+ import * as path44 from "node:path";
35116
35414
  var LOCK_FILE = "active.json";
35117
35415
  var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
35118
35416
  var RecoveryLock = class {
@@ -35123,7 +35421,7 @@ var RecoveryLock = class {
35123
35421
  sessionStore;
35124
35422
  probe;
35125
35423
  constructor(opts) {
35126
- this.file = path43.join(opts.dir, LOCK_FILE);
35424
+ this.file = path44.join(opts.dir, LOCK_FILE);
35127
35425
  this.pid = opts.pid ?? process.pid;
35128
35426
  this.hostname = opts.hostname ?? os3.hostname();
35129
35427
  this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
@@ -35197,7 +35495,7 @@ var RecoveryLock = class {
35197
35495
  * null return before calling this.
35198
35496
  */
35199
35497
  async write(sessionId) {
35200
- await ensureDir(path43.dirname(this.file));
35498
+ await ensureDir(path44.dirname(this.file));
35201
35499
  const lock = {
35202
35500
  v: 1,
35203
35501
  sessionId,