@caupulican/pi-adaptative 0.93.2 → 0.93.4

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 (224) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +6 -6
  3. package/dist/cli/args.js +1 -1
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-contracts.d.ts +2 -2
  6. package/dist/core/agent-session-contracts.d.ts.map +1 -1
  7. package/dist/core/agent-session-contracts.js.map +1 -1
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +16 -11
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/autonomy/approval-gate.d.ts +1 -0
  12. package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
  13. package/dist/core/autonomy/approval-gate.js +4 -1
  14. package/dist/core/autonomy/approval-gate.js.map +1 -1
  15. package/dist/core/autonomy/composite-tool-gate.d.ts +13 -0
  16. package/dist/core/autonomy/composite-tool-gate.d.ts.map +1 -0
  17. package/dist/core/autonomy/composite-tool-gate.js +44 -0
  18. package/dist/core/autonomy/composite-tool-gate.js.map +1 -0
  19. package/dist/core/autonomy/envelope-enforcement.d.ts +2 -0
  20. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  21. package/dist/core/autonomy/envelope-enforcement.js +48 -7
  22. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  23. package/dist/core/autonomy/gates.d.ts.map +1 -1
  24. package/dist/core/autonomy/gates.js +48 -76
  25. package/dist/core/autonomy/gates.js.map +1 -1
  26. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  27. package/dist/core/autonomy/path-scope.js +7 -7
  28. package/dist/core/autonomy/path-scope.js.map +1 -1
  29. package/dist/core/autonomy/subagent-prompt.d.ts +1 -1
  30. package/dist/core/autonomy/subagent-prompt.js.map +1 -1
  31. package/dist/core/background-tool-task-controller.d.ts +2 -0
  32. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  33. package/dist/core/background-tool-task-controller.js +13 -5
  34. package/dist/core/background-tool-task-controller.js.map +1 -1
  35. package/dist/core/compaction-controller.d.ts +3 -0
  36. package/dist/core/compaction-controller.d.ts.map +1 -1
  37. package/dist/core/compaction-controller.js +132 -97
  38. package/dist/core/compaction-controller.js.map +1 -1
  39. package/dist/core/cost-guard.js +1 -1
  40. package/dist/core/cost-guard.js.map +1 -1
  41. package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -1
  42. package/dist/core/delegation/session-root-mailbox.js +14 -1
  43. package/dist/core/delegation/session-root-mailbox.js.map +1 -1
  44. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  45. package/dist/core/delegation/worker-agent-control-coordinator.js +6 -9
  46. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  47. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  48. package/dist/core/delegation/worker-attempt-executor.js +7 -5
  49. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  50. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
  51. package/dist/core/delegation/worker-authority-resolver.js +3 -2
  52. package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
  53. package/dist/core/delegation/worker-delegation-request.d.ts +5 -2
  54. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  55. package/dist/core/delegation/worker-delegation-request.js +1 -1
  56. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  57. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  58. package/dist/core/delegation/worker-execution-policy.js +10 -8
  59. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  60. package/dist/core/foreground-recovery-controller.d.ts +2 -0
  61. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  62. package/dist/core/foreground-recovery-controller.js +18 -9
  63. package/dist/core/foreground-recovery-controller.js.map +1 -1
  64. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  65. package/dist/core/goal-loop-controller.js +3 -5
  66. package/dist/core/goal-loop-controller.js.map +1 -1
  67. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  68. package/dist/core/goals/goal-auto-continue-controller.js +6 -0
  69. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  70. package/dist/core/goals/goal-session-controller.js +1 -1
  71. package/dist/core/goals/goal-session-controller.js.map +1 -1
  72. package/dist/core/goals/goal-state.d.ts.map +1 -1
  73. package/dist/core/goals/goal-state.js +1 -0
  74. package/dist/core/goals/goal-state.js.map +1 -1
  75. package/dist/core/goals/goal-tool-core.d.ts +2 -0
  76. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  77. package/dist/core/goals/goal-tool-core.js +1 -1
  78. package/dist/core/goals/goal-tool-core.js.map +1 -1
  79. package/dist/core/model-router-controller.js +2 -2
  80. package/dist/core/model-router-controller.js.map +1 -1
  81. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  82. package/dist/core/orchestration/capability-gateway.js +32 -9
  83. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  84. package/dist/core/orchestration/lane-tool-manifests.js +5 -5
  85. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  86. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -1
  87. package/dist/core/orchestration/policy-compiler.js +7 -0
  88. package/dist/core/orchestration/policy-compiler.js.map +1 -1
  89. package/dist/core/orchestration/profile-registry.js +2 -2
  90. package/dist/core/orchestration/profile-registry.js.map +1 -1
  91. package/dist/core/orchestration/risk-budget.d.ts +0 -10
  92. package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
  93. package/dist/core/orchestration/risk-budget.js +0 -14
  94. package/dist/core/orchestration/risk-budget.js.map +1 -1
  95. package/dist/core/pipelines/active-context.d.ts +16 -0
  96. package/dist/core/pipelines/active-context.d.ts.map +1 -0
  97. package/dist/core/pipelines/active-context.js +29 -0
  98. package/dist/core/pipelines/active-context.js.map +1 -0
  99. package/dist/core/pipelines/context.d.ts +1 -1
  100. package/dist/core/pipelines/context.d.ts.map +1 -1
  101. package/dist/core/pipelines/context.js +40 -31
  102. package/dist/core/pipelines/context.js.map +1 -1
  103. package/dist/core/pipelines/discover.d.ts +5 -1
  104. package/dist/core/pipelines/discover.d.ts.map +1 -1
  105. package/dist/core/pipelines/discover.js +91 -33
  106. package/dist/core/pipelines/discover.js.map +1 -1
  107. package/dist/core/pipelines/increment.d.ts +0 -2
  108. package/dist/core/pipelines/increment.d.ts.map +1 -1
  109. package/dist/core/pipelines/increment.js +35 -13
  110. package/dist/core/pipelines/increment.js.map +1 -1
  111. package/dist/core/pipelines/index.d.ts +4 -3
  112. package/dist/core/pipelines/index.d.ts.map +1 -1
  113. package/dist/core/pipelines/index.js +4 -3
  114. package/dist/core/pipelines/index.js.map +1 -1
  115. package/dist/core/pipelines/run-state.d.ts +8 -0
  116. package/dist/core/pipelines/run-state.d.ts.map +1 -1
  117. package/dist/core/pipelines/run-state.js +172 -38
  118. package/dist/core/pipelines/run-state.js.map +1 -1
  119. package/dist/core/pipelines/types.d.ts +2 -0
  120. package/dist/core/pipelines/types.d.ts.map +1 -1
  121. package/dist/core/pipelines/types.js +21 -4
  122. package/dist/core/pipelines/types.js.map +1 -1
  123. package/dist/core/provider-prompt-contracts.d.ts +1 -0
  124. package/dist/core/provider-prompt-contracts.d.ts.map +1 -1
  125. package/dist/core/provider-prompt-contracts.js +6 -5
  126. package/dist/core/provider-prompt-contracts.js.map +1 -1
  127. package/dist/core/reflection-controller.js +1 -1
  128. package/dist/core/reflection-controller.js.map +1 -1
  129. package/dist/core/runtime-builder.d.ts +2 -1
  130. package/dist/core/runtime-builder.d.ts.map +1 -1
  131. package/dist/core/runtime-builder.js +46 -10
  132. package/dist/core/runtime-builder.js.map +1 -1
  133. package/dist/core/settings-manager.d.ts +5 -6
  134. package/dist/core/settings-manager.d.ts.map +1 -1
  135. package/dist/core/settings-manager.js +8 -13
  136. package/dist/core/settings-manager.js.map +1 -1
  137. package/dist/core/system-prompt-builder.d.ts +2 -4
  138. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  139. package/dist/core/system-prompt-builder.js +35 -11
  140. package/dist/core/system-prompt-builder.js.map +1 -1
  141. package/dist/core/system-prompt.d.ts.map +1 -1
  142. package/dist/core/system-prompt.js +6 -1
  143. package/dist/core/system-prompt.js.map +1 -1
  144. package/dist/core/tasks/task-projection.d.ts +2 -0
  145. package/dist/core/tasks/task-projection.d.ts.map +1 -1
  146. package/dist/core/tasks/task-projection.js +2 -0
  147. package/dist/core/tasks/task-projection.js.map +1 -1
  148. package/dist/core/tasks/task-state.d.ts +10 -0
  149. package/dist/core/tasks/task-state.d.ts.map +1 -1
  150. package/dist/core/tasks/task-state.js +43 -2
  151. package/dist/core/tasks/task-state.js.map +1 -1
  152. package/dist/core/tool-capability-policy.d.ts +13 -3
  153. package/dist/core/tool-capability-policy.d.ts.map +1 -1
  154. package/dist/core/tool-capability-policy.js +136 -28
  155. package/dist/core/tool-capability-policy.js.map +1 -1
  156. package/dist/core/tools/delegate.d.ts +0 -16
  157. package/dist/core/tools/delegate.d.ts.map +1 -1
  158. package/dist/core/tools/delegate.js +21 -11
  159. package/dist/core/tools/delegate.js.map +1 -1
  160. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  161. package/dist/core/tools/edit-diff.js +45 -7
  162. package/dist/core/tools/edit-diff.js.map +1 -1
  163. package/dist/core/tools/edit.d.ts.map +1 -1
  164. package/dist/core/tools/edit.js +5 -1
  165. package/dist/core/tools/edit.js.map +1 -1
  166. package/dist/core/tools/goal.d.ts.map +1 -1
  167. package/dist/core/tools/goal.js +17 -1
  168. package/dist/core/tools/goal.js.map +1 -1
  169. package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
  170. package/dist/core/tools/orchestration-panel.js +1 -0
  171. package/dist/core/tools/orchestration-panel.js.map +1 -1
  172. package/dist/core/tools/persistent-process-coordinator.d.ts.map +1 -1
  173. package/dist/core/tools/persistent-process-coordinator.js +12 -7
  174. package/dist/core/tools/persistent-process-coordinator.js.map +1 -1
  175. package/dist/core/tools/pipeline.d.ts +1 -0
  176. package/dist/core/tools/pipeline.d.ts.map +1 -1
  177. package/dist/core/tools/pipeline.js +95 -44
  178. package/dist/core/tools/pipeline.js.map +1 -1
  179. package/dist/core/tools/profile-writer.d.ts +0 -9
  180. package/dist/core/tools/profile-writer.d.ts.map +1 -1
  181. package/dist/core/tools/profile-writer.js +0 -3
  182. package/dist/core/tools/profile-writer.js.map +1 -1
  183. package/dist/core/tools/secret-store.d.ts.map +1 -1
  184. package/dist/core/tools/secret-store.js +2 -1
  185. package/dist/core/tools/secret-store.js.map +1 -1
  186. package/dist/core/tools/shell-session.d.ts.map +1 -1
  187. package/dist/core/tools/shell-session.js +6 -1
  188. package/dist/core/tools/shell-session.js.map +1 -1
  189. package/dist/core/tools/task-steps.d.ts +10 -0
  190. package/dist/core/tools/task-steps.d.ts.map +1 -1
  191. package/dist/core/tools/task-steps.js +70 -29
  192. package/dist/core/tools/task-steps.js.map +1 -1
  193. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -1
  194. package/dist/core/tools/windows-shell-engine.js +6 -1
  195. package/dist/core/tools/windows-shell-engine.js.map +1 -1
  196. package/dist/core/util/bounded-file.d.ts +8 -0
  197. package/dist/core/util/bounded-file.d.ts.map +1 -1
  198. package/dist/core/util/bounded-file.js +81 -16
  199. package/dist/core/util/bounded-file.js.map +1 -1
  200. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/settings-selector.js +7 -16
  202. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  203. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/thinking-selector.js +2 -11
  205. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  206. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  207. package/dist/modes/interactive/local-model-commands.js +1 -10
  208. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  209. package/dist/modes/interactive/thinking-level-descriptions.d.ts +4 -0
  210. package/dist/modes/interactive/thinking-level-descriptions.d.ts.map +1 -0
  211. package/dist/modes/interactive/thinking-level-descriptions.js +12 -0
  212. package/dist/modes/interactive/thinking-level-descriptions.js.map +1 -0
  213. package/docs/resources.md +1 -1
  214. package/docs/settings.md +7 -5
  215. package/docs/worker-profiles.md +3 -3
  216. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  217. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  218. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  219. package/examples/extensions/sandbox/package-lock.json +2 -2
  220. package/examples/extensions/sandbox/package.json +1 -1
  221. package/examples/extensions/with-deps/package-lock.json +2 -2
  222. package/examples/extensions/with-deps/package.json +1 -1
  223. package/npm-shrinkwrap.json +12 -12
  224. package/package.json +4 -4
