@wrongstack/core 0.289.0 → 0.291.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boot.d.ts.map +1 -1
- package/dist/chronicle/domain-adapter.d.ts.map +1 -1
- package/dist/chronicle/index.d.ts +1 -1
- package/dist/chronicle/index.d.ts.map +1 -1
- package/dist/chronicle/index.js +964 -245
- package/dist/chronicle/index.js.map +3 -3
- package/dist/chronicle/journal.d.ts +35 -3
- package/dist/chronicle/journal.d.ts.map +1 -1
- package/dist/chronicle/query.d.ts +8 -5
- package/dist/chronicle/query.d.ts.map +1 -1
- package/dist/chronicle/tool-adapter.d.ts.map +1 -1
- package/dist/coordination/agent-subagent-runner.d.ts +6 -0
- package/dist/coordination/agent-subagent-runner.d.ts.map +1 -1
- package/dist/coordination/agents/phase6-domain.d.ts.map +1 -1
- package/dist/coordination/brain.d.ts +2 -1
- package/dist/coordination/brain.d.ts.map +1 -1
- package/dist/coordination/collab-debug.d.ts +0 -9
- package/dist/coordination/collab-debug.d.ts.map +1 -1
- package/dist/coordination/coordinator/error-classifier.d.ts.map +1 -1
- package/dist/coordination/dep-watcher.d.ts +2 -1
- package/dist/coordination/dep-watcher.d.ts.map +1 -1
- package/dist/coordination/director/director-collab.d.ts +1 -1
- package/dist/coordination/director/director-collab.d.ts.map +1 -1
- package/dist/coordination/director-tools.d.ts.map +1 -1
- package/dist/coordination/director.d.ts +19 -1
- package/dist/coordination/director.d.ts.map +1 -1
- package/dist/coordination/fleet-event-validation.d.ts +0 -7
- package/dist/coordination/fleet-event-validation.d.ts.map +1 -1
- package/dist/coordination/fleet-spawn.d.ts.map +1 -1
- package/dist/coordination/fleet-supervisor.d.ts +1 -1
- package/dist/coordination/fleet-supervisor.d.ts.map +1 -1
- package/dist/coordination/fleet.d.ts +0 -44
- package/dist/coordination/fleet.d.ts.map +1 -1
- package/dist/coordination/index.d.ts +2 -2
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +733 -111
- package/dist/coordination/index.js.map +4 -4
- package/dist/coordination/knowledge-graph.d.ts +2 -1
- package/dist/coordination/knowledge-graph.d.ts.map +1 -1
- package/dist/coordination/large-answer-store.d.ts +0 -6
- package/dist/coordination/large-answer-store.d.ts.map +1 -1
- package/dist/coordination/mail-tools.d.ts.map +1 -1
- package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
- package/dist/coordination/mailbox-message-codec.d.ts +35 -0
- package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
- package/dist/coordination/mailbox-tool.d.ts +1 -1
- package/dist/coordination/mailbox-tool.d.ts.map +1 -1
- package/dist/coordination/mailbox-types.d.ts +151 -0
- package/dist/coordination/mailbox-types.d.ts.map +1 -1
- package/dist/coordination/provider-status-tracker.d.ts +27 -2
- package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
- package/dist/coordination/subagent-budget.d.ts +2 -1
- package/dist/coordination/subagent-budget.d.ts.map +1 -1
- package/dist/core/agent-response.d.ts +2 -1
- package/dist/core/agent-response.d.ts.map +1 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/fallback-model.d.ts +1 -1
- package/dist/core/fallback-model.d.ts.map +1 -1
- package/dist/core/fallback-profile-manager.d.ts +1 -1
- package/dist/core/fallback-profile-manager.d.ts.map +1 -1
- package/dist/core/fleet-pulse.d.ts +2 -1
- package/dist/core/fleet-pulse.d.ts.map +1 -1
- package/dist/core/iteration-limit.d.ts +2 -1
- package/dist/core/iteration-limit.d.ts.map +1 -1
- package/dist/core/mailbox-loop.d.ts +11 -1
- package/dist/core/mailbox-loop.d.ts.map +1 -1
- package/dist/core/model-availability-calendar.d.ts +27 -0
- package/dist/core/model-availability-calendar.d.ts.map +1 -0
- package/dist/core/system-prompt-builder.d.ts +2 -1
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/defaults/index.js +1459 -560
- package/dist/defaults/index.js.map +4 -4
- package/dist/execution/auto-compaction-middleware.d.ts +3 -2
- package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
- package/dist/execution/enhance-recovery.d.ts +8 -0
- package/dist/execution/enhance-recovery.d.ts.map +1 -1
- package/dist/execution/error-handler.d.ts +2 -2
- package/dist/execution/error-handler.d.ts.map +1 -1
- package/dist/execution/index.js +643 -134
- package/dist/execution/index.js.map +4 -4
- package/dist/execution/intelligent-compactor.d.ts +0 -16
- package/dist/execution/intelligent-compactor.d.ts.map +1 -1
- package/dist/execution/one-shot-llm.d.ts.map +1 -1
- package/dist/execution/retry-policy.d.ts +0 -13
- package/dist/execution/retry-policy.d.ts.map +1 -1
- package/dist/execution/skill-loader.d.ts.map +1 -1
- package/dist/execution/tool-executor.d.ts.map +1 -1
- package/dist/hq/auth-store.d.ts.map +1 -1
- package/dist/hq/index.js +39 -1
- package/dist/hq/index.js.map +3 -3
- package/dist/index.d.ts +32 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22263 -19999
- package/dist/index.js.map +4 -4
- package/dist/infrastructure/index.js +59 -5
- package/dist/infrastructure/index.js.map +2 -2
- package/dist/kernel/events/agent-events.d.ts +2 -0
- package/dist/kernel/events/agent-events.d.ts.map +1 -1
- package/dist/kernel/events/file-events.d.ts +46 -0
- package/dist/kernel/events/file-events.d.ts.map +1 -1
- package/dist/kernel/events/memory-events.d.ts +9 -0
- package/dist/kernel/events/memory-events.d.ts.map +1 -1
- package/dist/kernel/events/provider-events.d.ts +2 -1
- package/dist/kernel/events/provider-events.d.ts.map +1 -1
- package/dist/kernel/events/tool-events.d.ts +25 -0
- package/dist/kernel/events/tool-events.d.ts.map +1 -1
- package/dist/mailbox-attach.d.ts.map +1 -1
- package/dist/plugins/auto-review-plugin.d.ts +10 -0
- package/dist/plugins/auto-review-plugin.d.ts.map +1 -1
- package/dist/plugins/chimera-plugin.d.ts +5 -0
- package/dist/plugins/chimera-plugin.d.ts.map +1 -1
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +317 -9
- package/dist/security/index.js.map +4 -4
- package/dist/security/kanban-boundary.d.ts +10 -0
- package/dist/security/kanban-boundary.d.ts.map +1 -0
- package/dist/security/permission-policy.d.ts +8 -1
- package/dist/security/permission-policy.d.ts.map +1 -1
- package/dist/storage/config-loader.d.ts +15 -0
- package/dist/storage/config-loader.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +627 -428
- package/dist/storage/index.js.map +4 -4
- package/dist/storage/memory-consolidator.d.ts +49 -4
- package/dist/storage/memory-consolidator.d.ts.map +1 -1
- package/dist/storage/memory-graph-backend.d.ts +11 -0
- package/dist/storage/memory-graph-backend.d.ts.map +1 -1
- package/dist/storage/provider-config-watcher.d.ts +17 -0
- package/dist/storage/provider-config-watcher.d.ts.map +1 -1
- package/dist/storage/session-event-bridge.d.ts +2 -2
- package/dist/storage/session-event-bridge.d.ts.map +1 -1
- package/dist/tools/fallback-manage-tools.d.ts.map +1 -1
- package/dist/tools/index.js +166 -26
- package/dist/tools/index.js.map +3 -3
- package/dist/types/config.d.ts +12 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/file-event-record.d.ts +68 -0
- package/dist/types/file-event-record.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +13 -8
- package/dist/types/index.js.map +3 -3
- package/dist/types/permission.d.ts +38 -0
- package/dist/types/permission.d.ts.map +1 -1
- package/dist/types/provider.d.ts +1 -1
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/session.d.ts +27 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/types/tool-executor.d.ts.map +1 -1
- package/dist/utils/config-backup.d.ts +20 -0
- package/dist/utils/config-backup.d.ts.map +1 -0
- package/dist/utils/index.d.ts +12 -11
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +436 -290
- package/dist/utils/index.js.map +4 -4
- package/dist/utils/message-invariants.d.ts +12 -0
- package/dist/utils/message-invariants.d.ts.map +1 -1
- package/dist/utils/safe-json.d.ts +1 -1
- package/dist/utils/safe-json.d.ts.map +1 -1
- package/dist/utils/term.d.ts +6 -0
- package/dist/utils/term.d.ts.map +1 -1
- package/dist/utils/tool-wire-compact.d.ts +9 -0
- package/dist/utils/tool-wire-compact.d.ts.map +1 -1
- package/dist/utils/wstack-paths.d.ts +21 -1
- package/dist/utils/wstack-paths.d.ts.map +1 -1
- package/instructions/agents/ios.md +113 -0
- package/instructions/llm/memory-consolidator.md +4 -12
- package/instructions/system.md +4 -1
- package/package.json +3 -3
- package/skills/auto-review/SKILL.md +176 -0
- package/skills/mnemosyne/SKILL.md +156 -0
- package/skills/mnemosyne/instructions/agent-prompt.md +200 -0
package/dist/security/index.js
CHANGED
|
@@ -264,7 +264,7 @@ async function renameWithRetry(from, to) {
|
|
|
264
264
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
265
265
|
throw err;
|
|
266
266
|
}
|
|
267
|
-
await new Promise((
|
|
267
|
+
await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
throw lastErr;
|
|
@@ -428,7 +428,7 @@ function matchAny(patterns, input) {
|
|
|
428
428
|
|
|
429
429
|
// src/utils/safe-json.ts
|
|
430
430
|
function safeParse(input, maxBytes = 5e6) {
|
|
431
|
-
if (input
|
|
431
|
+
if (Buffer.byteLength(input, "utf8") > maxBytes) {
|
|
432
432
|
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
433
433
|
}
|
|
434
434
|
try {
|
|
@@ -1265,19 +1265,19 @@ var UNSAFE_PROPERTY_NAMES = /* @__PURE__ */ new Set(["__proto__", "prototype", "
|
|
|
1265
1265
|
function isRecord(value) {
|
|
1266
1266
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1267
1267
|
}
|
|
1268
|
-
function error(diagnostics, code,
|
|
1269
|
-
diagnostics.push({ severity: "error", code, path:
|
|
1268
|
+
function error(diagnostics, code, path4, message) {
|
|
1269
|
+
diagnostics.push({ severity: "error", code, path: path4, message });
|
|
1270
1270
|
}
|
|
1271
|
-
function validatePatterns(value,
|
|
1271
|
+
function validatePatterns(value, path4, diagnostics) {
|
|
1272
1272
|
if (!Array.isArray(value)) {
|
|
1273
|
-
error(diagnostics, "invalid_pattern_list",
|
|
1273
|
+
error(diagnostics, "invalid_pattern_list", path4, "must be an array of strings");
|
|
1274
1274
|
return void 0;
|
|
1275
1275
|
}
|
|
1276
1276
|
if (value.length > TRUST_POLICY_LIMITS.maxPatternsPerRule) {
|
|
1277
1277
|
error(
|
|
1278
1278
|
diagnostics,
|
|
1279
1279
|
"too_many_patterns",
|
|
1280
|
-
|
|
1280
|
+
path4,
|
|
1281
1281
|
`must contain at most ${TRUST_POLICY_LIMITS.maxPatternsPerRule} patterns`
|
|
1282
1282
|
);
|
|
1283
1283
|
return void 0;
|
|
@@ -1285,7 +1285,7 @@ function validatePatterns(value, path3, diagnostics) {
|
|
|
1285
1285
|
const patterns = [];
|
|
1286
1286
|
const seen = /* @__PURE__ */ new Set();
|
|
1287
1287
|
for (const [index, pattern] of value.entries()) {
|
|
1288
|
-
const itemPath = `${
|
|
1288
|
+
const itemPath = `${path4}[${index}]`;
|
|
1289
1289
|
if (typeof pattern !== "string" || pattern.length === 0 || pattern.length > TRUST_POLICY_LIMITS.maxPatternChars) {
|
|
1290
1290
|
error(
|
|
1291
1291
|
diagnostics,
|
|
@@ -1397,8 +1397,8 @@ var LruCache = class {
|
|
|
1397
1397
|
this.capacity = capacity;
|
|
1398
1398
|
}
|
|
1399
1399
|
get(key) {
|
|
1400
|
+
if (!this.store.has(key)) return void 0;
|
|
1400
1401
|
const value = this.store.get(key);
|
|
1401
|
-
if (value === void 0) return void 0;
|
|
1402
1402
|
this.store.delete(key);
|
|
1403
1403
|
this.store.set(key, value);
|
|
1404
1404
|
return value;
|
|
@@ -1859,6 +1859,136 @@ var DefaultPermissionPolicy = class {
|
|
|
1859
1859
|
this.sessionAllowed.set(`${rule.tool}::${rule.pattern}`, true);
|
|
1860
1860
|
this._evalCache.clear();
|
|
1861
1861
|
}
|
|
1862
|
+
/**
|
|
1863
|
+
* Side-effect-free permission evaluation trace. Mirrors `evaluate()`
|
|
1864
|
+
* step-by-step without prompting the user, writing to trust files, or
|
|
1865
|
+
* mutating session state. Returns the full ordered trace and winner.
|
|
1866
|
+
*/
|
|
1867
|
+
async explain(tool, input, ctx) {
|
|
1868
|
+
if (!this.loaded) await this.reload();
|
|
1869
|
+
const subject = subjectForToolInput(tool.name, input, tool.subjectKey);
|
|
1870
|
+
const steps = [];
|
|
1871
|
+
let winnerIndex = -1;
|
|
1872
|
+
const add = (rule, matched, decision, source, detail) => {
|
|
1873
|
+
steps.push({ rule, matched, decision, source, detail });
|
|
1874
|
+
};
|
|
1875
|
+
if (this.policyInvalid) {
|
|
1876
|
+
add("policy invalid", true, "deny", "deny", "trust policy is invalid; all tools are denied");
|
|
1877
|
+
winnerIndex = 0;
|
|
1878
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "deny", source: "deny", reason: "trust policy is invalid" } };
|
|
1879
|
+
}
|
|
1880
|
+
add("policy valid", false, "auto", "default", "trust policy loaded successfully");
|
|
1881
|
+
const namespaceEntry = this.findNamespaceEntry(tool.name);
|
|
1882
|
+
const entry = this.policy[tool.name] ?? namespaceEntry;
|
|
1883
|
+
const namespaceSource = namespaceEntry ? `wildcard entry matched (${Object.keys(this.policy).find((k) => k.includes("*") && matchGlob(k, tool.name))})` : "no namespace match";
|
|
1884
|
+
const cacheKey = `${tool.name}::${subject ?? tool.name}`;
|
|
1885
|
+
if (this.sessionDenied.has(cacheKey)) {
|
|
1886
|
+
add("session soft deny", true, "deny", "deny", 'user pressed "no" earlier in this session \u2014 blocked until reload');
|
|
1887
|
+
winnerIndex = steps.length - 1;
|
|
1888
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" } };
|
|
1889
|
+
}
|
|
1890
|
+
add("session soft deny", false, "deny", "deny", "no session-level soft deny for this tool+subject");
|
|
1891
|
+
if (this.sessionAllowed.has(cacheKey)) {
|
|
1892
|
+
add("session soft allow", true, "auto", "trust", 'user pressed "yes" in current session \u2014 one-shot auto-approve (consumed)');
|
|
1893
|
+
winnerIndex = steps.length - 1;
|
|
1894
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "trust", reason: "session one-shot allow (user pressed yes)" } };
|
|
1895
|
+
}
|
|
1896
|
+
add("session soft allow", false, "auto", "trust", "no session-level one-shot allow for this tool+subject");
|
|
1897
|
+
if (entry?.deny && subject && matchesTrust(entry.deny, subject)) {
|
|
1898
|
+
add("trust deny", true, "deny", "deny", `subject "${subject}" matched a deny pattern in trust file`);
|
|
1899
|
+
winnerIndex = steps.length - 1;
|
|
1900
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "deny", source: "deny", reason: "matched deny pattern" } };
|
|
1901
|
+
}
|
|
1902
|
+
add("trust deny", false, "deny", "deny", `no deny pattern matched (namespace: ${namespaceSource})`);
|
|
1903
|
+
if (tool.permission === "deny") {
|
|
1904
|
+
add("tool default deny", true, "deny", "default", `tool "${tool.name}" has permission: deny by default`);
|
|
1905
|
+
winnerIndex = steps.length - 1;
|
|
1906
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "deny", source: "default", reason: "tool default deny" } };
|
|
1907
|
+
}
|
|
1908
|
+
add("tool default deny", false, "deny", "default", `tool "${tool.name}" has permission: "${tool.permission}"`);
|
|
1909
|
+
if (entry?.allow && subject && matchesTrust(entry.allow, subject)) {
|
|
1910
|
+
add("trust allow", true, "auto", "trust", `subject "${subject}" matched an allow pattern in trust file`);
|
|
1911
|
+
winnerIndex = steps.length - 1;
|
|
1912
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "trust", reason: "matched allow pattern" } };
|
|
1913
|
+
}
|
|
1914
|
+
add("trust allow", false, "auto", "trust", `no allow pattern matched (namespace: ${namespaceSource})`);
|
|
1915
|
+
if (entry?.auto) {
|
|
1916
|
+
add("trust auto", true, "auto", "trust", `trust file has auto: true for "${tool.name}"`);
|
|
1917
|
+
winnerIndex = steps.length - 1;
|
|
1918
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "trust", reason: "trust auto" } };
|
|
1919
|
+
}
|
|
1920
|
+
add("trust auto", false, "auto", "trust", `no auto flag for "${tool.name}" in trust file`);
|
|
1921
|
+
if (!this.yolo && this.isSensitiveReadCall(tool, input)) {
|
|
1922
|
+
const hasDelegate2 = this.promptDelegate !== void 0;
|
|
1923
|
+
add(
|
|
1924
|
+
"sensitive read",
|
|
1925
|
+
true,
|
|
1926
|
+
hasDelegate2 ? "confirm" : "confirm",
|
|
1927
|
+
"default",
|
|
1928
|
+
hasDelegate2 ? "sensitive file read detected \u2014 would prompt user for approval" : "sensitive file read detected \u2014 returns confirm (no prompt delegate)"
|
|
1929
|
+
);
|
|
1930
|
+
winnerIndex = steps.length - 1;
|
|
1931
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "confirm", source: "default", riskTier: "standard", reason: "sensitive file read needs explicit approval" } };
|
|
1932
|
+
}
|
|
1933
|
+
add("sensitive read", false, "confirm", "default", "not a sensitive read call (or YOLO bypasses this check)");
|
|
1934
|
+
if (this.yolo) {
|
|
1935
|
+
add("yolo", true, "auto", "yolo", "YOLO mode is active \u2014 auto-approving every non-denied call");
|
|
1936
|
+
winnerIndex = steps.length - 1;
|
|
1937
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "yolo" } };
|
|
1938
|
+
}
|
|
1939
|
+
add("yolo", false, "auto", "yolo", "YOLO mode is not active");
|
|
1940
|
+
if (tool.name === "write" && subject) {
|
|
1941
|
+
const hasRead = ctx.hasRead(subject);
|
|
1942
|
+
add(
|
|
1943
|
+
"write smart bypass",
|
|
1944
|
+
hasRead,
|
|
1945
|
+
"auto",
|
|
1946
|
+
"context",
|
|
1947
|
+
hasRead ? `file "${subject}" was already read in this session \u2014 auto-approving write` : `file "${subject}" was not read in this session \u2014 bypass does not apply`
|
|
1948
|
+
);
|
|
1949
|
+
if (hasRead) {
|
|
1950
|
+
winnerIndex = steps.length - 1;
|
|
1951
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "context", reason: "file already read in this session" } };
|
|
1952
|
+
}
|
|
1953
|
+
} else {
|
|
1954
|
+
add("write smart bypass", false, "auto", "context", 'tool is not "write" or has no subject \u2014 bypass does not apply');
|
|
1955
|
+
}
|
|
1956
|
+
const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
|
|
1957
|
+
const hasShellCap = hasCapability(tool, [
|
|
1958
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
1959
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
1960
|
+
ToolCapabilities.SHELL_EXEC
|
|
1961
|
+
]);
|
|
1962
|
+
const hasInstallCap = hasCapability(tool, ToolCapabilities.PACKAGE_INSTALL);
|
|
1963
|
+
const hasConfigCap = hasCapability(tool, ToolCapabilities.CONFIG_MUTATE);
|
|
1964
|
+
const hasSubagentCap = hasCapability(tool, ToolCapabilities.SUBAGENT_SPAWN);
|
|
1965
|
+
const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
|
|
1966
|
+
if (tool.permission === "auto" && !isMutating) {
|
|
1967
|
+
add("safe default auto", true, "auto", "default", `tool "${tool.name}" has auto permission and is not mutating \u2014 safe to auto-approve`);
|
|
1968
|
+
winnerIndex = steps.length - 1;
|
|
1969
|
+
return { toolName: tool.name, subject, steps, winnerIndex, decision: { permission: "auto", source: "default" } };
|
|
1970
|
+
}
|
|
1971
|
+
add(
|
|
1972
|
+
"mutating default confirm",
|
|
1973
|
+
true,
|
|
1974
|
+
"confirm",
|
|
1975
|
+
"default",
|
|
1976
|
+
isMutating ? `tool "${tool.name}" is mutating (default auto not enough) \u2014 needs confirmation` : `tool "${tool.name}" has "${tool.permission}" permission \u2014 needs confirmation`
|
|
1977
|
+
);
|
|
1978
|
+
winnerIndex = steps.length - 1;
|
|
1979
|
+
const hasDelegate = this.promptDelegate !== void 0;
|
|
1980
|
+
return {
|
|
1981
|
+
toolName: tool.name,
|
|
1982
|
+
subject,
|
|
1983
|
+
steps,
|
|
1984
|
+
winnerIndex,
|
|
1985
|
+
decision: {
|
|
1986
|
+
permission: "confirm",
|
|
1987
|
+
source: "default",
|
|
1988
|
+
...hasDelegate ? { reason: "would prompt user via delegate" } : {}
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1862
1992
|
findNamespaceEntry(toolName) {
|
|
1863
1993
|
for (const { pattern, value } of this.wildcardEntries) {
|
|
1864
1994
|
if (matchGlob(pattern, toolName)) return value;
|
|
@@ -1904,9 +2034,186 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
1904
2034
|
}
|
|
1905
2035
|
allowOnce() {
|
|
1906
2036
|
}
|
|
2037
|
+
async explain(tool) {
|
|
2038
|
+
const decision = await this.evaluate(tool);
|
|
2039
|
+
return {
|
|
2040
|
+
toolName: tool.name,
|
|
2041
|
+
subject: null,
|
|
2042
|
+
steps: [
|
|
2043
|
+
{
|
|
2044
|
+
rule: "subagent auto",
|
|
2045
|
+
matched: decision.permission === "auto",
|
|
2046
|
+
decision: decision.permission,
|
|
2047
|
+
source: decision.source,
|
|
2048
|
+
detail: decision.reason ?? `subagent policy: ${decision.permission}`
|
|
2049
|
+
}
|
|
2050
|
+
],
|
|
2051
|
+
winnerIndex: 0,
|
|
2052
|
+
decision
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
1907
2055
|
async reload() {
|
|
1908
2056
|
}
|
|
1909
2057
|
};
|
|
2058
|
+
|
|
2059
|
+
// src/security/kanban-boundary.ts
|
|
2060
|
+
import { realpath } from "node:fs/promises";
|
|
2061
|
+
import * as path3 from "node:path";
|
|
2062
|
+
import {
|
|
2063
|
+
evaluateKanbanBoundaryOpaque,
|
|
2064
|
+
evaluateKanbanBoundaryPath,
|
|
2065
|
+
readBoard,
|
|
2066
|
+
resolveKanbanBoundaryLayers
|
|
2067
|
+
} from "@wrongstack/kanban";
|
|
2068
|
+
var PATH_KEYS = /* @__PURE__ */ new Set([
|
|
2069
|
+
"path",
|
|
2070
|
+
"paths",
|
|
2071
|
+
"file",
|
|
2072
|
+
"files",
|
|
2073
|
+
"directory",
|
|
2074
|
+
"dir",
|
|
2075
|
+
"cwd",
|
|
2076
|
+
"root",
|
|
2077
|
+
"baseDir",
|
|
2078
|
+
"out",
|
|
2079
|
+
"outputPath",
|
|
2080
|
+
"sourcePath",
|
|
2081
|
+
"targetPath",
|
|
2082
|
+
"destinationPath",
|
|
2083
|
+
"fromPath",
|
|
2084
|
+
"toPath",
|
|
2085
|
+
"worktreePath"
|
|
2086
|
+
]);
|
|
2087
|
+
var TOOL_PATH_KEYS = {
|
|
2088
|
+
// `target` is an identifier in tools such as plan/task/document, but a
|
|
2089
|
+
// filesystem target in language_info. Keep ambiguous names tool-specific.
|
|
2090
|
+
language_info: ["target"]
|
|
2091
|
+
};
|
|
2092
|
+
async function evaluateToolKanbanBoundary(tool, input, ctx) {
|
|
2093
|
+
const identity = resolveKanbanIdentity(ctx);
|
|
2094
|
+
if (!identity.boardId) return { decision: "allow" };
|
|
2095
|
+
const board = await readBoard(identity.policyRoot ?? ctx.projectRoot, identity.boardId);
|
|
2096
|
+
if (!board) return { decision: "allow" };
|
|
2097
|
+
const task = identity.taskId ? board.tasks.find((candidate) => candidate.id === identity.taskId) : void 0;
|
|
2098
|
+
const layers = resolveKanbanBoundaryLayers(board, task);
|
|
2099
|
+
if (layers.length === 0) return { decision: "allow" };
|
|
2100
|
+
const caps = tool.capabilities ?? [];
|
|
2101
|
+
const access2 = caps.includes("fs.write") || caps.includes("fs.write.outside-project") ? "write" : caps.includes("fs.read") ? "read" : void 0;
|
|
2102
|
+
const shellLike = caps.some((capability) => capability.startsWith("shell."));
|
|
2103
|
+
const candidates = access2 ? await extractCandidatePaths(tool.name, input, ctx) : [];
|
|
2104
|
+
let evaluation;
|
|
2105
|
+
if (shellLike || access2 && candidates.length === 0) {
|
|
2106
|
+
evaluation = evaluateKanbanBoundaryOpaque(layers, tool.name);
|
|
2107
|
+
} else if (access2) {
|
|
2108
|
+
const results = candidates.map(
|
|
2109
|
+
(candidate) => evaluateKanbanBoundaryPath(layers, candidate, access2)
|
|
2110
|
+
);
|
|
2111
|
+
evaluation = results.find((result) => result.decision === "block") ?? results.find((result) => result.decision === "confirm") ?? { decision: "allow" };
|
|
2112
|
+
} else {
|
|
2113
|
+
evaluation = { decision: "allow" };
|
|
2114
|
+
}
|
|
2115
|
+
return {
|
|
2116
|
+
...evaluation,
|
|
2117
|
+
boardId: board.id,
|
|
2118
|
+
...task ? { taskId: task.id } : {}
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
function resolveKanbanIdentity(ctx) {
|
|
2122
|
+
const metaKanban = ctx.meta["kanban"];
|
|
2123
|
+
const record = metaKanban && typeof metaKanban === "object" ? metaKanban : void 0;
|
|
2124
|
+
const boardId = ctx.currentKanbanBoardId ?? (typeof record?.["boardId"] === "string" ? record["boardId"] : void 0);
|
|
2125
|
+
const taskId = ctx.currentKanbanTaskId ?? (typeof record?.["taskId"] === "string" ? record["taskId"] : void 0);
|
|
2126
|
+
const policyRoot = typeof record?.["projectRoot"] === "string" ? record["projectRoot"] : void 0;
|
|
2127
|
+
return {
|
|
2128
|
+
...boardId ? { boardId } : {},
|
|
2129
|
+
...taskId ? { taskId } : {},
|
|
2130
|
+
...policyRoot ? { policyRoot } : {}
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
async function extractCandidatePaths(toolName, input, ctx) {
|
|
2134
|
+
if (toolName === "patch" && typeof input["patch"] === "string") {
|
|
2135
|
+
const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
|
|
2136
|
+
const directory = path3.isAbsolute(directoryInput) ? directoryInput : path3.resolve(ctx.workingDir, directoryInput);
|
|
2137
|
+
const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
|
|
2138
|
+
const targets = extractPatchTargets(input["patch"], strip).map(
|
|
2139
|
+
(target) => relativeToProject(path3.resolve(directory, target), ctx.projectRoot)
|
|
2140
|
+
);
|
|
2141
|
+
return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
|
|
2142
|
+
}
|
|
2143
|
+
const values = [];
|
|
2144
|
+
const pathKeys = new Set(PATH_KEYS);
|
|
2145
|
+
for (const key of TOOL_PATH_KEYS[toolName] ?? []) pathKeys.add(key);
|
|
2146
|
+
collectPathValues(input, values, pathKeys);
|
|
2147
|
+
if (toolName === "scaffold" && typeof input["name"] === "string") {
|
|
2148
|
+
const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
|
|
2149
|
+
values.push(path3.join(cwd, input["name"]));
|
|
2150
|
+
}
|
|
2151
|
+
const candidates = [
|
|
2152
|
+
...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
|
|
2153
|
+
];
|
|
2154
|
+
return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
|
|
2155
|
+
}
|
|
2156
|
+
async function canonicalizeCandidatePath(candidate, ctx) {
|
|
2157
|
+
if (path3.isAbsolute(candidate)) return candidate;
|
|
2158
|
+
const absolute = path3.resolve(ctx.projectRoot, candidate);
|
|
2159
|
+
const canonicalRoot = await realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
2160
|
+
let probe = absolute;
|
|
2161
|
+
const missingSegments = [];
|
|
2162
|
+
while (true) {
|
|
2163
|
+
try {
|
|
2164
|
+
const canonical = path3.join(await realpath(probe), ...missingSegments);
|
|
2165
|
+
return relativeToProject(canonical, canonicalRoot);
|
|
2166
|
+
} catch (cause) {
|
|
2167
|
+
const code = cause.code;
|
|
2168
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
|
|
2169
|
+
const parent = path3.dirname(probe);
|
|
2170
|
+
if (parent === probe) return absolute;
|
|
2171
|
+
missingSegments.unshift(path3.basename(probe));
|
|
2172
|
+
probe = parent;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
function collectPathValues(value, output, pathKeys, key) {
|
|
2177
|
+
if (typeof value === "string") {
|
|
2178
|
+
if (key && pathKeys.has(key)) output.push(value);
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
if (Array.isArray(value)) {
|
|
2182
|
+
for (const item of value) collectPathValues(item, output, pathKeys, key);
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
if (!value || typeof value !== "object") return;
|
|
2186
|
+
for (const [childKey, child] of Object.entries(value)) {
|
|
2187
|
+
collectPathValues(child, output, pathKeys, childKey);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
function splitPathList(value) {
|
|
2191
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2192
|
+
}
|
|
2193
|
+
function resolveInputPath(value, ctx) {
|
|
2194
|
+
const absolute = path3.isAbsolute(value) ? value : path3.resolve(ctx.workingDir, value);
|
|
2195
|
+
return relativeToProject(absolute, ctx.projectRoot);
|
|
2196
|
+
}
|
|
2197
|
+
function relativeToProject(absolute, projectRoot) {
|
|
2198
|
+
const relative2 = path3.relative(projectRoot, absolute).replace(/\\/g, "/");
|
|
2199
|
+
return relative2.startsWith("../") || path3.isAbsolute(relative2) ? absolute : relative2 || ".";
|
|
2200
|
+
}
|
|
2201
|
+
function extractPatchTargets(patchText, strip) {
|
|
2202
|
+
const targets = [];
|
|
2203
|
+
for (const match of patchText.matchAll(/^\+\+\+\s+([^\t\r\n]+)/gm)) {
|
|
2204
|
+
const raw = match[1]?.trim();
|
|
2205
|
+
if (!raw || raw === "/dev/null") continue;
|
|
2206
|
+
const parts = raw.replace(/\\/g, "/").split("/").filter((part) => part && part !== ".");
|
|
2207
|
+
if (parts.length > strip) targets.push(parts.slice(strip).join("/"));
|
|
2208
|
+
}
|
|
2209
|
+
return targets;
|
|
2210
|
+
}
|
|
2211
|
+
function stringValue(value) {
|
|
2212
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
2213
|
+
}
|
|
2214
|
+
function numericValue(value) {
|
|
2215
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2216
|
+
}
|
|
1910
2217
|
export {
|
|
1911
2218
|
AutoApprovePermissionPolicy,
|
|
1912
2219
|
DANGEROUS_FOR_SUBAGENTS,
|
|
@@ -1919,6 +2226,7 @@ export {
|
|
|
1919
2226
|
ToolCapabilities,
|
|
1920
2227
|
decryptConfigSecrets,
|
|
1921
2228
|
encryptConfigSecrets,
|
|
2229
|
+
evaluateToolKanbanBoundary,
|
|
1922
2230
|
getDangerousCapabilities,
|
|
1923
2231
|
hasCapability,
|
|
1924
2232
|
hasDangerousCapabilityForSubagents,
|