@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.
- package/README.md +1 -1
- package/dist/chronicle/index.js +117 -30
- package/dist/chronicle/project-server.js +12 -5
- package/dist/coordination/agents/index.js +4313 -3516
- package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
- package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
- package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
- package/dist/coordination/agents/project-agent-consolidation.d.ts +29 -2
- package/dist/coordination/agents/project-agent-files.d.ts +12 -3
- package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
- package/dist/coordination/agents/project-agent-identity.d.ts +22 -9
- package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
- package/dist/coordination/agents/project-agent-learning-structured.d.ts +27 -1
- package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
- package/dist/coordination/agents/project-agent-skill-layer.d.ts +101 -0
- package/dist/coordination/agents/role-skills.d.ts +11 -1
- package/dist/coordination/index.d.ts +1 -1
- package/dist/coordination/index.js +2322 -1526
- package/dist/coordination/mail-tools.d.ts +1 -1
- package/dist/core/context.d.ts +4 -0
- package/dist/core/index.js +9 -0
- package/dist/defaults/index.js +895 -597
- package/dist/execution/index.js +2915 -2621
- package/dist/goal/index.js +7 -0
- package/dist/index.js +2406 -1331
- package/dist/kernel/events/agent-events.d.ts +28 -0
- package/dist/plugin/index.js +10 -4
- package/dist/security/index.js +69 -3
- package/dist/security/kanban-boundary.d.ts +5 -1
- package/dist/session-catalog/index.js +24 -2
- package/dist/session-catalog/project-server.js +27 -4
- package/dist/session-catalog/protocol.d.ts +9 -0
- package/dist/session-catalog/store.d.ts +17 -1
- package/dist/storage/index.js +81 -2
- package/dist/storage/plan-store.d.ts +1 -1
- package/dist/tasking/index.js +5 -0
- package/dist/tools/index.js +2824 -2604
- package/dist/types/config/root.d.ts +11 -1
- package/dist/types/config/skills-fleet-brain.d.ts +34 -0
- package/dist/types/config/ui.d.ts +14 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +20 -0
- package/dist/types/multi-agent.d.ts +7 -0
- package/dist/types/task-graph.d.ts +2 -0
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/instructions/system-lite.md +14 -8
- package/instructions/system-pro.md +17 -11
- package/instructions/system.md +17 -11
- package/package.json +3 -3
- package/skills/wrongstack-kanban/SKILL.md +39 -8
package/dist/defaults/index.js
CHANGED
|
@@ -1277,8 +1277,8 @@ function defaultFormatTaskInput(task) {
|
|
|
1277
1277
|
}
|
|
1278
1278
|
|
|
1279
1279
|
// src/coordination/agents/agent-prompts.ts
|
|
1280
|
-
import { readFileSync as
|
|
1281
|
-
import * as
|
|
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
|
|
1425
|
-
import * as
|
|
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 =
|
|
2294
|
+
const identityPath = path5.join(roleDir(role, projectRoot), "identity.md");
|
|
1837
2295
|
try {
|
|
1838
|
-
return
|
|
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 =
|
|
2302
|
+
const learnedPath = path5.join(roleDir(role, projectRoot), "learned.md");
|
|
1845
2303
|
try {
|
|
1846
|
-
return
|
|
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 =
|
|
2310
|
+
const projectPath = path5.join(roleDir(role, projectRoot), "knowledge.json");
|
|
1853
2311
|
try {
|
|
1854
|
-
const raw =
|
|
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
|
|
1885
|
-
|
|
2343
|
+
const rawEntries = parseStructuredLearnedEntriesFromContent(
|
|
2344
|
+
rawLearned,
|
|
2345
|
+
splitLearnedEntries(rawLearned)
|
|
2346
|
+
);
|
|
1886
2347
|
const rawBytes = Buffer.byteLength(rawLearned, "utf8");
|
|
1887
|
-
const
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
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
|
-
${
|
|
1898
|
-
|
|
1899
|
-
learnedContent = rawLearned;
|
|
1900
|
-
}
|
|
2361
|
+
${delta}`;
|
|
2362
|
+
learnedLabel = "Consolidated knowledge for this project";
|
|
1901
2363
|
} else {
|
|
1902
|
-
learnedContent =
|
|
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 =
|
|
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 =
|
|
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
|
|
1967
|
-
const
|
|
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
|
-
##
|
|
2461
|
+
## Project-developed skills
|
|
1972
2462
|
|
|
1973
|
-
|
|
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 =
|
|
2552
|
+
const here = path6.dirname(fileURLToPath(import.meta.url));
|
|
2064
2553
|
const profileInstructions = resolveWstackPaths({ projectRoot: process.cwd() }).globalInstructions;
|
|
2065
2554
|
return [
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
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 =
|
|
2565
|
+
const here = path6.dirname(fileURLToPath(import.meta.url));
|
|
2077
2566
|
const roots = [
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
2628
|
+
const here = path6.dirname(fileURLToPath(import.meta.url));
|
|
2140
2629
|
const explicitDir = envDir || void 0;
|
|
2141
2630
|
const candidates = [
|
|
2142
|
-
...explicitDir ? [
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
7922
|
-
import * as
|
|
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 =
|
|
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 =
|
|
8173
|
+
const here = path9.dirname(fileURLToPath2(import.meta.url));
|
|
7949
8174
|
const candidates = [
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
10144
|
+
return path10.join(os2.tmpdir(), `wssc-v${SESSION_CATALOG_PROTOCOL_VERSION}`, `${key}.sock`);
|
|
9920
10145
|
}
|
|
9921
10146
|
function sessionCatalogProjectServerMetadataPath(projectDir) {
|
|
9922
|
-
return
|
|
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 =
|
|
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
|
|
10699
|
+
import * as path12 from "node:path";
|
|
10475
10700
|
function configHistoryDir(globalRoot) {
|
|
10476
|
-
return
|
|
10701
|
+
return path12.join(globalRoot, "config-history");
|
|
10477
10702
|
}
|
|
10478
10703
|
function configSlug(absolutePath, globalRoot) {
|
|
10479
|
-
const rel =
|
|
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 =
|
|
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
|
|
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 =
|
|
12215
|
+
const resolved = path13.isAbsolute(dir) ? path13.resolve(dir) : path13.resolve(this.projectRoot, dir);
|
|
11990
12216
|
if (!this.allowOutsideProjectRoot) {
|
|
11991
|
-
const root =
|
|
11992
|
-
const rel =
|
|
11993
|
-
if (rel.startsWith("..") ||
|
|
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,
|
|
12648
|
+
function walk(value, schema, path45, errors, depth) {
|
|
12423
12649
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
12424
12650
|
errors.push({
|
|
12425
|
-
path:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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, `${
|
|
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
|
|
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 =
|
|
12835
|
-
const rel =
|
|
12836
|
-
if (rel.startsWith("..") ||
|
|
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
|
|
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 ?
|
|
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
|
|
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 =
|
|
14825
|
-
return relative8 === "" || !relative8.startsWith("..") && !
|
|
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 ||
|
|
15057
|
+
if (!input || path16.isAbsolute(input)) return null;
|
|
14829
15058
|
const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
14830
|
-
const resolved =
|
|
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 =
|
|
14846
|
-
this.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 =
|
|
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 =
|
|
14881
|
-
if (
|
|
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 =
|
|
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 (
|
|
15212
|
+
if (path16.isAbsolute(entry.linkTarget)) {
|
|
14984
15213
|
throw new Error("absolute symlink target refused");
|
|
14985
15214
|
}
|
|
14986
|
-
const resolvedLink =
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
15048
|
-
|
|
15049
|
-
`.${
|
|
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 =
|
|
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 =
|
|
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
|
|
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) =>
|
|
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
|
|
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 =
|
|
15250
|
-
return relative8 === "" || !relative8.startsWith("..") && !
|
|
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 =
|
|
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 =
|
|
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 =
|
|
15564
|
+
const root = path18.resolve(projectRoot);
|
|
15336
15565
|
const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
|
|
15337
|
-
const relative8 =
|
|
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
|
|
15591
|
+
import * as path20 from "node:path";
|
|
15363
15592
|
|
|
15364
15593
|
// src/storage/session-store/paths.ts
|
|
15365
|
-
import * as
|
|
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 ?
|
|
15599
|
+
return shardKey ? path19.join(storeDir, shardKey, "_manifest.json") : path19.join(storeDir, "_manifest.json");
|
|
15371
15600
|
}
|
|
15372
15601
|
function shardKeyForSessionId(id) {
|
|
15373
|
-
const dirName =
|
|
15602
|
+
const dirName = path19.dirname(id);
|
|
15374
15603
|
return dirName === "." ? "" : dirName;
|
|
15375
15604
|
}
|
|
15376
15605
|
async function ensureShardDir(storeDir, id) {
|
|
15377
|
-
const dirPath =
|
|
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 =
|
|
15389
|
-
const base =
|
|
15390
|
-
const sessDir =
|
|
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
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
16639
|
+
this.projectRoot = opts.projectRoot ? path23.resolve(opts.projectRoot) : void 0;
|
|
16411
16640
|
this.checkpointCas = this.projectRoot ? new SessionCheckpointCas({
|
|
16412
|
-
rootDir:
|
|
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:
|
|
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
|
|
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:
|
|
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 ?
|
|
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 ?
|
|
17584
|
+
dir = opts.sessionsRoot ? path24.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
17352
17585
|
} else if (opts.sessionsRoot) {
|
|
17353
|
-
dir =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
22998
|
+
const here = path26.dirname(fileURLToPath4(import.meta.url));
|
|
22766
22999
|
const candidates = [
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
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
|
|
23042
|
+
import * as path27 from "node:path";
|
|
22810
23043
|
var DESIGN_DIR = ".design";
|
|
22811
23044
|
function designProjectDir(projectRoot) {
|
|
22812
|
-
return
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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(
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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)
|
|
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 =
|
|
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(
|
|
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(
|
|
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 (
|
|
27879
|
-
const absolute =
|
|
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 =
|
|
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 =
|
|
28186
|
+
const parent = path31.dirname(probe);
|
|
27891
28187
|
if (parent === probe) return absolute;
|
|
27892
|
-
missingSegments.unshift(
|
|
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 =
|
|
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 =
|
|
27920
|
-
return relative8.startsWith("../") ||
|
|
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
|
|
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 =
|
|
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 =
|
|
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" ?
|
|
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
|
|
29846
|
+
import * as path35 from "node:path";
|
|
29549
29847
|
|
|
29550
29848
|
// src/types/mode-prompts.ts
|
|
29551
|
-
import { readFileSync as
|
|
29552
|
-
import * as
|
|
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
|
|
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 =
|
|
29862
|
+
const here = path34.dirname(fileURLToPath5(import.meta.url));
|
|
29565
29863
|
const candidates = [
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
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 ===
|
|
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:
|
|
31120
|
+
const { readFileSync: readFileSync11 } = await import("node:fs");
|
|
30823
31121
|
server = createServer(
|
|
30824
|
-
{ cert:
|
|
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}${
|
|
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
|
|
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,
|
|
31168
|
-
diagnostics.push({ severity: "error", code, path:
|
|
31465
|
+
function error(diagnostics, code, path45, message) {
|
|
31466
|
+
diagnostics.push({ severity: "error", code, path: path45, message });
|
|
31169
31467
|
}
|
|
31170
|
-
function validatePatterns(value,
|
|
31468
|
+
function validatePatterns(value, path45, diagnostics) {
|
|
31171
31469
|
if (!Array.isArray(value)) {
|
|
31172
|
-
error(diagnostics, "invalid_pattern_list",
|
|
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
|
-
|
|
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 = `${
|
|
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
|
|
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 =
|
|
31352
|
-
const relative8 =
|
|
31353
|
-
return !!relative8 && !relative8.startsWith("..") && !
|
|
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
|
|
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) :
|
|
31985
|
+
return tail.length === 0 ? realpathSync(probe) : path38.join(realpathSync(probe), ...tail);
|
|
31688
31986
|
} catch {
|
|
31689
|
-
const parent =
|
|
31987
|
+
const parent = path38.dirname(probe);
|
|
31690
31988
|
if (parent === probe) return p;
|
|
31691
|
-
tail.unshift(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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,
|
|
33598
|
-
const segments =
|
|
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:
|
|
33656
|
-
).map(({ path:
|
|
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:
|
|
33664
|
-
).map(({ path:
|
|
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:
|
|
33693
|
-
if (deleteNestedPath(out,
|
|
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
|
|
34180
|
+
import * as path42 from "node:path";
|
|
33883
34181
|
function samePath(a, b) {
|
|
33884
|
-
let ra =
|
|
33885
|
-
let rb =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
35498
|
+
await ensureDir(path44.dirname(this.file));
|
|
35201
35499
|
const lock = {
|
|
35202
35500
|
v: 1,
|
|
35203
35501
|
sessionId,
|