@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.
Files changed (177) hide show
  1. package/dist/boot.d.ts.map +1 -1
  2. package/dist/chronicle/domain-adapter.d.ts.map +1 -1
  3. package/dist/chronicle/index.d.ts +1 -1
  4. package/dist/chronicle/index.d.ts.map +1 -1
  5. package/dist/chronicle/index.js +964 -245
  6. package/dist/chronicle/index.js.map +3 -3
  7. package/dist/chronicle/journal.d.ts +35 -3
  8. package/dist/chronicle/journal.d.ts.map +1 -1
  9. package/dist/chronicle/query.d.ts +8 -5
  10. package/dist/chronicle/query.d.ts.map +1 -1
  11. package/dist/chronicle/tool-adapter.d.ts.map +1 -1
  12. package/dist/coordination/agent-subagent-runner.d.ts +6 -0
  13. package/dist/coordination/agent-subagent-runner.d.ts.map +1 -1
  14. package/dist/coordination/agents/phase6-domain.d.ts.map +1 -1
  15. package/dist/coordination/brain.d.ts +2 -1
  16. package/dist/coordination/brain.d.ts.map +1 -1
  17. package/dist/coordination/collab-debug.d.ts +0 -9
  18. package/dist/coordination/collab-debug.d.ts.map +1 -1
  19. package/dist/coordination/coordinator/error-classifier.d.ts.map +1 -1
  20. package/dist/coordination/dep-watcher.d.ts +2 -1
  21. package/dist/coordination/dep-watcher.d.ts.map +1 -1
  22. package/dist/coordination/director/director-collab.d.ts +1 -1
  23. package/dist/coordination/director/director-collab.d.ts.map +1 -1
  24. package/dist/coordination/director-tools.d.ts.map +1 -1
  25. package/dist/coordination/director.d.ts +19 -1
  26. package/dist/coordination/director.d.ts.map +1 -1
  27. package/dist/coordination/fleet-event-validation.d.ts +0 -7
  28. package/dist/coordination/fleet-event-validation.d.ts.map +1 -1
  29. package/dist/coordination/fleet-spawn.d.ts.map +1 -1
  30. package/dist/coordination/fleet-supervisor.d.ts +1 -1
  31. package/dist/coordination/fleet-supervisor.d.ts.map +1 -1
  32. package/dist/coordination/fleet.d.ts +0 -44
  33. package/dist/coordination/fleet.d.ts.map +1 -1
  34. package/dist/coordination/index.d.ts +2 -2
  35. package/dist/coordination/index.d.ts.map +1 -1
  36. package/dist/coordination/index.js +733 -111
  37. package/dist/coordination/index.js.map +4 -4
  38. package/dist/coordination/knowledge-graph.d.ts +2 -1
  39. package/dist/coordination/knowledge-graph.d.ts.map +1 -1
  40. package/dist/coordination/large-answer-store.d.ts +0 -6
  41. package/dist/coordination/large-answer-store.d.ts.map +1 -1
  42. package/dist/coordination/mail-tools.d.ts.map +1 -1
  43. package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
  44. package/dist/coordination/mailbox-message-codec.d.ts +35 -0
  45. package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
  46. package/dist/coordination/mailbox-tool.d.ts +1 -1
  47. package/dist/coordination/mailbox-tool.d.ts.map +1 -1
  48. package/dist/coordination/mailbox-types.d.ts +151 -0
  49. package/dist/coordination/mailbox-types.d.ts.map +1 -1
  50. package/dist/coordination/provider-status-tracker.d.ts +27 -2
  51. package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
  52. package/dist/coordination/subagent-budget.d.ts +2 -1
  53. package/dist/coordination/subagent-budget.d.ts.map +1 -1
  54. package/dist/core/agent-response.d.ts +2 -1
  55. package/dist/core/agent-response.d.ts.map +1 -1
  56. package/dist/core/agent-tools.d.ts.map +1 -1
  57. package/dist/core/context.d.ts +51 -0
  58. package/dist/core/context.d.ts.map +1 -1
  59. package/dist/core/fallback-model.d.ts +1 -1
  60. package/dist/core/fallback-model.d.ts.map +1 -1
  61. package/dist/core/fallback-profile-manager.d.ts +1 -1
  62. package/dist/core/fallback-profile-manager.d.ts.map +1 -1
  63. package/dist/core/fleet-pulse.d.ts +2 -1
  64. package/dist/core/fleet-pulse.d.ts.map +1 -1
  65. package/dist/core/iteration-limit.d.ts +2 -1
  66. package/dist/core/iteration-limit.d.ts.map +1 -1
  67. package/dist/core/mailbox-loop.d.ts +11 -1
  68. package/dist/core/mailbox-loop.d.ts.map +1 -1
  69. package/dist/core/model-availability-calendar.d.ts +27 -0
  70. package/dist/core/model-availability-calendar.d.ts.map +1 -0
  71. package/dist/core/system-prompt-builder.d.ts +2 -1
  72. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  73. package/dist/defaults/index.js +1459 -560
  74. package/dist/defaults/index.js.map +4 -4
  75. package/dist/execution/auto-compaction-middleware.d.ts +3 -2
  76. package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
  77. package/dist/execution/enhance-recovery.d.ts +8 -0
  78. package/dist/execution/enhance-recovery.d.ts.map +1 -1
  79. package/dist/execution/error-handler.d.ts +2 -2
  80. package/dist/execution/error-handler.d.ts.map +1 -1
  81. package/dist/execution/index.js +643 -134
  82. package/dist/execution/index.js.map +4 -4
  83. package/dist/execution/intelligent-compactor.d.ts +0 -16
  84. package/dist/execution/intelligent-compactor.d.ts.map +1 -1
  85. package/dist/execution/one-shot-llm.d.ts.map +1 -1
  86. package/dist/execution/retry-policy.d.ts +0 -13
  87. package/dist/execution/retry-policy.d.ts.map +1 -1
  88. package/dist/execution/skill-loader.d.ts.map +1 -1
  89. package/dist/execution/tool-executor.d.ts.map +1 -1
  90. package/dist/hq/auth-store.d.ts.map +1 -1
  91. package/dist/hq/index.js +39 -1
  92. package/dist/hq/index.js.map +3 -3
  93. package/dist/index.d.ts +32 -30
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +22263 -19999
  96. package/dist/index.js.map +4 -4
  97. package/dist/infrastructure/index.js +59 -5
  98. package/dist/infrastructure/index.js.map +2 -2
  99. package/dist/kernel/events/agent-events.d.ts +2 -0
  100. package/dist/kernel/events/agent-events.d.ts.map +1 -1
  101. package/dist/kernel/events/file-events.d.ts +46 -0
  102. package/dist/kernel/events/file-events.d.ts.map +1 -1
  103. package/dist/kernel/events/memory-events.d.ts +9 -0
  104. package/dist/kernel/events/memory-events.d.ts.map +1 -1
  105. package/dist/kernel/events/provider-events.d.ts +2 -1
  106. package/dist/kernel/events/provider-events.d.ts.map +1 -1
  107. package/dist/kernel/events/tool-events.d.ts +25 -0
  108. package/dist/kernel/events/tool-events.d.ts.map +1 -1
  109. package/dist/mailbox-attach.d.ts.map +1 -1
  110. package/dist/plugins/auto-review-plugin.d.ts +10 -0
  111. package/dist/plugins/auto-review-plugin.d.ts.map +1 -1
  112. package/dist/plugins/chimera-plugin.d.ts +5 -0
  113. package/dist/plugins/chimera-plugin.d.ts.map +1 -1
  114. package/dist/security/index.d.ts +1 -0
  115. package/dist/security/index.d.ts.map +1 -1
  116. package/dist/security/index.js +317 -9
  117. package/dist/security/index.js.map +4 -4
  118. package/dist/security/kanban-boundary.d.ts +10 -0
  119. package/dist/security/kanban-boundary.d.ts.map +1 -0
  120. package/dist/security/permission-policy.d.ts +8 -1
  121. package/dist/security/permission-policy.d.ts.map +1 -1
  122. package/dist/storage/config-loader.d.ts +15 -0
  123. package/dist/storage/config-loader.d.ts.map +1 -1
  124. package/dist/storage/index.d.ts +2 -2
  125. package/dist/storage/index.d.ts.map +1 -1
  126. package/dist/storage/index.js +627 -428
  127. package/dist/storage/index.js.map +4 -4
  128. package/dist/storage/memory-consolidator.d.ts +49 -4
  129. package/dist/storage/memory-consolidator.d.ts.map +1 -1
  130. package/dist/storage/memory-graph-backend.d.ts +11 -0
  131. package/dist/storage/memory-graph-backend.d.ts.map +1 -1
  132. package/dist/storage/provider-config-watcher.d.ts +17 -0
  133. package/dist/storage/provider-config-watcher.d.ts.map +1 -1
  134. package/dist/storage/session-event-bridge.d.ts +2 -2
  135. package/dist/storage/session-event-bridge.d.ts.map +1 -1
  136. package/dist/tools/fallback-manage-tools.d.ts.map +1 -1
  137. package/dist/tools/index.js +166 -26
  138. package/dist/tools/index.js.map +3 -3
  139. package/dist/types/config.d.ts +12 -4
  140. package/dist/types/config.d.ts.map +1 -1
  141. package/dist/types/file-event-record.d.ts +68 -0
  142. package/dist/types/file-event-record.d.ts.map +1 -0
  143. package/dist/types/index.d.ts +1 -0
  144. package/dist/types/index.d.ts.map +1 -1
  145. package/dist/types/index.js +13 -8
  146. package/dist/types/index.js.map +3 -3
  147. package/dist/types/permission.d.ts +38 -0
  148. package/dist/types/permission.d.ts.map +1 -1
  149. package/dist/types/provider.d.ts +1 -1
  150. package/dist/types/provider.d.ts.map +1 -1
  151. package/dist/types/session.d.ts +27 -0
  152. package/dist/types/session.d.ts.map +1 -1
  153. package/dist/types/tool-executor.d.ts +2 -0
  154. package/dist/types/tool-executor.d.ts.map +1 -1
  155. package/dist/utils/config-backup.d.ts +20 -0
  156. package/dist/utils/config-backup.d.ts.map +1 -0
  157. package/dist/utils/index.d.ts +12 -11
  158. package/dist/utils/index.d.ts.map +1 -1
  159. package/dist/utils/index.js +436 -290
  160. package/dist/utils/index.js.map +4 -4
  161. package/dist/utils/message-invariants.d.ts +12 -0
  162. package/dist/utils/message-invariants.d.ts.map +1 -1
  163. package/dist/utils/safe-json.d.ts +1 -1
  164. package/dist/utils/safe-json.d.ts.map +1 -1
  165. package/dist/utils/term.d.ts +6 -0
  166. package/dist/utils/term.d.ts.map +1 -1
  167. package/dist/utils/tool-wire-compact.d.ts +9 -0
  168. package/dist/utils/tool-wire-compact.d.ts.map +1 -1
  169. package/dist/utils/wstack-paths.d.ts +21 -1
  170. package/dist/utils/wstack-paths.d.ts.map +1 -1
  171. package/instructions/agents/ios.md +113 -0
  172. package/instructions/llm/memory-consolidator.md +4 -12
  173. package/instructions/system.md +4 -1
  174. package/package.json +3 -3
  175. package/skills/auto-review/SKILL.md +176 -0
  176. package/skills/mnemosyne/SKILL.md +156 -0
  177. package/skills/mnemosyne/instructions/agent-prompt.md +200 -0
@@ -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((resolve) => setTimeout(resolve, delays[i]));
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.length > maxBytes) {
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, path3, message) {
1269
- diagnostics.push({ severity: "error", code, path: path3, message });
1268
+ function error(diagnostics, code, path4, message) {
1269
+ diagnostics.push({ severity: "error", code, path: path4, message });
1270
1270
  }
1271
- function validatePatterns(value, path3, diagnostics) {
1271
+ function validatePatterns(value, path4, diagnostics) {
1272
1272
  if (!Array.isArray(value)) {
1273
- error(diagnostics, "invalid_pattern_list", path3, "must be an array of strings");
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
- path3,
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 = `${path3}[${index}]`;
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,