@@ -1,5 +1,6 @@
1
1
  import type { HarnessCapability } from "../capability-contract.ts";
2
2
  export declare function hasCapabilityPolicyForTool(toolName: string): boolean;
3
3
  export declare function requiredCapabilitiesForTool(toolName: string, args?: unknown): readonly HarnessCapability[];
4
+ export declare function describeCapabilityRequirementForTool(toolName: string, args?: unknown): string;
4
5
  export declare function hasRequiredCapabilityForTool(capabilities: readonly HarnessCapability[], toolName: string, args?: unknown): boolean;
5
6
  //# sourceMappingURL=approval-gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"approval-gate.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/approval-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAOnE,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,iBAAiB,EAAE,CAE1G;AAED,wBAAgB,4BAA4B,CAC3C,YAAY,EAAE,SAAS,iBAAiB,EAAE,EAC1C,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,GACZ,OAAO,CAET"}
1
+ {"version":3,"file":"approval-gate.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/approval-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAQnE,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,iBAAiB,EAAE,CAE1G;AAED,wBAAgB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAE7F;AAED,wBAAgB,4BAA4B,CAC3C,YAAY,EAAE,SAAS,iBAAiB,EAAE,EAC1C,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,GACZ,OAAO,CAET"}
@@ -1,10 +1,13 @@
1
- import { envelopeHasToolCapability, hasToolCapabilityPolicy, requiredEnvelopeCapabilities, } from "../tool-capability-policy.js";
1
+ import { envelopeHasToolCapability, formatToolCapabilityRequirement, hasToolCapabilityPolicy, requiredEnvelopeCapabilities, } from "../tool-capability-policy.js";
2
2
  export function hasCapabilityPolicyForTool(toolName) {
3
3
  return hasToolCapabilityPolicy(toolName);
4
4
  }
5
5
  export function requiredCapabilitiesForTool(toolName, args) {
6
6
  return requiredEnvelopeCapabilities(toolName, args);
7
7
  }
8
+ export function describeCapabilityRequirementForTool(toolName, args) {
9
+ return formatToolCapabilityRequirement(toolName, args);
10
+ }
8
11
  export function hasRequiredCapabilityForTool(capabilities, toolName, args) {
9
12
  return envelopeHasToolCapability(capabilities, toolName, args);
10
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"approval-gate.js","sourceRoot":"","sources":["../../../src/core/autonomy/approval-gate.ts"],"names":[],"mappings":"AACA,OAAO,EACN,yBAAyB,EACzB,uBAAuB,EACvB,4BAA4B,GAC5B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,UAAU,0BAA0B,CAAC,QAAgB;IAC1D,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAgB,EAAE,IAAc;IAC3E,OAAO,4BAA4B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC3C,YAA0C,EAC1C,QAAgB,EAChB,IAAc;IAEd,OAAO,yBAAyB,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAChE,CAAC","sourcesContent":["import type { HarnessCapability } from \"../capability-contract.ts\";\nimport {\n\tenvelopeHasToolCapability,\n\thasToolCapabilityPolicy,\n\trequiredEnvelopeCapabilities,\n} from \"../tool-capability-policy.ts\";\n\nexport function hasCapabilityPolicyForTool(toolName: string): boolean {\n\treturn hasToolCapabilityPolicy(toolName);\n}\n\nexport function requiredCapabilitiesForTool(toolName: string, args?: unknown): readonly HarnessCapability[] {\n\treturn requiredEnvelopeCapabilities(toolName, args);\n}\n\nexport function hasRequiredCapabilityForTool(\n\tcapabilities: readonly HarnessCapability[],\n\ttoolName: string,\n\targs?: unknown,\n): boolean {\n\treturn envelopeHasToolCapability(capabilities, toolName, args);\n}\n"]}
1
+ {"version":3,"file":"approval-gate.js","sourceRoot":"","sources":["../../../src/core/autonomy/approval-gate.ts"],"names":[],"mappings":"AACA,OAAO,EACN,yBAAyB,EACzB,+BAA+B,EAC/B,uBAAuB,EACvB,4BAA4B,GAC5B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,UAAU,0BAA0B,CAAC,QAAgB;IAC1D,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAgB,EAAE,IAAc;IAC3E,OAAO,4BAA4B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,oCAAoC,CAAC,QAAgB,EAAE,IAAc;IACpF,OAAO,+BAA+B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC3C,YAA0C,EAC1C,QAAgB,EAChB,IAAc;IAEd,OAAO,yBAAyB,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAChE,CAAC","sourcesContent":["import type { HarnessCapability } from \"../capability-contract.ts\";\nimport {\n\tenvelopeHasToolCapability,\n\tformatToolCapabilityRequirement,\n\thasToolCapabilityPolicy,\n\trequiredEnvelopeCapabilities,\n} from \"../tool-capability-policy.ts\";\n\nexport function hasCapabilityPolicyForTool(toolName: string): boolean {\n\treturn hasToolCapabilityPolicy(toolName);\n}\n\nexport function requiredCapabilitiesForTool(toolName: string, args?: unknown): readonly HarnessCapability[] {\n\treturn requiredEnvelopeCapabilities(toolName, args);\n}\n\nexport function describeCapabilityRequirementForTool(toolName: string, args?: unknown): string {\n\treturn formatToolCapabilityRequirement(toolName, args);\n}\n\nexport function hasRequiredCapabilityForTool(\n\tcapabilities: readonly HarnessCapability[],\n\ttoolName: string,\n\targs?: unknown,\n): boolean {\n\treturn envelopeHasToolCapability(capabilities, toolName, args);\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { AgentTool } from "@caupulican/pi-agent-core";
2
+ import type { TSchema } from "typebox";
3
+ import type { CapabilityEnvelope } from "./contracts.ts";
4
+ /**
5
+ * Derive one immutable child-tool view from an already-authorized composite tool call.
6
+ * Explicit child denials and every capability/path bound remain intact. An allowlist is widened
7
+ * only when it actually grants the parent; direct or accidental use cannot turn a denied parent
8
+ * into authority for its implementation tools.
9
+ */
10
+ export declare function deriveCompositeChildEnvelope(parentToolName: string, childToolNames: readonly string[], envelope: CapabilityEnvelope | undefined): CapabilityEnvelope | undefined;
11
+ /** Apply the complete autonomy gate to a composite tool's concrete child tool at execution time. */
12
+ export declare function wrapToolWithCapabilityEnvelopeGate<TParameters extends TSchema, TDetails>(tool: AgentTool<TParameters, TDetails>, cwd: string, envelope: CapabilityEnvelope | undefined): AgentTool<TParameters, TDetails>;
13
+ //# sourceMappingURL=composite-tool-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-tool-gate.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/composite-tool-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAiBzD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC3C,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,QAAQ,EAAE,kBAAkB,GAAG,SAAS,GACtC,kBAAkB,GAAG,SAAS,CAUhC;AAED,oGAAoG;AACpG,wBAAgB,kCAAkC,CAAC,WAAW,SAAS,OAAO,EAAE,QAAQ,EACvF,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,kBAAkB,GAAG,SAAS,GACtC,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAclC"}
@@ -0,0 +1,44 @@
1
+ import { evaluateToolGate } from "./gates.js";
2
+ function copyEnvelope(envelope, allowedTools = envelope.allowedTools) {
3
+ return {
4
+ ...envelope,
5
+ capabilities: [...envelope.capabilities],
6
+ ...(allowedTools ? { allowedTools: [...allowedTools] } : {}),
7
+ ...(envelope.deniedTools ? { deniedTools: [...envelope.deniedTools] } : {}),
8
+ ...(envelope.allowedPaths ? { allowedPaths: [...envelope.allowedPaths] } : {}),
9
+ ...(envelope.deniedPaths ? { deniedPaths: [...envelope.deniedPaths] } : {}),
10
+ };
11
+ }
12
+ /**
13
+ * Derive one immutable child-tool view from an already-authorized composite tool call.
14
+ * Explicit child denials and every capability/path bound remain intact. An allowlist is widened
15
+ * only when it actually grants the parent; direct or accidental use cannot turn a denied parent
16
+ * into authority for its implementation tools.
17
+ */
18
+ export function deriveCompositeChildEnvelope(parentToolName, childToolNames, envelope) {
19
+ if (!envelope)
20
+ return undefined;
21
+ if (envelope.deniedTools?.includes(parentToolName) ||
22
+ (envelope.allowedTools !== undefined && !envelope.allowedTools.includes(parentToolName))) {
23
+ return copyEnvelope(envelope, []);
24
+ }
25
+ if (!envelope.allowedTools)
26
+ return copyEnvelope(envelope);
27
+ return copyEnvelope(envelope, [...new Set([...envelope.allowedTools, ...childToolNames])]);
28
+ }
29
+ /** Apply the complete autonomy gate to a composite tool's concrete child tool at execution time. */
30
+ export function wrapToolWithCapabilityEnvelopeGate(tool, cwd, envelope) {
31
+ if (!envelope)
32
+ return tool;
33
+ return {
34
+ ...tool,
35
+ async execute(toolCallId, params, signal, onUpdate) {
36
+ const outcome = evaluateToolGate({ toolName: tool.name, args: params, cwd, envelope });
37
+ if (outcome.outcome === "block" || outcome.outcome === "ask-user") {
38
+ throw new Error(`Tool '${tool.name}' execution blocked by autonomy gate [${outcome.gate}]: ${outcome.message ?? "denied"} (${outcome.reasonCode})`);
39
+ }
40
+ return tool.execute(toolCallId, params, signal, onUpdate);
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=composite-tool-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-tool-gate.js","sourceRoot":"","sources":["../../../src/core/autonomy/composite-tool-gate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,SAAS,YAAY,CACpB,QAA4B,EAC5B,YAAY,GAAkC,QAAQ,CAAC,YAAY;IAEnE,OAAO;QACN,GAAG,QAAQ;QACX,YAAY,EAAE,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC;QACxC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3E,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC3C,cAAsB,EACtB,cAAiC,EACjC,QAAwC;IAExC,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,IACC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC;QAC9C,CAAC,QAAQ,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,EACvF,CAAC;QACF,OAAO,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,YAAY;QAAE,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC1D,OAAO,YAAY,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,YAAY,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,kCAAkC,CACjD,IAAsC,EACtC,GAAW,EACX,QAAwC;IAExC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO;QACN,GAAG,IAAI;QACP,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;YACjD,MAAM,OAAO,GAAG,gBAAgB,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YACvF,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBACnE,MAAM,IAAI,KAAK,CACd,SAAS,IAAI,CAAC,IAAI,yCAAyC,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,OAAO,IAAI,QAAQ,KAAK,OAAO,CAAC,UAAU,GAAG,CAClI,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3D,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import type { AgentTool } from \"@caupulican/pi-agent-core\";\nimport type { TSchema } from \"typebox\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { evaluateToolGate } from \"./gates.ts\";\n\nfunction copyEnvelope(\n\tenvelope: CapabilityEnvelope,\n\tallowedTools: readonly string[] | undefined = envelope.allowedTools,\n): CapabilityEnvelope {\n\treturn {\n\t\t...envelope,\n\t\tcapabilities: [...envelope.capabilities],\n\t\t...(allowedTools ? { allowedTools: [...allowedTools] } : {}),\n\t\t...(envelope.deniedTools ? { deniedTools: [...envelope.deniedTools] } : {}),\n\t\t...(envelope.allowedPaths ? { allowedPaths: [...envelope.allowedPaths] } : {}),\n\t\t...(envelope.deniedPaths ? { deniedPaths: [...envelope.deniedPaths] } : {}),\n\t};\n}\n\n/**\n * Derive one immutable child-tool view from an already-authorized composite tool call.\n * Explicit child denials and every capability/path bound remain intact. An allowlist is widened\n * only when it actually grants the parent; direct or accidental use cannot turn a denied parent\n * into authority for its implementation tools.\n */\nexport function deriveCompositeChildEnvelope(\n\tparentToolName: string,\n\tchildToolNames: readonly string[],\n\tenvelope: CapabilityEnvelope | undefined,\n): CapabilityEnvelope | undefined {\n\tif (!envelope) return undefined;\n\tif (\n\t\tenvelope.deniedTools?.includes(parentToolName) ||\n\t\t(envelope.allowedTools !== undefined && !envelope.allowedTools.includes(parentToolName))\n\t) {\n\t\treturn copyEnvelope(envelope, []);\n\t}\n\tif (!envelope.allowedTools) return copyEnvelope(envelope);\n\treturn copyEnvelope(envelope, [...new Set([...envelope.allowedTools, ...childToolNames])]);\n}\n\n/** Apply the complete autonomy gate to a composite tool's concrete child tool at execution time. */\nexport function wrapToolWithCapabilityEnvelopeGate<TParameters extends TSchema, TDetails>(\n\ttool: AgentTool<TParameters, TDetails>,\n\tcwd: string,\n\tenvelope: CapabilityEnvelope | undefined,\n): AgentTool<TParameters, TDetails> {\n\tif (!envelope) return tool;\n\treturn {\n\t\t...tool,\n\t\tasync execute(toolCallId, params, signal, onUpdate) {\n\t\t\tconst outcome = evaluateToolGate({ toolName: tool.name, args: params, cwd, envelope });\n\t\t\tif (outcome.outcome === \"block\" || outcome.outcome === \"ask-user\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Tool '${tool.name}' execution blocked by autonomy gate [${outcome.gate}]: ${outcome.message ?? \"denied\"} (${outcome.reasonCode})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn tool.execute(toolCallId, params, signal, onUpdate);\n\t\t},\n\t};\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  import type { CapabilityEnvelope } from "./contracts.ts";
2
2
  export declare function extractPathArguments(params: unknown): string[];
3
+ /** Tool-aware path projection shared by every envelope/gateway enforcement boundary. */
4
+ export declare function extractToolPathArguments(toolName: string, params: unknown): string[];
3
5
  /**
4
6
  * Deny wins over allow; an empty/absent allow list means "no positive scope restriction"
5
7
  * (only denies apply) — mirroring the resource-profile filter semantics.
@@ -1 +1 @@
1
- {"version":3,"file":"envelope-enforcement.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAezD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiB9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAuBxG;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,kBAAkB,EACrE,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,MAAM,GACT,CAAC,CAsBH"}
1
+ {"version":3,"file":"envelope-enforcement.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAezD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiB9D;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAuBpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAoCxG;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,kBAAkB,EACrE,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,MAAM,GACT,CAAC,CAsBH"}
@@ -17,20 +17,45 @@ export function extractPathArguments(params) {
17
17
  const found = [];
18
18
  for (const key of PATH_ARGUMENT_KEYS) {
19
19
  const value = record[key];
20
- if (typeof value === "string" && value.length > 0)
21
- found.push(value);
20
+ if (typeof value === "string" && value.trim())
21
+ found.push(value.trim());
22
22
  }
23
23
  for (const key of PATH_LIST_ARGUMENT_KEYS) {
24
24
  const value = record[key];
25
25
  if (Array.isArray(value)) {
26
26
  for (const entry of value) {
27
- if (typeof entry === "string" && entry.length > 0)
28
- found.push(entry);
27
+ if (typeof entry === "string" && entry.trim())
28
+ found.push(entry.trim());
29
29
  }
30
30
  }
31
31
  }
32
32
  return found;
33
33
  }
34
+ /** Tool-aware path projection shared by every envelope/gateway enforcement boundary. */
35
+ export function extractToolPathArguments(toolName, params) {
36
+ const normalizedToolName = toolName.toLowerCase();
37
+ const paths = extractPathArguments(params);
38
+ if (normalizedToolName === "secret_store" && params && typeof params === "object") {
39
+ const record = params;
40
+ if (record.action === "migrate" && Array.isArray(record.sources)) {
41
+ for (const source of record.sources) {
42
+ if (!source || typeof source !== "object" || !("path" in source))
43
+ continue;
44
+ const sourcePath = source.path;
45
+ if (typeof sourcePath === "string" && sourcePath.trim())
46
+ paths.push(sourcePath.trim());
47
+ }
48
+ }
49
+ }
50
+ if (paths.length === 0 &&
51
+ (normalizedToolName === "find" ||
52
+ normalizedToolName === "grep" ||
53
+ normalizedToolName === "ls" ||
54
+ normalizedToolName === "pipeline")) {
55
+ paths.push(".");
56
+ }
57
+ return paths;
58
+ }
34
59
  /**
35
60
  * Deny wins over allow; an empty/absent allow list means "no positive scope restriction"
36
61
  * (only denies apply) — mirroring the resource-profile filter semantics.
@@ -41,9 +66,26 @@ export function extractPathArguments(params) {
41
66
  * denied subtree is still denied. An unresolvable target fails closed.
42
67
  */
43
68
  export function isPathWithinEnvelope(envelope, rawPath, cwd) {
69
+ const lexicalTarget = resolve(cwd, rawPath);
70
+ const allowed = envelope.allowedPaths ?? [];
71
+ if (allowed.length > 0) {
72
+ const couldBeAllowed = allowed.some((root) => {
73
+ const lexicalRoot = resolve(cwd, root);
74
+ if (isPathWithinScope(lexicalTarget, lexicalRoot))
75
+ return true;
76
+ try {
77
+ return isPathWithinScope(lexicalTarget, safeRealpathSync(lexicalRoot));
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ });
83
+ if (!couldBeAllowed)
84
+ return false;
85
+ }
44
86
  let target;
45
87
  try {
46
- target = safeRealpathSync(resolve(cwd, rawPath));
88
+ target = safeRealpathSync(lexicalTarget);
47
89
  }
48
90
  catch {
49
91
  return false;
@@ -57,7 +99,6 @@ export function isPathWithinEnvelope(envelope, rawPath, cwd) {
57
99
  // Mirror checkPathScope: an unresolvable deny root cannot match anything.
58
100
  }
59
101
  }
60
- const allowed = envelope.allowedPaths ?? [];
61
102
  if (allowed.length === 0)
62
103
  return true;
63
104
  return allowed.some((root) => {
@@ -78,7 +119,7 @@ export function wrapToolWithEnvelopeScope(tool, envelope, cwd) {
78
119
  ...tool,
79
120
  execute: (...args) => {
80
121
  const params = args[1];
81
- for (const rawPath of extractPathArguments(params)) {
122
+ for (const rawPath of extractToolPathArguments(tool.name, params)) {
82
123
  if (!isPathWithinEnvelope(envelope, rawPath, cwd)) {
83
124
  return {
84
125
  content: [
@@ -1 +1 @@
1
- {"version":3,"file":"envelope-enforcement.js","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;;;;;GAOG;AAEH,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAU,CAAC;AAC3G,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAU,CAAC;AAE5D,MAAM,UAAU,oBAAoB,CAAC,MAAe;IACnD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACrD,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,uBAAuB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtE,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA4B,EAAE,OAAe,EAAE,GAAW;IAC9F,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACJ,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC;YACJ,IAAI,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrF,CAAC;QAAC,MAAM,CAAC;YACR,0EAA0E;QAC3E,CAAC;IACF,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,IAAI,CAAC;YACJ,OAAO,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACxC,IAAO,EACP,QAA4B,EAC5B,GAAW;IAEX,OAAO;QACN,GAAG,IAAI;QACP,OAAO,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACvB,KAAK,MAAM,OAAO,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpD,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;oBACnD,OAAO;wBACN,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,0BAA0B,OAAO,yBAAyB,QAAQ,CAAC,EAAE,sCAAsC;6BACjH;yBACD;wBACD,OAAO,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE;wBACrG,OAAO,EAAE,IAAI;qBACb,CAAC;gBACH,CAAC;YACF,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9B,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\n\n/**\n * Tool-level envelope enforcement (G2 prerequisite for code-writing workers): the capability\n * envelope's `allowedPaths`/`deniedPaths` were previously VALIDATION-ONLY — recorded on the\n * envelope but never checked when a tool actually ran. This module wraps tools so path-bearing\n * arguments are checked AT EXECUTION TIME, structurally refusing out-of-scope paths the same way\n * a failed script can never look like success: the refusal is an isError result with a stable\n * outcome code, never a silent no-op.\n */\n\nconst PATH_ARGUMENT_KEYS = [\"path\", \"file_path\", \"filePath\", \"cwd\", \"directory\", \"dir\", \"target\"] as const;\nconst PATH_LIST_ARGUMENT_KEYS = [\"paths\", \"files\"] as const;\n\nexport function extractPathArguments(params: unknown): string[] {\n\tif (!params || typeof params !== \"object\") return [];\n\tconst record = params as Record<string, unknown>;\n\tconst found: string[] = [];\n\tfor (const key of PATH_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.length > 0) found.push(value);\n\t}\n\tfor (const key of PATH_LIST_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const entry of value) {\n\t\t\t\tif (typeof entry === \"string\" && entry.length > 0) found.push(entry);\n\t\t\t}\n\t\t}\n\t}\n\treturn found;\n}\n\n/**\n * Deny wins over allow; an empty/absent allow list means \"no positive scope restriction\"\n * (only denies apply) — mirroring the resource-profile filter semantics.\n *\n * Both the target and every scope root are resolved through the real filesystem\n * (symlinks expanded in the existing prefix) before comparison: a pre-existing symlink\n * under an allowed root cannot smuggle a write outside the scope, and a shortcut into a\n * denied subtree is still denied. An unresolvable target fails closed.\n */\nexport function isPathWithinEnvelope(envelope: CapabilityEnvelope, rawPath: string, cwd: string): boolean {\n\tlet target: string;\n\ttry {\n\t\ttarget = safeRealpathSync(resolve(cwd, rawPath));\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const denied of envelope.deniedPaths ?? []) {\n\t\ttry {\n\t\t\tif (isPathWithinScope(target, safeRealpathSync(resolve(cwd, denied)))) return false;\n\t\t} catch {\n\t\t\t// Mirror checkPathScope: an unresolvable deny root cannot match anything.\n\t\t}\n\t}\n\tconst allowed = envelope.allowedPaths ?? [];\n\tif (allowed.length === 0) return true;\n\treturn allowed.some((root) => {\n\t\ttry {\n\t\t\treturn isPathWithinScope(target, safeRealpathSync(resolve(cwd, root)));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nexport interface EnvelopeScopedTool {\n\tname: string;\n\texecute: (...args: unknown[]) => unknown;\n}\n\n/**\n * Wrap a tool so every path-bearing argument is scope-checked when it RUNS. The wrapped tool is\n * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).\n */\nexport function wrapToolWithEnvelopeScope<T extends EnvelopeScopedTool>(\n\ttool: T,\n\tenvelope: CapabilityEnvelope,\n\tcwd: string,\n): T {\n\treturn {\n\t\t...tool,\n\t\texecute: (...args: unknown[]) => {\n\t\t\tconst params = args[1];\n\t\t\tfor (const rawPath of extractPathArguments(params)) {\n\t\t\t\tif (!isPathWithinEnvelope(envelope, rawPath, cwd)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: `envelope_path_denied: \"${rawPath}\" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: { outcome: \"envelope_path_denied\", tool: tool.name, path: rawPath, envelopeId: envelope.id },\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tool.execute(...args);\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"envelope-enforcement.js","sourceRoot":"","sources":["../../../src/core/autonomy/envelope-enforcement.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\n\n/**\n * Tool-level envelope enforcement (G2 prerequisite for code-writing workers): the capability\n * envelope's `allowedPaths`/`deniedPaths` were previously VALIDATION-ONLY — recorded on the\n * envelope but never checked when a tool actually ran. This module wraps tools so path-bearing\n * arguments are checked AT EXECUTION TIME, structurally refusing out-of-scope paths the same way\n * a failed script can never look like success: the refusal is an isError result with a stable\n * outcome code, never a silent no-op.\n */\n\nconst PATH_ARGUMENT_KEYS = [\"path\", \"file_path\", \"filePath\", \"cwd\", \"directory\", \"dir\", \"target\"] as const;\nconst PATH_LIST_ARGUMENT_KEYS = [\"paths\", \"files\"] as const;\n\nexport function extractPathArguments(params: unknown): string[] {\n\tif (!params || typeof params !== \"object\") return [];\n\tconst record = params as Record<string, unknown>;\n\tconst found: string[] = [];\n\tfor (const key of PATH_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) found.push(value.trim());\n\t}\n\tfor (const key of PATH_LIST_ARGUMENT_KEYS) {\n\t\tconst value = record[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const entry of value) {\n\t\t\t\tif (typeof entry === \"string\" && entry.trim()) found.push(entry.trim());\n\t\t\t}\n\t\t}\n\t}\n\treturn found;\n}\n\n/** Tool-aware path projection shared by every envelope/gateway enforcement boundary. */\nexport function extractToolPathArguments(toolName: string, params: unknown): string[] {\n\tconst normalizedToolName = toolName.toLowerCase();\n\tconst paths = extractPathArguments(params);\n\tif (normalizedToolName === \"secret_store\" && params && typeof params === \"object\") {\n\t\tconst record = params as Record<string, unknown>;\n\t\tif (record.action === \"migrate\" && Array.isArray(record.sources)) {\n\t\t\tfor (const source of record.sources) {\n\t\t\t\tif (!source || typeof source !== \"object\" || !(\"path\" in source)) continue;\n\t\t\t\tconst sourcePath = source.path;\n\t\t\t\tif (typeof sourcePath === \"string\" && sourcePath.trim()) paths.push(sourcePath.trim());\n\t\t\t}\n\t\t}\n\t}\n\tif (\n\t\tpaths.length === 0 &&\n\t\t(normalizedToolName === \"find\" ||\n\t\t\tnormalizedToolName === \"grep\" ||\n\t\t\tnormalizedToolName === \"ls\" ||\n\t\t\tnormalizedToolName === \"pipeline\")\n\t) {\n\t\tpaths.push(\".\");\n\t}\n\treturn paths;\n}\n\n/**\n * Deny wins over allow; an empty/absent allow list means \"no positive scope restriction\"\n * (only denies apply) — mirroring the resource-profile filter semantics.\n *\n * Both the target and every scope root are resolved through the real filesystem\n * (symlinks expanded in the existing prefix) before comparison: a pre-existing symlink\n * under an allowed root cannot smuggle a write outside the scope, and a shortcut into a\n * denied subtree is still denied. An unresolvable target fails closed.\n */\nexport function isPathWithinEnvelope(envelope: CapabilityEnvelope, rawPath: string, cwd: string): boolean {\n\tconst lexicalTarget = resolve(cwd, rawPath);\n\tconst allowed = envelope.allowedPaths ?? [];\n\tif (allowed.length > 0) {\n\t\tconst couldBeAllowed = allowed.some((root) => {\n\t\t\tconst lexicalRoot = resolve(cwd, root);\n\t\t\tif (isPathWithinScope(lexicalTarget, lexicalRoot)) return true;\n\t\t\ttry {\n\t\t\t\treturn isPathWithinScope(lexicalTarget, safeRealpathSync(lexicalRoot));\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t\tif (!couldBeAllowed) return false;\n\t}\n\tlet target: string;\n\ttry {\n\t\ttarget = safeRealpathSync(lexicalTarget);\n\t} catch {\n\t\treturn false;\n\t}\n\tfor (const denied of envelope.deniedPaths ?? []) {\n\t\ttry {\n\t\t\tif (isPathWithinScope(target, safeRealpathSync(resolve(cwd, denied)))) return false;\n\t\t} catch {\n\t\t\t// Mirror checkPathScope: an unresolvable deny root cannot match anything.\n\t\t}\n\t}\n\tif (allowed.length === 0) return true;\n\treturn allowed.some((root) => {\n\t\ttry {\n\t\t\treturn isPathWithinScope(target, safeRealpathSync(resolve(cwd, root)));\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nexport interface EnvelopeScopedTool {\n\tname: string;\n\texecute: (...args: unknown[]) => unknown;\n}\n\n/**\n * Wrap a tool so every path-bearing argument is scope-checked when it RUNS. The wrapped tool is\n * shape-identical; params are conventionally the second execute argument (toolCallId, params, …).\n */\nexport function wrapToolWithEnvelopeScope<T extends EnvelopeScopedTool>(\n\ttool: T,\n\tenvelope: CapabilityEnvelope,\n\tcwd: string,\n): T {\n\treturn {\n\t\t...tool,\n\t\texecute: (...args: unknown[]) => {\n\t\t\tconst params = args[1];\n\t\t\tfor (const rawPath of extractToolPathArguments(tool.name, params)) {\n\t\t\t\tif (!isPathWithinEnvelope(envelope, rawPath, cwd)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: `envelope_path_denied: \"${rawPath}\" is outside envelope ${envelope.id}'s path scope. The tool was NOT run.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: { outcome: \"envelope_path_denied\", tool: tool.name, path: rawPath, envelopeId: envelope.id },\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tool.execute(...args);\n\t\t},\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAC;AAmBvF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA+BjF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAWhH;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CA+B/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,WAAW,CAwJd"}
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAC;AAoBvF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA+BjF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAWhH;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,WAAW,CA4Id"}
@@ -1,6 +1,8 @@
1
1
  import path from "node:path";
2
- import { hasCapabilityPolicyForTool, hasRequiredCapabilityForTool, requiredCapabilitiesForTool, } from "./approval-gate.js";
3
- import { checkPathScope } from "./path-scope.js";
2
+ import { resolveToolCallCapabilities, resolveToolCallPathAccess, toolUsesPathScope, } from "../tool-capability-policy.js";
3
+ import { describeCapabilityRequirementForTool, hasCapabilityPolicyForTool } from "./approval-gate.js";
4
+ import { extractToolPathArguments, isPathWithinEnvelope } from "./envelope-enforcement.js";
5
+ import { isPathWithinScope, safeRealpathSync } from "./path-scope.js";
4
6
  import { assessOperationRisk } from "./risk-assessment.js";
5
7
  function isGateOutcomeKind(value) {
6
8
  return (value === "allow" || value === "downgrade" || value === "escalate" || value === "ask-user" || value === "block");
@@ -58,32 +60,7 @@ export function fallbackGateOutcome(args) {
58
60
  };
59
61
  }
60
62
  export function extractCandidatePaths(toolName, args) {
61
- if (!args || typeof args !== "object")
62
- return [];
63
- const obj = args;
64
- const paths = [];
65
- if (toolName === "read" ||
66
- toolName === "write" ||
67
- toolName === "edit" ||
68
- toolName === "ls" ||
69
- toolName === "grep" ||
70
- toolName === "find") {
71
- if (typeof obj.path === "string" && obj.path.trim()) {
72
- paths.push(obj.path.trim());
73
- }
74
- }
75
- else if (toolName === "secret_store" && obj.action === "migrate" && Array.isArray(obj.sources)) {
76
- for (const source of obj.sources) {
77
- if (source &&
78
- typeof source === "object" &&
79
- "path" in source &&
80
- typeof source.path === "string" &&
81
- source.path.trim()) {
82
- paths.push(source.path.trim());
83
- }
84
- }
85
- }
86
- return paths;
63
+ return toolUsesPathScope(toolName) ? extractToolPathArguments(toolName, args) : [];
87
64
  }
88
65
  export function evaluateToolGate(input) {
89
66
  if (!input.envelope) {
@@ -112,51 +89,8 @@ export function evaluateToolGate(input) {
112
89
  message: `Tool '${input.toolName}' is not in the allowed tools list.`,
113
90
  };
114
91
  }
115
- // 2. Path scope containment for file tools
116
- const paths = extractCandidatePaths(input.toolName, input.args);
117
- if (paths.length > 0 && envelope.allowedPaths) {
118
- // If envelope has allowedPaths, we must check them
119
- for (const targetPath of paths) {
120
- const scopedTargetPath = path.isAbsolute(targetPath) ? targetPath : path.resolve(input.cwd, targetPath);
121
- let isInsideAny = false;
122
- let isDenied = false;
123
- let denyRule = "";
124
- for (const allowedRoot of envelope.allowedPaths) {
125
- const decision = checkPathScope({
126
- root: allowedRoot,
127
- allowedPaths: envelope.allowedPaths,
128
- deniedPaths: envelope.deniedPaths,
129
- }, scopedTargetPath);
130
- if (decision.kind === "denied") {
131
- isDenied = true;
132
- denyRule = decision.matchedRule || "";
133
- break;
134
- }
135
- if (decision.kind === "inside") {
136
- isInsideAny = true;
137
- }
138
- }
139
- if (isDenied) {
140
- return {
141
- outcome: "block",
142
- gate: "path_scope",
143
- reasonCode: "path_denied",
144
- message: `Path '${targetPath}' is explicitly denied by rule '${denyRule}'.`,
145
- };
146
- }
147
- if (!isInsideAny) {
148
- // Block only if the tool is mutating. Wait, read path outside allowed root -> block.
149
- // "read path inside allowed root -> allow. write/edit path outside allowed root -> block. denied path inside allowed root -> block."
150
- return {
151
- outcome: "block",
152
- gate: "path_scope",
153
- reasonCode: "path_outside_allowed_roots",
154
- message: `Path '${targetPath}' is outside all allowed roots.`,
155
- };
156
- }
157
- }
158
- }
159
- // 2.5. Capability checks
92
+ // 2. Capability checks. Resolve the selected alternatives once so action-sensitive path
93
+ // enforcement cannot disagree with capability admission.
160
94
  if (!hasCapabilityPolicyForTool(input.toolName)) {
161
95
  return {
162
96
  outcome: "block",
@@ -165,15 +99,53 @@ export function evaluateToolGate(input) {
165
99
  message: `Tool '${input.toolName}' has no capability policy in the active envelope.`,
166
100
  };
167
101
  }
168
- const requiredCaps = requiredCapabilitiesForTool(input.toolName, input.args);
169
- if (!hasRequiredCapabilityForTool(envelope.capabilities, input.toolName, input.args)) {
102
+ const callCapabilities = resolveToolCallCapabilities(envelope.capabilities, input.toolName, input.args);
103
+ if (!callCapabilities) {
104
+ const requirement = describeCapabilityRequirementForTool(input.toolName, input.args);
170
105
  return {
171
106
  outcome: "block",
172
107
  gate: "tool_gate",
173
108
  reasonCode: "missing_capability",
174
- message: `Tool '${input.toolName}' requires capability '${requiredCaps.join(" or ")}', which is missing from the active envelope.`,
109
+ message: `Tool '${input.toolName}' requires ${requirement || "a classified capability"}, which is missing from the active envelope.`,
175
110
  };
176
111
  }
112
+ // 3. Path scope containment for caller-controlled paths. A workflow.plan pipeline read is a
113
+ // fixed-root control-plane operation; selecting filesystem.read instead keeps the path gate.
114
+ const pathAccess = resolveToolCallPathAccess(envelope.capabilities, input.toolName, input.args);
115
+ const paths = pathAccess === "none" ? [] : extractCandidatePaths(input.toolName, input.args);
116
+ if (paths.length > 0) {
117
+ for (const targetPath of paths) {
118
+ if (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {
119
+ let isDenied = false;
120
+ try {
121
+ const target = safeRealpathSync(path.resolve(input.cwd, targetPath));
122
+ isDenied = (envelope.deniedPaths ?? []).some((denied) => {
123
+ try {
124
+ return isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ });
130
+ }
131
+ catch { }
132
+ if (isDenied) {
133
+ return {
134
+ outcome: "block",
135
+ gate: "path_scope",
136
+ reasonCode: "path_denied",
137
+ message: `Path '${targetPath}' is explicitly denied.`,
138
+ };
139
+ }
140
+ return {
141
+ outcome: "block",
142
+ gate: "path_scope",
143
+ reasonCode: "path_outside_allowed_roots",
144
+ message: `Path '${targetPath}' is outside all allowed roots.`,
145
+ };
146
+ }
147
+ }
148
+ }
177
149
  let command = "";
178
150
  if (input.toolName === "bash" ||
179
151
  input.toolName === "powershell" ||
@@ -1 +1 @@
1
- {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport {\n\thasCapabilityPolicyForTool,\n\thasRequiredCapabilityForTool,\n\trequiredCapabilitiesForTool,\n} from \"./approval-gate.ts\";\nimport type { CapabilityEnvelope, GateOutcome, GateOutcomeKind } from \"./contracts.ts\";\nimport { checkPathScope } from \"./path-scope.ts\";\nimport { assessOperationRisk } from \"./risk-assessment.ts\";\n\nfunction isGateOutcomeKind(value: unknown): value is GateOutcomeKind {\n\treturn (\n\t\tvalue === \"allow\" || value === \"downgrade\" || value === \"escalate\" || value === \"ask-user\" || value === \"block\"\n\t);\n}\n\nfunction getPrecedence(kind: unknown): number {\n\tif (kind === \"allow\") return 0;\n\tif (kind === \"downgrade\") return 1;\n\tif (kind === \"escalate\") return 2;\n\tif (kind === \"ask-user\") return 3;\n\tif (kind === \"block\") return 4;\n\treturn 4; // Malformed/unknown outcome kind defaults to most restrictive (block)\n}\n\nexport function combineGateOutcomes(outcomes: readonly GateOutcome[]): GateOutcome {\n\tif (outcomes.length === 0) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"gate-combiner\",\n\t\t\treasonCode: \"no_gate_outcomes\",\n\t\t\tmessage: \"No gate outcomes to combine\",\n\t\t};\n\t}\n\n\tlet winner = outcomes[0];\n\tlet maxPrecedence = getPrecedence(winner.outcome);\n\n\tfor (let i = 1; i < outcomes.length; i++) {\n\t\tconst current = outcomes[i];\n\t\tconst currentPrecedence = getPrecedence(current.outcome);\n\t\tif (currentPrecedence > maxPrecedence) {\n\t\t\twinner = current;\n\t\t\tmaxPrecedence = currentPrecedence;\n\t\t}\n\t}\n\n\tif (!isGateOutcomeKind(winner.outcome)) {\n\t\treturn {\n\t\t\t...winner,\n\t\t\toutcome: \"block\",\n\t\t\tmessage: winner.message || \"Malformed outcome kind coerced to block\",\n\t\t};\n\t}\n\n\treturn winner;\n}\n\nexport function fallbackGateOutcome(args: { gate: string; reversible: boolean; reasonCode: string }): GateOutcome {\n\tconst gate = (args.gate || \"\").trim() || \"unknown_gate\";\n\tconst reasonCode = (args.reasonCode || \"\").trim() || \"unknown_reason\";\n\tconst outcome: GateOutcomeKind = args.reversible ? \"ask-user\" : \"block\";\n\n\treturn {\n\t\toutcome,\n\t\tgate,\n\t\treasonCode,\n\t\tmessage: `Fallback gate outcome: ${outcome} for gate ${gate} (${reasonCode})`,\n\t};\n}\n\nexport function extractCandidatePaths(toolName: string, args: unknown): string[] {\n\tif (!args || typeof args !== \"object\") return [];\n\tconst obj = args as Record<string, unknown>;\n\tconst paths: string[] = [];\n\n\tif (\n\t\ttoolName === \"read\" ||\n\t\ttoolName === \"write\" ||\n\t\ttoolName === \"edit\" ||\n\t\ttoolName === \"ls\" ||\n\t\ttoolName === \"grep\" ||\n\t\ttoolName === \"find\"\n\t) {\n\t\tif (typeof obj.path === \"string\" && obj.path.trim()) {\n\t\t\tpaths.push(obj.path.trim());\n\t\t}\n\t} else if (toolName === \"secret_store\" && obj.action === \"migrate\" && Array.isArray(obj.sources)) {\n\t\tfor (const source of obj.sources) {\n\t\t\tif (\n\t\t\t\tsource &&\n\t\t\t\ttypeof source === \"object\" &&\n\t\t\t\t\"path\" in source &&\n\t\t\t\ttypeof source.path === \"string\" &&\n\t\t\t\tsource.path.trim()\n\t\t\t) {\n\t\t\t\tpaths.push(source.path.trim());\n\t\t\t}\n\t\t}\n\t}\n\n\treturn paths;\n}\n\nexport function evaluateToolGate(input: {\n\ttoolName: string;\n\targs?: unknown;\n\tcwd: string;\n\tenvelope?: CapabilityEnvelope;\n}): GateOutcome {\n\tif (!input.envelope) {\n\t\treturn {\n\t\t\toutcome: \"allow\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"no_envelope\",\n\t\t\tmessage: \"No envelope active, preserving existing session behavior.\",\n\t\t};\n\t}\n\n\tconst envelope = input.envelope;\n\n\t// 1. Tool allow/deny list overrides\n\tif (envelope.deniedTools?.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_denied\",\n\t\t\tmessage: `Tool '${input.toolName}' is explicitly denied.`,\n\t\t};\n\t}\n\n\tif (envelope.allowedTools && !envelope.allowedTools.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_not_allowed\",\n\t\t\tmessage: `Tool '${input.toolName}' is not in the allowed tools list.`,\n\t\t};\n\t}\n\n\t// 2. Path scope containment for file tools\n\tconst paths = extractCandidatePaths(input.toolName, input.args);\n\tif (paths.length > 0 && envelope.allowedPaths) {\n\t\t// If envelope has allowedPaths, we must check them\n\t\tfor (const targetPath of paths) {\n\t\t\tconst scopedTargetPath = path.isAbsolute(targetPath) ? targetPath : path.resolve(input.cwd, targetPath);\n\t\t\tlet isInsideAny = false;\n\t\t\tlet isDenied = false;\n\t\t\tlet denyRule = \"\";\n\n\t\t\tfor (const allowedRoot of envelope.allowedPaths) {\n\t\t\t\tconst decision = checkPathScope(\n\t\t\t\t\t{\n\t\t\t\t\t\troot: allowedRoot,\n\t\t\t\t\t\tallowedPaths: envelope.allowedPaths,\n\t\t\t\t\t\tdeniedPaths: envelope.deniedPaths,\n\t\t\t\t\t},\n\t\t\t\t\tscopedTargetPath,\n\t\t\t\t);\n\n\t\t\t\tif (decision.kind === \"denied\") {\n\t\t\t\t\tisDenied = true;\n\t\t\t\t\tdenyRule = decision.matchedRule || \"\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (decision.kind === \"inside\") {\n\t\t\t\t\tisInsideAny = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isDenied) {\n\t\t\t\treturn {\n\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\treasonCode: \"path_denied\",\n\t\t\t\t\tmessage: `Path '${targetPath}' is explicitly denied by rule '${denyRule}'.`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!isInsideAny) {\n\t\t\t\t// Block only if the tool is mutating. Wait, read path outside allowed root -> block.\n\t\t\t\t// \"read path inside allowed root -> allow. write/edit path outside allowed root -> block. denied path inside allowed root -> block.\"\n\t\t\t\treturn {\n\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\treasonCode: \"path_outside_allowed_roots\",\n\t\t\t\t\tmessage: `Path '${targetPath}' is outside all allowed roots.`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2.5. Capability checks\n\tif (!hasCapabilityPolicyForTool(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"unknown_tool_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' has no capability policy in the active envelope.`,\n\t\t};\n\t}\n\n\tconst requiredCaps = requiredCapabilitiesForTool(input.toolName, input.args);\n\tif (!hasRequiredCapabilityForTool(envelope.capabilities, input.toolName, input.args)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"missing_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' requires capability '${requiredCaps.join(\" or \")}', which is missing from the active envelope.`,\n\t\t};\n\t}\n\tlet command = \"\";\n\tif (\n\t\tinput.toolName === \"bash\" ||\n\t\tinput.toolName === \"powershell\" ||\n\t\tinput.toolName === \"shell\" ||\n\t\tinput.toolName === \"python\"\n\t) {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.command === \"string\") command = argsObj.command;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.code === \"string\") command = argsObj.code;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.scriptPath === \"string\") {\n\t\t\tcommand = `python ${argsObj.scriptPath}`;\n\t\t}\n\t} else if (input.toolName === \"secret_store\") {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.action === \"string\") command = argsObj.action;\n\t}\n\n\tconst riskResult = assessOperationRisk({\n\t\toperation: `Tool ${input.toolName}`,\n\t\ttoolName: input.toolName,\n\t\tcommand,\n\t\tpaths,\n\t\tcapabilities: envelope.capabilities,\n\t});\n\n\tif (riskResult.requiresApproval) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\", // or block, prompt says: ask-user/block\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `Operation requires approval: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\tif (riskResult.risk === \"high-impact\") {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `High-impact operation requires review: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\treturn {\n\t\toutcome: \"allow\",\n\t\tgate: \"tool_gate\",\n\t\treasonCode: \"allowed_by_envelope\",\n\t\tmessage: \"Operation allowed by current capability envelope.\",\n\t};\n}\n"]}
1
+ {"version":3,"file":"gates.js","sourceRoot":"","sources":["../../../src/core/autonomy/gates.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport {\n\tresolveToolCallCapabilities,\n\tresolveToolCallPathAccess,\n\ttoolUsesPathScope,\n} from \"../tool-capability-policy.ts\";\nimport { describeCapabilityRequirementForTool, hasCapabilityPolicyForTool } from \"./approval-gate.ts\";\nimport type { CapabilityEnvelope, GateOutcome, GateOutcomeKind } from \"./contracts.ts\";\nimport { extractToolPathArguments, isPathWithinEnvelope } from \"./envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"./path-scope.ts\";\nimport { assessOperationRisk } from \"./risk-assessment.ts\";\n\nfunction isGateOutcomeKind(value: unknown): value is GateOutcomeKind {\n\treturn (\n\t\tvalue === \"allow\" || value === \"downgrade\" || value === \"escalate\" || value === \"ask-user\" || value === \"block\"\n\t);\n}\n\nfunction getPrecedence(kind: unknown): number {\n\tif (kind === \"allow\") return 0;\n\tif (kind === \"downgrade\") return 1;\n\tif (kind === \"escalate\") return 2;\n\tif (kind === \"ask-user\") return 3;\n\tif (kind === \"block\") return 4;\n\treturn 4; // Malformed/unknown outcome kind defaults to most restrictive (block)\n}\n\nexport function combineGateOutcomes(outcomes: readonly GateOutcome[]): GateOutcome {\n\tif (outcomes.length === 0) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"gate-combiner\",\n\t\t\treasonCode: \"no_gate_outcomes\",\n\t\t\tmessage: \"No gate outcomes to combine\",\n\t\t};\n\t}\n\n\tlet winner = outcomes[0];\n\tlet maxPrecedence = getPrecedence(winner.outcome);\n\n\tfor (let i = 1; i < outcomes.length; i++) {\n\t\tconst current = outcomes[i];\n\t\tconst currentPrecedence = getPrecedence(current.outcome);\n\t\tif (currentPrecedence > maxPrecedence) {\n\t\t\twinner = current;\n\t\t\tmaxPrecedence = currentPrecedence;\n\t\t}\n\t}\n\n\tif (!isGateOutcomeKind(winner.outcome)) {\n\t\treturn {\n\t\t\t...winner,\n\t\t\toutcome: \"block\",\n\t\t\tmessage: winner.message || \"Malformed outcome kind coerced to block\",\n\t\t};\n\t}\n\n\treturn winner;\n}\n\nexport function fallbackGateOutcome(args: { gate: string; reversible: boolean; reasonCode: string }): GateOutcome {\n\tconst gate = (args.gate || \"\").trim() || \"unknown_gate\";\n\tconst reasonCode = (args.reasonCode || \"\").trim() || \"unknown_reason\";\n\tconst outcome: GateOutcomeKind = args.reversible ? \"ask-user\" : \"block\";\n\n\treturn {\n\t\toutcome,\n\t\tgate,\n\t\treasonCode,\n\t\tmessage: `Fallback gate outcome: ${outcome} for gate ${gate} (${reasonCode})`,\n\t};\n}\n\nexport function extractCandidatePaths(toolName: string, args: unknown): string[] {\n\treturn toolUsesPathScope(toolName) ? extractToolPathArguments(toolName, args) : [];\n}\n\nexport function evaluateToolGate(input: {\n\ttoolName: string;\n\targs?: unknown;\n\tcwd: string;\n\tenvelope?: CapabilityEnvelope;\n}): GateOutcome {\n\tif (!input.envelope) {\n\t\treturn {\n\t\t\toutcome: \"allow\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"no_envelope\",\n\t\t\tmessage: \"No envelope active, preserving existing session behavior.\",\n\t\t};\n\t}\n\n\tconst envelope = input.envelope;\n\n\t// 1. Tool allow/deny list overrides\n\tif (envelope.deniedTools?.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_denied\",\n\t\t\tmessage: `Tool '${input.toolName}' is explicitly denied.`,\n\t\t};\n\t}\n\n\tif (envelope.allowedTools && !envelope.allowedTools.includes(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"tool_not_allowed\",\n\t\t\tmessage: `Tool '${input.toolName}' is not in the allowed tools list.`,\n\t\t};\n\t}\n\n\t// 2. Capability checks. Resolve the selected alternatives once so action-sensitive path\n\t// enforcement cannot disagree with capability admission.\n\tif (!hasCapabilityPolicyForTool(input.toolName)) {\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"unknown_tool_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' has no capability policy in the active envelope.`,\n\t\t};\n\t}\n\n\tconst callCapabilities = resolveToolCallCapabilities(envelope.capabilities, input.toolName, input.args);\n\tif (!callCapabilities) {\n\t\tconst requirement = describeCapabilityRequirementForTool(input.toolName, input.args);\n\t\treturn {\n\t\t\toutcome: \"block\",\n\t\t\tgate: \"tool_gate\",\n\t\t\treasonCode: \"missing_capability\",\n\t\t\tmessage: `Tool '${input.toolName}' requires ${requirement || \"a classified capability\"}, which is missing from the active envelope.`,\n\t\t};\n\t}\n\n\t// 3. Path scope containment for caller-controlled paths. A workflow.plan pipeline read is a\n\t// fixed-root control-plane operation; selecting filesystem.read instead keeps the path gate.\n\tconst pathAccess = resolveToolCallPathAccess(envelope.capabilities, input.toolName, input.args);\n\tconst paths = pathAccess === \"none\" ? [] : extractCandidatePaths(input.toolName, input.args);\n\tif (paths.length > 0) {\n\t\tfor (const targetPath of paths) {\n\t\t\tif (!isPathWithinEnvelope(envelope, targetPath, input.cwd)) {\n\t\t\t\tlet isDenied = false;\n\t\t\t\ttry {\n\t\t\t\t\tconst target = safeRealpathSync(path.resolve(input.cwd, targetPath));\n\t\t\t\t\tisDenied = (envelope.deniedPaths ?? []).some((denied) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn isPathWithinScope(target, safeRealpathSync(path.resolve(input.cwd, denied)));\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} catch {}\n\n\t\t\t\tif (isDenied) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\t\treasonCode: \"path_denied\",\n\t\t\t\t\t\tmessage: `Path '${targetPath}' is explicitly denied.`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\toutcome: \"block\",\n\t\t\t\t\tgate: \"path_scope\",\n\t\t\t\t\treasonCode: \"path_outside_allowed_roots\",\n\t\t\t\t\tmessage: `Path '${targetPath}' is outside all allowed roots.`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\tlet command = \"\";\n\tif (\n\t\tinput.toolName === \"bash\" ||\n\t\tinput.toolName === \"powershell\" ||\n\t\tinput.toolName === \"shell\" ||\n\t\tinput.toolName === \"python\"\n\t) {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.command === \"string\") command = argsObj.command;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.code === \"string\") command = argsObj.code;\n\t\telse if (input.toolName === \"python\" && argsObj && typeof argsObj.scriptPath === \"string\") {\n\t\t\tcommand = `python ${argsObj.scriptPath}`;\n\t\t}\n\t} else if (input.toolName === \"secret_store\") {\n\t\tconst argsObj = input.args as Record<string, unknown>;\n\t\tif (argsObj && typeof argsObj.action === \"string\") command = argsObj.action;\n\t}\n\n\tconst riskResult = assessOperationRisk({\n\t\toperation: `Tool ${input.toolName}`,\n\t\ttoolName: input.toolName,\n\t\tcommand,\n\t\tpaths,\n\t\tcapabilities: envelope.capabilities,\n\t});\n\n\tif (riskResult.requiresApproval) {\n\t\treturn {\n\t\t\toutcome: \"ask-user\", // or block, prompt says: ask-user/block\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `Operation requires approval: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\tif (riskResult.risk === \"high-impact\") {\n\t\treturn {\n\t\t\toutcome: \"ask-user\",\n\t\t\tgate: \"risk_assessment\",\n\t\t\treasonCode: riskResult.reasonCode,\n\t\t\tmessage: `High-impact operation requires review: ${riskResult.reasons.join(\", \")}`,\n\t\t};\n\t}\n\n\treturn {\n\t\toutcome: \"allow\",\n\t\tgate: \"tool_gate\",\n\t\treasonCode: \"allowed_by_envelope\",\n\t\tmessage: \"Operation allowed by current capability envelope.\",\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"path-scope.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/path-scope.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAOnE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAyDD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAShF;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAElF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAqGtF"}
1
+ {"version":3,"file":"path-scope.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/path-scope.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAOnE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAyDD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAShF;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAElF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAmGtF"}
@@ -92,27 +92,27 @@ export function checkPathScope(scope, targetPath) {
92
92
  reasonCode: "empty_path",
93
93
  };
94
94
  }
95
- let resolvedTarget;
95
+ let resolvedRoot;
96
96
  try {
97
- resolvedTarget = safeRealpathSync(targetPath);
97
+ resolvedRoot = safeRealpathSync(scope.root);
98
98
  }
99
99
  catch {
100
- // If we can't resolve the path at all (e.g. permission error), fail safely
101
100
  return {
102
101
  kind: "outside",
103
102
  path: targetPath,
104
- reasonCode: "unresolvable_target",
103
+ reasonCode: "unresolvable_root",
105
104
  };
106
105
  }
107
- let resolvedRoot;
106
+ let resolvedTarget;
108
107
  try {
109
- resolvedRoot = safeRealpathSync(scope.root);
108
+ resolvedTarget = safeRealpathSync(targetPath);
110
109
  }
111
110
  catch {
111
+ // If we can't resolve the path at all (e.g. permission error), fail safely
112
112
  return {
113
113
  kind: "outside",
114
114
  path: targetPath,
115
- reasonCode: "unresolvable_root",
115
+ reasonCode: "unresolvable_target",
116
116
  };
117
117
  }
118
118
  // First, it must be inside the main root
@@ -1 +1 @@
1
- {"version":3,"file":"path-scope.js","sourceRoot":"","sources":["../../../src/core/autonomy/path-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,+FAA+F;AAC/F,8FAA8F;AAC9F,wBAAwB;AACxB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IAClD,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,aAAa,CAAC,YAAoB,EAAE,IAAY;IACxD,IAAI,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8BAA8B,gBAAgB,4BAA4B,YAAY,GAAG,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IAED,4FAA4F;IAC5F,0FAA0F;IAC1F,6FAA6F;IAC7F,4FAA4F;IAC5F,8FAA8F;IAC9F,2FAA2F;IAC3F,uFAAuF;IACvF,IAAI,KAAkD,CAAC;IACvD,IAAI,CAAC;QACJ,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,KAAK,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,EAAE,cAAc,EAAE,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAC3E,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YACjD,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;YAC1B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC5C,OAAO,aAAa,CAAC,cAAc,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,uEAAuE;IACvE,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACrC,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,cAAc,CAAC,GAAG,KAAwB;IAClD,OAAO,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;AAC9D,CAAC;AAED,SAAS,iCAAiC,CAAC,KAAa,EAAE,OAA8C;IACvG,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACvD,OAAO,iCAAiC,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;IACtE,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,iCAAiC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,iCAAiC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpD,OAAO,CACN,YAAY,KAAK,EAAE;QACnB,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAC5G,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,WAAmB;IACxE,OAAO,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,iBAAiB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAgB,EAAE,UAAkB;IAClE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,YAAY;SACxB,CAAC;IACH,CAAC;IAED,IAAI,cAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,cAAc,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACR,2EAA2E;QAC3E,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,qBAAqB;SACjC,CAAC;IACH,CAAC;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,mBAAmB;SAC/B,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,YAAY,CAAC,EAAE,CAAC;QACtD,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,cAAc;YAC5B,UAAU,EAAE,cAAc;SAC1B,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,CAAC;gBACJ,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,iBAAiB,CAAC,cAAc,EAAE,cAAc,CAAC,EAAE,CAAC;oBACvD,OAAO;wBACN,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,UAAU;wBAChB,YAAY,EAAE,cAAc;wBAC5B,WAAW,EAAE,MAAM;wBACnB,UAAU,EAAE,qBAAqB;qBACjC,CAAC;gBACH,CAAC;YACF,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACX,CAAC;IACF,CAAC;IAED,qBAAqB;IACrB,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,cAAkC,CAAC;QAEvC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACJ,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAClD,IAAI,iBAAiB,CAAC,cAAc,EAAE,eAAe,CAAC,EAAE,CAAC;oBACxD,SAAS,GAAG,IAAI,CAAC;oBACjB,cAAc,GAAG,OAAO,CAAC;oBACzB,MAAM;gBACP,CAAC;YACF,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACX,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,OAAO;gBACN,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,UAAU;gBAChB,YAAY,EAAE,cAAc;gBAC5B,UAAU,EAAE,uBAAuB;aACnC,CAAC;QACH,CAAC;QAED,OAAO;YACN,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,cAAc;YAC5B,WAAW,EAAE,cAAc;YAC3B,UAAU,EAAE,sBAAsB;SAClC,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,cAAc;QAC5B,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,UAAU,EAAE,aAAa;KACzB,CAAC;AACH,CAAC","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { PathScope, PathScopeDecision } from \"./contracts.ts\";\n\n// Bounds the symlink-following recursion in resolveSafely: a chain of dangling links is finite\n// in practice, and a genuine symlink cycle (a -> b -> a) must fail closed with a throw rather\n// than recurse forever.\nconst MAX_SYMLINK_HOPS = 32;\n\n/**\n * Resolves the real path of a target. If the target does not exist, it recursively\n * resolves the deepest existing parent directory and then appends the non-existent\n * remainder, ensuring that any symlinks in the existing path prefix are expanded.\n */\nexport function safeRealpathSync(targetPath: string): string {\n\treturn resolveSafely(path.resolve(targetPath), 0);\n}\n\nfunction resolveSafely(absolutePath: string, hops: number): string {\n\tif (hops > MAX_SYMLINK_HOPS) {\n\t\tthrow new Error(`safeRealpathSync: exceeded ${MAX_SYMLINK_HOPS} symlink hops resolving \"${absolutePath}\"`);\n\t}\n\n\tif (fs.existsSync(absolutePath)) {\n\t\treturn fs.realpathSync(absolutePath);\n\t}\n\n\t// existsSync FOLLOWS symlinks, so it reports false for a DANGLING link (the link exists but\n\t// its target does not) — the same as a path that plain doesn't exist at all. Without this\n\t// lstat check, a dangling leaf would fall straight into the lexical parent-join below, never\n\t// dereferencing the link itself. That would let a dangling symlink placed inside an allowed\n\t// root smuggle writes to wherever it points, since the write itself (e.g. writeFileSync) DOES\n\t// follow the link. lstatSync throws ENOENT for a path that truly doesn't exist anywhere in\n\t// the chain — that case falls through to the parent-recursion branch below, unchanged.\n\tlet lstat: ReturnType<typeof fs.lstatSync> | undefined;\n\ttry {\n\t\tlstat = fs.lstatSync(absolutePath);\n\t} catch {\n\t\tlstat = undefined;\n\t}\n\n\tif (lstat?.isSymbolicLink()) {\n\t\tconst linkTarget = fs.readlinkSync(absolutePath);\n\t\tconst resolvedParent = resolveSafely(path.dirname(absolutePath), hops + 1);\n\t\tconst resolvedTarget = path.isAbsolute(linkTarget)\n\t\t\t? path.resolve(linkTarget)\n\t\t\t: path.resolve(resolvedParent, linkTarget);\n\t\treturn resolveSafely(resolvedTarget, hops + 1);\n\t}\n\n\tconst parent = path.dirname(absolutePath);\n\t// Base case: if parent is the same as absolutePath, we've hit the root\n\tif (parent === absolutePath) {\n\t\treturn absolutePath;\n\t}\n\n\tconst resolvedParent = resolveSafely(parent, hops + 1);\n\treturn path.join(resolvedParent, path.basename(absolutePath));\n}\n\nfunction usesWindowsPath(value: string): boolean {\n\treturn /^[a-zA-Z]:([\\\\/]|$)/.test(value) || value.startsWith(\"\\\\\\\\\") || value.startsWith(\"//\");\n}\n\nfunction lexicalPathApi(...paths: readonly string[]): typeof path.posix | typeof path.win32 {\n\treturn paths.some(usesWindowsPath) ? path.win32 : path.posix;\n}\n\nfunction canonicalPathScopeIdentityWithApi(value: string, pathApi: typeof path.posix | typeof path.win32): string {\n\tconst resolved = pathApi.resolve(value);\n\treturn pathApi === path.win32 ? resolved.toLowerCase() : resolved;\n}\n\n/**\n * Stable lexical scope identity for durable planners. Windows drive and UNC paths are normalized\n * case-insensitively even when a WSL process is only comparing them and cannot realpath them.\n */\nexport function canonicalPathScopeIdentity(value: string): string {\n\treturn canonicalPathScopeIdentityWithApi(value, lexicalPathApi(value));\n}\n\n/**\n * Boundary-safe lexical containment for already-resolved paths and portable scope planning.\n * Windows drive and UNC paths compare case-insensitively; native POSIX paths remain case-sensitive.\n * This deliberately does not resolve symlinks — callers that authorize filesystem access must first\n * use {@link safeRealpathSync}.\n */\nexport function isPathWithinScope(targetPath: string, scopeRoot: string): boolean {\n\tconst pathApi = lexicalPathApi(targetPath, scopeRoot);\n\tconst target = canonicalPathScopeIdentityWithApi(targetPath, pathApi);\n\tconst root = canonicalPathScopeIdentityWithApi(scopeRoot, pathApi);\n\tconst relativePath = pathApi.relative(root, target);\n\treturn (\n\t\trelativePath === \"\" ||\n\t\t(!relativePath.startsWith(`..${pathApi.sep}`) && relativePath !== \"..\" && !pathApi.isAbsolute(relativePath))\n\t);\n}\n\n/** True when either scope contains the other, including an exact match. */\nexport function pathScopesOverlap(firstScope: string, secondScope: string): boolean {\n\treturn isPathWithinScope(firstScope, secondScope) || isPathWithinScope(secondScope, firstScope);\n}\n\nexport function checkPathScope(scope: PathScope, targetPath: string): PathScopeDecision {\n\tif (!targetPath) {\n\t\treturn {\n\t\t\tkind: \"missing\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"empty_path\",\n\t\t};\n\t}\n\n\tlet resolvedTarget: string;\n\ttry {\n\t\tresolvedTarget = safeRealpathSync(targetPath);\n\t} catch {\n\t\t// If we can't resolve the path at all (e.g. permission error), fail safely\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"unresolvable_target\",\n\t\t};\n\t}\n\n\tlet resolvedRoot: string;\n\ttry {\n\t\tresolvedRoot = safeRealpathSync(scope.root);\n\t} catch {\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"unresolvable_root\",\n\t\t};\n\t}\n\n\t// First, it must be inside the main root\n\tif (!isPathWithinScope(resolvedTarget, resolvedRoot)) {\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\tresolvedPath: resolvedTarget,\n\t\t\treasonCode: \"outside_root\",\n\t\t};\n\t}\n\n\t// Check deniedPaths (overrides allowedPaths)\n\tif (scope.deniedPaths && scope.deniedPaths.length > 0) {\n\t\tfor (const denied of scope.deniedPaths) {\n\t\t\ttry {\n\t\t\t\tconst resolvedDenied = safeRealpathSync(denied);\n\t\t\t\tif (isPathWithinScope(resolvedTarget, resolvedDenied)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: \"denied\",\n\t\t\t\t\t\tpath: targetPath,\n\t\t\t\t\t\tresolvedPath: resolvedTarget,\n\t\t\t\t\t\tmatchedRule: denied,\n\t\t\t\t\t\treasonCode: \"matches_denied_path\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t// Check allowedPaths\n\tif (scope.allowedPaths && scope.allowedPaths.length > 0) {\n\t\tlet isAllowed = false;\n\t\tlet matchedAllowed: string | undefined;\n\n\t\tfor (const allowed of scope.allowedPaths) {\n\t\t\ttry {\n\t\t\t\tconst resolvedAllowed = safeRealpathSync(allowed);\n\t\t\t\tif (isPathWithinScope(resolvedTarget, resolvedAllowed)) {\n\t\t\t\t\tisAllowed = true;\n\t\t\t\t\tmatchedAllowed = allowed;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (!isAllowed) {\n\t\t\treturn {\n\t\t\t\tkind: \"outside\",\n\t\t\t\tpath: targetPath,\n\t\t\t\tresolvedPath: resolvedTarget,\n\t\t\t\treasonCode: \"outside_allowed_paths\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"inside\",\n\t\t\tpath: targetPath,\n\t\t\tresolvedPath: resolvedTarget,\n\t\t\tmatchedRule: matchedAllowed,\n\t\t\treasonCode: \"inside_allowed_paths\",\n\t\t};\n\t}\n\n\treturn {\n\t\tkind: \"inside\",\n\t\tpath: targetPath,\n\t\tresolvedPath: resolvedTarget,\n\t\tmatchedRule: scope.root,\n\t\treasonCode: \"inside_root\",\n\t};\n}\n"]}
1
+ {"version":3,"file":"path-scope.js","sourceRoot":"","sources":["../../../src/core/autonomy/path-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,+FAA+F;AAC/F,8FAA8F;AAC9F,wBAAwB;AACxB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IAClD,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,aAAa,CAAC,YAAoB,EAAE,IAAY;IACxD,IAAI,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8BAA8B,gBAAgB,4BAA4B,YAAY,GAAG,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC;IAED,4FAA4F;IAC5F,0FAA0F;IAC1F,6FAA6F;IAC7F,4FAA4F;IAC5F,8FAA8F;IAC9F,2FAA2F;IAC3F,uFAAuF;IACvF,IAAI,KAAkD,CAAC;IACvD,IAAI,CAAC;QACJ,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,KAAK,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,EAAE,cAAc,EAAE,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QAC3E,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YACjD,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;YAC1B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC5C,OAAO,aAAa,CAAC,cAAc,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,uEAAuE;IACvE,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACrC,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,cAAc,CAAC,GAAG,KAAwB;IAClD,OAAO,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;AAC9D,CAAC;AAED,SAAS,iCAAiC,CAAC,KAAa,EAAE,OAA8C;IACvG,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACvD,OAAO,iCAAiC,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;IACtE,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,iCAAiC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,iCAAiC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpD,OAAO,CACN,YAAY,KAAK,EAAE;QACnB,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAC5G,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,WAAmB;IACxE,OAAO,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,iBAAiB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAgB,EAAE,UAAkB;IAClE,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,YAAY;SACxB,CAAC;IACH,CAAC;IACD,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACJ,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,mBAAmB;SAC/B,CAAC;IACH,CAAC;IACD,IAAI,cAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,cAAc,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACR,2EAA2E;QAC3E,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,qBAAqB;SACjC,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,YAAY,CAAC,EAAE,CAAC;QACtD,OAAO;YACN,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,cAAc;YAC5B,UAAU,EAAE,cAAc;SAC1B,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,CAAC;gBACJ,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,iBAAiB,CAAC,cAAc,EAAE,cAAc,CAAC,EAAE,CAAC;oBACvD,OAAO;wBACN,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,UAAU;wBAChB,YAAY,EAAE,cAAc;wBAC5B,WAAW,EAAE,MAAM;wBACnB,UAAU,EAAE,qBAAqB;qBACjC,CAAC;gBACH,CAAC;YACF,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACX,CAAC;IACF,CAAC;IAED,qBAAqB;IACrB,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,cAAkC,CAAC;QAEvC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACJ,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAClD,IAAI,iBAAiB,CAAC,cAAc,EAAE,eAAe,CAAC,EAAE,CAAC;oBACxD,SAAS,GAAG,IAAI,CAAC;oBACjB,cAAc,GAAG,OAAO,CAAC;oBACzB,MAAM;gBACP,CAAC;YACF,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACX,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,OAAO;gBACN,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,UAAU;gBAChB,YAAY,EAAE,cAAc;gBAC5B,UAAU,EAAE,uBAAuB;aACnC,CAAC;QACH,CAAC;QAED,OAAO;YACN,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,cAAc;YAC5B,WAAW,EAAE,cAAc;YAC3B,UAAU,EAAE,sBAAsB;SAClC,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,cAAc;QAC5B,WAAW,EAAE,KAAK,CAAC,IAAI;QACvB,UAAU,EAAE,aAAa;KACzB,CAAC;AACH,CAAC","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { PathScope, PathScopeDecision } from \"./contracts.ts\";\n\n// Bounds the symlink-following recursion in resolveSafely: a chain of dangling links is finite\n// in practice, and a genuine symlink cycle (a -> b -> a) must fail closed with a throw rather\n// than recurse forever.\nconst MAX_SYMLINK_HOPS = 32;\n\n/**\n * Resolves the real path of a target. If the target does not exist, it recursively\n * resolves the deepest existing parent directory and then appends the non-existent\n * remainder, ensuring that any symlinks in the existing path prefix are expanded.\n */\nexport function safeRealpathSync(targetPath: string): string {\n\treturn resolveSafely(path.resolve(targetPath), 0);\n}\n\nfunction resolveSafely(absolutePath: string, hops: number): string {\n\tif (hops > MAX_SYMLINK_HOPS) {\n\t\tthrow new Error(`safeRealpathSync: exceeded ${MAX_SYMLINK_HOPS} symlink hops resolving \"${absolutePath}\"`);\n\t}\n\n\tif (fs.existsSync(absolutePath)) {\n\t\treturn fs.realpathSync(absolutePath);\n\t}\n\n\t// existsSync FOLLOWS symlinks, so it reports false for a DANGLING link (the link exists but\n\t// its target does not) — the same as a path that plain doesn't exist at all. Without this\n\t// lstat check, a dangling leaf would fall straight into the lexical parent-join below, never\n\t// dereferencing the link itself. That would let a dangling symlink placed inside an allowed\n\t// root smuggle writes to wherever it points, since the write itself (e.g. writeFileSync) DOES\n\t// follow the link. lstatSync throws ENOENT for a path that truly doesn't exist anywhere in\n\t// the chain — that case falls through to the parent-recursion branch below, unchanged.\n\tlet lstat: ReturnType<typeof fs.lstatSync> | undefined;\n\ttry {\n\t\tlstat = fs.lstatSync(absolutePath);\n\t} catch {\n\t\tlstat = undefined;\n\t}\n\n\tif (lstat?.isSymbolicLink()) {\n\t\tconst linkTarget = fs.readlinkSync(absolutePath);\n\t\tconst resolvedParent = resolveSafely(path.dirname(absolutePath), hops + 1);\n\t\tconst resolvedTarget = path.isAbsolute(linkTarget)\n\t\t\t? path.resolve(linkTarget)\n\t\t\t: path.resolve(resolvedParent, linkTarget);\n\t\treturn resolveSafely(resolvedTarget, hops + 1);\n\t}\n\n\tconst parent = path.dirname(absolutePath);\n\t// Base case: if parent is the same as absolutePath, we've hit the root\n\tif (parent === absolutePath) {\n\t\treturn absolutePath;\n\t}\n\n\tconst resolvedParent = resolveSafely(parent, hops + 1);\n\treturn path.join(resolvedParent, path.basename(absolutePath));\n}\n\nfunction usesWindowsPath(value: string): boolean {\n\treturn /^[a-zA-Z]:([\\\\/]|$)/.test(value) || value.startsWith(\"\\\\\\\\\") || value.startsWith(\"//\");\n}\n\nfunction lexicalPathApi(...paths: readonly string[]): typeof path.posix | typeof path.win32 {\n\treturn paths.some(usesWindowsPath) ? path.win32 : path.posix;\n}\n\nfunction canonicalPathScopeIdentityWithApi(value: string, pathApi: typeof path.posix | typeof path.win32): string {\n\tconst resolved = pathApi.resolve(value);\n\treturn pathApi === path.win32 ? resolved.toLowerCase() : resolved;\n}\n\n/**\n * Stable lexical scope identity for durable planners. Windows drive and UNC paths are normalized\n * case-insensitively even when a WSL process is only comparing them and cannot realpath them.\n */\nexport function canonicalPathScopeIdentity(value: string): string {\n\treturn canonicalPathScopeIdentityWithApi(value, lexicalPathApi(value));\n}\n\n/**\n * Boundary-safe lexical containment for already-resolved paths and portable scope planning.\n * Windows drive and UNC paths compare case-insensitively; native POSIX paths remain case-sensitive.\n * This deliberately does not resolve symlinks — callers that authorize filesystem access must first\n * use {@link safeRealpathSync}.\n */\nexport function isPathWithinScope(targetPath: string, scopeRoot: string): boolean {\n\tconst pathApi = lexicalPathApi(targetPath, scopeRoot);\n\tconst target = canonicalPathScopeIdentityWithApi(targetPath, pathApi);\n\tconst root = canonicalPathScopeIdentityWithApi(scopeRoot, pathApi);\n\tconst relativePath = pathApi.relative(root, target);\n\treturn (\n\t\trelativePath === \"\" ||\n\t\t(!relativePath.startsWith(`..${pathApi.sep}`) && relativePath !== \"..\" && !pathApi.isAbsolute(relativePath))\n\t);\n}\n\n/** True when either scope contains the other, including an exact match. */\nexport function pathScopesOverlap(firstScope: string, secondScope: string): boolean {\n\treturn isPathWithinScope(firstScope, secondScope) || isPathWithinScope(secondScope, firstScope);\n}\n\nexport function checkPathScope(scope: PathScope, targetPath: string): PathScopeDecision {\n\tif (!targetPath) {\n\t\treturn {\n\t\t\tkind: \"missing\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"empty_path\",\n\t\t};\n\t}\n\tlet resolvedRoot: string;\n\ttry {\n\t\tresolvedRoot = safeRealpathSync(scope.root);\n\t} catch {\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"unresolvable_root\",\n\t\t};\n\t}\n\tlet resolvedTarget: string;\n\ttry {\n\t\tresolvedTarget = safeRealpathSync(targetPath);\n\t} catch {\n\t\t// If we can't resolve the path at all (e.g. permission error), fail safely\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\treasonCode: \"unresolvable_target\",\n\t\t};\n\t}\n\n\t// First, it must be inside the main root\n\tif (!isPathWithinScope(resolvedTarget, resolvedRoot)) {\n\t\treturn {\n\t\t\tkind: \"outside\",\n\t\t\tpath: targetPath,\n\t\t\tresolvedPath: resolvedTarget,\n\t\t\treasonCode: \"outside_root\",\n\t\t};\n\t}\n\n\t// Check deniedPaths (overrides allowedPaths)\n\tif (scope.deniedPaths && scope.deniedPaths.length > 0) {\n\t\tfor (const denied of scope.deniedPaths) {\n\t\t\ttry {\n\t\t\t\tconst resolvedDenied = safeRealpathSync(denied);\n\t\t\t\tif (isPathWithinScope(resolvedTarget, resolvedDenied)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: \"denied\",\n\t\t\t\t\t\tpath: targetPath,\n\t\t\t\t\t\tresolvedPath: resolvedTarget,\n\t\t\t\t\t\tmatchedRule: denied,\n\t\t\t\t\t\treasonCode: \"matches_denied_path\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t// Check allowedPaths\n\tif (scope.allowedPaths && scope.allowedPaths.length > 0) {\n\t\tlet isAllowed = false;\n\t\tlet matchedAllowed: string | undefined;\n\n\t\tfor (const allowed of scope.allowedPaths) {\n\t\t\ttry {\n\t\t\t\tconst resolvedAllowed = safeRealpathSync(allowed);\n\t\t\t\tif (isPathWithinScope(resolvedTarget, resolvedAllowed)) {\n\t\t\t\t\tisAllowed = true;\n\t\t\t\t\tmatchedAllowed = allowed;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\tif (!isAllowed) {\n\t\t\treturn {\n\t\t\t\tkind: \"outside\",\n\t\t\t\tpath: targetPath,\n\t\t\t\tresolvedPath: resolvedTarget,\n\t\t\t\treasonCode: \"outside_allowed_paths\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"inside\",\n\t\t\tpath: targetPath,\n\t\t\tresolvedPath: resolvedTarget,\n\t\t\tmatchedRule: matchedAllowed,\n\t\t\treasonCode: \"inside_allowed_paths\",\n\t\t};\n\t}\n\n\treturn {\n\t\tkind: \"inside\",\n\t\tpath: targetPath,\n\t\tresolvedPath: resolvedTarget,\n\t\tmatchedRule: scope.root,\n\t\treasonCode: \"inside_root\",\n\t};\n}\n"]}