@raishin/vanguard-frontier-agentic 3.15.0 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +3 -1
  3. package/.cursor-plugin/plugin.json +3 -1
  4. package/.github/plugin/marketplace.json +1 -1
  5. package/README.md +11 -11
  6. package/agents/AGENTS.md +1 -1
  7. package/agents/README.md +1 -1
  8. package/agents/cross-functional/agentic-security-review-agent/AGENT.md +168 -0
  9. package/agents/cross-functional/agentic-security-review-agent/harnesses/claude-code.agent.md +158 -0
  10. package/agents/cross-functional/agentic-security-review-agent/harnesses/codex.toml +165 -0
  11. package/agents/cross-functional/agentic-security-review-agent/harnesses/copilot.agent.md +163 -0
  12. package/agents/cross-functional/agentic-security-review-agent/harnesses/cursor.agent.md +158 -0
  13. package/agents/cross-functional/agentic-security-review-agent/harnesses/gemini.agent.md +164 -0
  14. package/agents/cross-functional/agentic-security-review-agent/harnesses/kiro-cli.agent.json +5 -0
  15. package/agents/cross-functional/agentic-security-review-agent/harnesses/kiro-ide.agent.md +157 -0
  16. package/agents/cross-functional/agentic-security-review-agent/metadata.json +45 -0
  17. package/agents/cross-functional/agentic-system-architecture-review-agent/AGENT.md +157 -0
  18. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/claude-code.agent.md +147 -0
  19. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/codex.toml +154 -0
  20. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/copilot.agent.md +152 -0
  21. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/cursor.agent.md +147 -0
  22. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/gemini.agent.md +153 -0
  23. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/kiro-cli.agent.json +5 -0
  24. package/agents/cross-functional/agentic-system-architecture-review-agent/harnesses/kiro-ide.agent.md +146 -0
  25. package/agents/cross-functional/agentic-system-architecture-review-agent/metadata.json +46 -0
  26. package/agents/typescript/typescript-maestro-agent/AGENT.md +1 -1
  27. package/agents/typescript/typescript-maestro-agent/harnesses/claude-code.agent.md +1 -1
  28. package/agents/typescript/typescript-maestro-agent/harnesses/codex.toml +1 -1
  29. package/agents/typescript/typescript-maestro-agent/harnesses/copilot.agent.md +1 -1
  30. package/agents/typescript/typescript-maestro-agent/harnesses/cursor.agent.md +1 -1
  31. package/agents/typescript/typescript-maestro-agent/harnesses/gemini.agent.md +1 -1
  32. package/agents/typescript/typescript-maestro-agent/harnesses/kiro-cli.agent.json +1 -1
  33. package/agents/typescript/typescript-maestro-agent/harnesses/kiro-ide.agent.md +1 -1
  34. package/agents/typescript/typescript-mcp-tool-contract-agent/AGENT.md +1 -1
  35. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/claude-code.agent.md +1 -1
  36. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/copilot.agent.md +1 -1
  37. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/cursor.agent.md +1 -1
  38. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/gemini.agent.md +1 -1
  39. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/kiro-cli.agent.json +1 -1
  40. package/agents/typescript/typescript-mcp-tool-contract-agent/harnesses/kiro-ide.agent.md +1 -1
  41. package/catalog/AGENTS.md +2 -1
  42. package/catalog/agents.json +71 -0
  43. package/catalog/asset-integrity.json +1639 -92
  44. package/catalog/index.json +3 -2
  45. package/catalog/install-roles.json +24 -3
  46. package/catalog/model-assignments.json +66 -0
  47. package/catalog/mods.json +131 -0
  48. package/catalog/skill-manifest.json +134 -8
  49. package/catalog/skills.json +89 -0
  50. package/mods/README.md +220 -0
  51. package/mods/audit-trail/README.md +70 -0
  52. package/mods/audit-trail/bin/hook.mjs +65 -0
  53. package/mods/audit-trail/claude-code/UNSUPPORTED.md +11 -0
  54. package/mods/audit-trail/claude-code/settings.hooks.json +14 -0
  55. package/mods/audit-trail/codex/hooks.json +14 -0
  56. package/mods/audit-trail/kiro/UNSUPPORTED.md +7 -0
  57. package/mods/audit-trail/kiro/audit-trail-post-tool-use-hook.json +14 -0
  58. package/mods/audit-trail/lib/common.mjs +213 -0
  59. package/mods/audit-trail/lib/core.mjs +50 -0
  60. package/mods/audit-trail/lib/runtime.mjs +140 -0
  61. package/mods/audit-trail/mod.toml +37 -0
  62. package/mods/blast-radius-guard/.claude-plugin/plugin.json +5 -0
  63. package/mods/blast-radius-guard/README.md +53 -0
  64. package/mods/blast-radius-guard/bin/hook.mjs +18 -0
  65. package/mods/blast-radius-guard/claude-code/settings.hooks.json +14 -0
  66. package/mods/blast-radius-guard/codex/UNSUPPORTED.md +7 -0
  67. package/mods/blast-radius-guard/codex/hooks.json +14 -0
  68. package/mods/blast-radius-guard/hooks/hooks.json +5 -0
  69. package/mods/blast-radius-guard/hooks/register.test.ts +97 -0
  70. package/mods/blast-radius-guard/hooks/register.ts +13 -0
  71. package/mods/blast-radius-guard/kiro/UNSUPPORTED.md +7 -0
  72. package/mods/blast-radius-guard/kiro/blast-radius-guard-pre-tool-use-hook.json +14 -0
  73. package/mods/blast-radius-guard/lib/common.mjs +213 -0
  74. package/mods/blast-radius-guard/lib/core.mjs +66 -0
  75. package/mods/blast-radius-guard/lib/patterns.mjs +102 -0
  76. package/mods/blast-radius-guard/lib/runtime.mjs +140 -0
  77. package/mods/blast-radius-guard/mod.toml +34 -0
  78. package/mods/blast-radius-guard/patterns.json +101 -0
  79. package/mods/context-burn/.claude-plugin/plugin.json +5 -0
  80. package/mods/context-burn/README.md +66 -0
  81. package/mods/context-burn/bin/hook.mjs +38 -0
  82. package/mods/context-burn/claude-code/UNSUPPORTED.md +7 -0
  83. package/mods/context-burn/claude-code/settings.hooks.json +24 -0
  84. package/mods/context-burn/codex/UNSUPPORTED.md +7 -0
  85. package/mods/context-burn/codex/hooks.json +24 -0
  86. package/mods/context-burn/hooks/hooks.json +5 -0
  87. package/mods/context-burn/hooks/register.test.ts +151 -0
  88. package/mods/context-burn/hooks/register.ts +35 -0
  89. package/mods/context-burn/kiro/UNSUPPORTED.md +7 -0
  90. package/mods/context-burn/kiro/context-burn-post-tool-use-hook.json +14 -0
  91. package/mods/context-burn/kiro/context-burn-session-start-hook.json +14 -0
  92. package/mods/context-burn/lib/common.mjs +213 -0
  93. package/mods/context-burn/lib/core.mjs +79 -0
  94. package/mods/context-burn/lib/runtime.mjs +140 -0
  95. package/mods/context-burn/mod.toml +37 -0
  96. package/mods/live-guard-gate/.claude-plugin/plugin.json +5 -0
  97. package/mods/live-guard-gate/README.md +43 -0
  98. package/mods/live-guard-gate/bin/hook.mjs +12 -0
  99. package/mods/live-guard-gate/claude-code/settings.hooks.json +14 -0
  100. package/mods/live-guard-gate/codex/UNSUPPORTED.md +7 -0
  101. package/mods/live-guard-gate/codex/hooks.json +14 -0
  102. package/mods/live-guard-gate/hooks/hooks.json +5 -0
  103. package/mods/live-guard-gate/hooks/register.test.ts +59 -0
  104. package/mods/live-guard-gate/hooks/register.ts +11 -0
  105. package/mods/live-guard-gate/kiro/UNSUPPORTED.md +7 -0
  106. package/mods/live-guard-gate/kiro/live-guard-gate-pre-tool-use-hook.json +14 -0
  107. package/mods/live-guard-gate/lib/common.mjs +213 -0
  108. package/mods/live-guard-gate/lib/core.mjs +24 -0
  109. package/mods/live-guard-gate/lib/live-agents.mjs +65 -0
  110. package/mods/live-guard-gate/lib/patterns.mjs +523 -0
  111. package/mods/live-guard-gate/lib/runtime.mjs +140 -0
  112. package/mods/live-guard-gate/mod.toml +34 -0
  113. package/mods/live-guard-gate/patterns.json +522 -0
  114. package/mods/secret-redactor/.claude-plugin/plugin.json +5 -0
  115. package/mods/secret-redactor/README.md +86 -0
  116. package/mods/secret-redactor/bin/hook.mjs +35 -0
  117. package/mods/secret-redactor/claude-code/UNSUPPORTED.md +7 -0
  118. package/mods/secret-redactor/claude-code/settings.hooks.json +24 -0
  119. package/mods/secret-redactor/codex/UNSUPPORTED.md +11 -0
  120. package/mods/secret-redactor/codex/hooks.json +14 -0
  121. package/mods/secret-redactor/hooks/hooks.json +5 -0
  122. package/mods/secret-redactor/hooks/register.test.ts +150 -0
  123. package/mods/secret-redactor/hooks/register.ts +19 -0
  124. package/mods/secret-redactor/kiro/UNSUPPORTED.md +11 -0
  125. package/mods/secret-redactor/kiro/secret-redactor-pre-tool-use-hook.json +14 -0
  126. package/mods/secret-redactor/lib/common.mjs +213 -0
  127. package/mods/secret-redactor/lib/core.mjs +61 -0
  128. package/mods/secret-redactor/lib/patterns.mjs +75 -0
  129. package/mods/secret-redactor/lib/runtime.mjs +140 -0
  130. package/mods/secret-redactor/mod.toml +43 -0
  131. package/mods/secret-redactor/patterns.json +74 -0
  132. package/package.json +9 -4
  133. package/plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json +1 -1
  134. package/powers/vanguard-generic/POWER.md +6 -6
  135. package/powers/vanguard-kubernetes/POWER.md +1 -1
  136. package/powers/vanguard-multi-cloud/POWER.md +1 -1
  137. package/schemas/agentic-handoff.schema.json +443 -0
  138. package/scripts/generate-kiro-powers.mjs +17 -6
  139. package/scripts/generate-mods.mjs +784 -0
  140. package/scripts/mods/common.mjs +213 -0
  141. package/scripts/mods/runtime.mjs +140 -0
  142. package/scripts/typescript_data/agents/00-typescript-maestro-agent.json +2 -2
  143. package/scripts/typescript_data/agents/11-typescript-mcp-tool-contract-agent.json +4 -4
  144. package/skills/cross-functional/agentic-board-protocol/SKILL.md +85 -0
  145. package/skills/cross-functional/agentic-board-protocol/metadata.json +28 -0
  146. package/skills/cross-functional/agentic-board-protocol/references/decision-record.md +134 -0
  147. package/skills/cross-functional/agentic-board-protocol/references/handoff-envelope.md +208 -0
  148. package/skills/cross-functional/agentic-board-protocol/references/risk-tier-crosswalk.md +88 -0
  149. package/skills/cross-functional/agentic-board-protocol/references/stop-conditions.md +122 -0
  150. package/skills/cross-functional/agentic-board-protocol/references/value-case.md +159 -0
  151. package/skills/cross-functional/agentic-security-review/SKILL.md +78 -0
  152. package/skills/cross-functional/agentic-security-review/metadata.json +30 -0
  153. package/skills/cross-functional/agentic-security-review/references/identity-and-delegated-authorization.md +144 -0
  154. package/skills/cross-functional/agentic-security-review/references/supply-chain-and-provenance.md +111 -0
  155. package/skills/cross-functional/agentic-security-review/references/threat-model-owasp-asi.md +55 -0
  156. package/skills/cross-functional/agentic-security-review/references/tools-memory-and-data-reach.md +125 -0
  157. package/skills/cross-functional/agentic-security-review/references/workflow-and-output.md +210 -0
  158. package/skills/cross-functional/agentic-system-architecture-review/SKILL.md +74 -0
  159. package/skills/cross-functional/agentic-system-architecture-review/metadata.json +31 -0
  160. package/skills/cross-functional/agentic-system-architecture-review/references/delegation-and-handoff-review.md +107 -0
  161. package/skills/cross-functional/agentic-system-architecture-review/references/pattern-selection.md +81 -0
  162. package/skills/cross-functional/agentic-system-architecture-review/references/reliability-and-containment.md +86 -0
  163. package/skills/cross-functional/agentic-system-architecture-review/references/state-memory-and-durability.md +102 -0
  164. package/skills/cross-functional/agentic-system-architecture-review/references/workflow-and-output.md +135 -0
  165. package/skills/typescript/typescript-maestro/SKILL.md +1 -1
  166. package/skills/typescript/typescript-maestro/references/routing-taxonomy.md +1 -1
  167. package/skills/typescript/typescript-mcp-tool-contract/SKILL.md +1 -1
  168. package/tests/agentic-board-suite/_build_fixtures.py +1751 -0
  169. package/tests/agentic-board-suite/_build_harnesses.py +123 -0
  170. package/tests/agentic-board-suite/agentic_board_rules.py +857 -0
  171. package/tests/agentic-board-suite/fixtures/invalid/CH-PROPOSAL-LINK--challenge-names-a-later-envelope.json +244 -0
  172. package/tests/agentic-board-suite/fixtures/invalid/CH-PROPOSAL-LINK--dangling-challenge-beside-a-request.json +410 -0
  173. package/tests/agentic-board-suite/fixtures/invalid/CH-PROPOSAL-LINK--dangling-proposal.json +187 -0
  174. package/tests/agentic-board-suite/fixtures/invalid/CH-ROUND-COUNTED--round-two-declared-as-one.json +481 -0
  175. package/tests/agentic-board-suite/fixtures/invalid/CH-ROUND-COUNTED--self-declared-round.json +187 -0
  176. package/tests/agentic-board-suite/fixtures/invalid/DR-ALTERNATIVES--no-alternatives.json +340 -0
  177. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-COMPLETE--hard-gate-left-behind-by-proposer-swap.json +532 -0
  178. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-COMPLETE--hard-gate-left-unattached.json +480 -0
  179. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-COMPLETE--hard-gate-on-root-left-unattached.json +410 -0
  180. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-COMPLETE--late-challenge-before-record.json +410 -0
  181. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-COMPLETE--superseded-request-missed-earlier-challenge.json +565 -0
  182. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-PRECEDES--withdrawal-attached-before-it-was-sent.json +483 -0
  183. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-PRECEDES--withdrawal-descends-from-the-record.json +547 -0
  184. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-REQUIRED--r1-flag-without-challenge.json +349 -0
  185. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-REQUIRED--r2-without-challenge.json +339 -0
  186. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-REQUIRED--r3-no-flags-without-challenge.json +339 -0
  187. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-RESOLVES--dangling-challenge.json +347 -0
  188. package/tests/agentic-board-suite/fixtures/invalid/DR-CHALLENGE-TARGETS-PROPOSAL--challenge-of-requester-message.json +415 -0
  189. package/tests/agentic-board-suite/fixtures/invalid/DR-CLASS-FROM-ROOT--root-declares-no-class.json +345 -0
  190. package/tests/agentic-board-suite/fixtures/invalid/DR-CLASS-FROM-ROOT--root-declares-other-class.json +346 -0
  191. package/tests/agentic-board-suite/fixtures/invalid/DR-DESIGNATED-CHALLENGER--designated-challenger-only-on-the-framing.json +346 -0
  192. package/tests/agentic-board-suite/fixtures/invalid/DR-DESIGNATED-CHALLENGER--investment-without-finance.json +436 -0
  193. package/tests/agentic-board-suite/fixtures/invalid/DR-DESIGNATED-CHALLENGER--stopped-designated-challenge.json +348 -0
  194. package/tests/agentic-board-suite/fixtures/invalid/DR-HARD-GATE--conditional-over-hard-gate.json +357 -0
  195. package/tests/agentic-board-suite/fixtures/invalid/DR-HARD-GATE--unresolved-hard-gate.json +357 -0
  196. package/tests/agentic-board-suite/fixtures/invalid/DR-NO-APPROVE-OVER-DISSENT--approve-over-a-stopped-challenge.json +413 -0
  197. package/tests/agentic-board-suite/fixtures/invalid/DR-NO-APPROVE-OVER-DISSENT--approve-over-dissent.json +346 -0
  198. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--re-ask-without-supersedes.json +426 -0
  199. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--request-superseded-twice.json +581 -0
  200. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--supersedes-a-challenge.json +427 -0
  201. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--supersedes-a-dangling-id.json +427 -0
  202. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--supersedes-a-later-request.json +500 -0
  203. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--superseding-request-from-another-proposer.json +621 -0
  204. package/tests/agentic-board-suite/fixtures/invalid/DR-ONE-REQUEST--supersession-cycle.json +501 -0
  205. package/tests/agentic-board-suite/fixtures/invalid/DR-OWNER-FROM-ROOT--agent-opens-decision-path.json +480 -0
  206. package/tests/agentic-board-suite/fixtures/invalid/DR-OWNER-FROM-ROOT--owner-is-the-requester.json +273 -0
  207. package/tests/agentic-board-suite/fixtures/invalid/DR-OWNER-FROM-ROOT--request-to-later-sorting-non-owner.json +273 -0
  208. package/tests/agentic-board-suite/fixtures/invalid/DR-OWNER-FROM-ROOT--request-to-non-owner.json +273 -0
  209. package/tests/agentic-board-suite/fixtures/invalid/DR-OWNER-FROM-ROOT--root-names-no-owner.json +341 -0
  210. package/tests/agentic-board-suite/fixtures/invalid/DR-PROPOSER-IS-SENDER--challenger-assembles-request.json +337 -0
  211. package/tests/agentic-board-suite/fixtures/invalid/DR-PROPOSER-NOT-CHALLENGER--self-challenge.json +411 -0
  212. package/tests/agentic-board-suite/fixtures/invalid/DR-RISK-CARRIED--concurrent-higher-tier-envelope.json +403 -0
  213. package/tests/agentic-board-suite/fixtures/invalid/DR-RISK-CARRIED--r5-decided-through-lower-tier-request.json +433 -0
  214. package/tests/agentic-board-suite/fixtures/invalid/DR-RISK-CARRIED--request-drops-materiality-flags.json +479 -0
  215. package/tests/agentic-board-suite/fixtures/invalid/DR-RISK-CARRIED--risk-raised-after-request-before-decision.json +403 -0
  216. package/tests/agentic-board-suite/fixtures/invalid/DR-TO-PRINCIPAL--request-to-agent.json +272 -0
  217. package/tests/agentic-board-suite/fixtures/invalid/DR-UNRESOLVED-DISSENT--dissent-dropped.json +341 -0
  218. package/tests/agentic-board-suite/fixtures/invalid/DR-UNRESOLVED-DISSENT--stopped-challenge-recorded-withdrawn.json +408 -0
  219. package/tests/agentic-board-suite/fixtures/invalid/DR-UNRESOLVED-DISSENT--unresolved-without-dissent.json +341 -0
  220. package/tests/agentic-board-suite/fixtures/invalid/DR-VALUE-CASE-REQUIRED--investment-without-value-case.json +353 -0
  221. package/tests/agentic-board-suite/fixtures/invalid/DRC-AFTER-REQUEST--decided-before-request.json +408 -0
  222. package/tests/agentic-board-suite/fixtures/invalid/DRC-AFTER-REQUEST--request-descends-from-its-record.json +408 -0
  223. package/tests/agentic-board-suite/fixtures/invalid/DRC-APPROVAL-STATE--state-contradicts-outcome.json +346 -0
  224. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-approves-a-redesign.json +433 -0
  225. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-contradicts-outcome.json +433 -0
  226. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-does-not-descend-from-its-record.json +499 -0
  227. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-is-the-parent-of-its-record.json +500 -0
  228. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-names-another-approver.json +438 -0
  229. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-nobody-asked-for.json +421 -0
  230. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGN-BINDING--co-sign-sent-before-the-record.json +499 -0
  231. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGNED--decision-in-force-not-co-signed.json +591 -0
  232. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGNED--later-sorting-second-approver-never-signs.json +359 -0
  233. package/tests/agentic-board-suite/fixtures/invalid/DRC-CO-SIGNED--named-second-approver-never-signs.json +359 -0
  234. package/tests/agentic-board-suite/fixtures/invalid/DRC-COMPLETED--record-left-dispatched.json +346 -0
  235. package/tests/agentic-board-suite/fixtures/invalid/DRC-DECIDER-IS-ADDRESSEE--decided-by-non-addressee.json +408 -0
  236. package/tests/agentic-board-suite/fixtures/invalid/DRC-DECIDER-IS-ADDRESSEE--earlier-sorting-non-addressee-decides.json +408 -0
  237. package/tests/agentic-board-suite/fixtures/invalid/DRC-DECIDER-IS-SENDER--relayed-decision.json +408 -0
  238. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--override-without-acceptance.json +356 -0
  239. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--same-round-withdrawal.json +423 -0
  240. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--stopped-over-cap-withdrawal.json +623 -0
  241. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--unrelated-withdrawal-does-not-clear-gate.json +482 -0
  242. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--withdrawal-names-an-undelivered-envelope.json +482 -0
  243. package/tests/agentic-board-suite/fixtures/invalid/DRC-HARD-GATE-ACCEPTANCE--withdrawal-on-another-proposers-revision.json +600 -0
  244. package/tests/agentic-board-suite/fixtures/invalid/DRC-NOT-REQUESTER--second-approver-is-requester.json +433 -0
  245. package/tests/agentic-board-suite/fixtures/invalid/DRC-ONE-RECORD--contradicting-records.json +416 -0
  246. package/tests/agentic-board-suite/fixtures/invalid/DRC-ONE-RECORD--duplicate-co-signature.json +507 -0
  247. package/tests/agentic-board-suite/fixtures/invalid/DRC-PRINCIPAL-SOURCE--agent-writes-decision.json +345 -0
  248. package/tests/agentic-board-suite/fixtures/invalid/DRC-REQUEST-BINDING--points-at-challenge.json +346 -0
  249. package/tests/agentic-board-suite/fixtures/invalid/DRC-REQUEST-BINDING--record-names-a-missing-request.json +346 -0
  250. package/tests/agentic-board-suite/fixtures/invalid/DRC-REQUEST-BINDING--request-edited-after-decision.json +346 -0
  251. package/tests/agentic-board-suite/fixtures/invalid/DRC-SECOND-APPROVER--r5-request-decided-on-lower-tier-branch.json +418 -0
  252. package/tests/agentic-board-suite/fixtures/invalid/DRC-SECOND-APPROVER--r5-single-approver.json +354 -0
  253. package/tests/agentic-board-suite/fixtures/invalid/DRC-SECOND-APPROVER--second-approver-is-decider.json +359 -0
  254. package/tests/agentic-board-suite/fixtures/invalid/HO-AGENT-NO-APPROVAL--agent-claims-approval.json +132 -0
  255. package/tests/agentic-board-suite/fixtures/invalid/HO-APPROVAL-ONLY-ON-RECORD--principal-delegate-claims-approval.json +132 -0
  256. package/tests/agentic-board-suite/fixtures/invalid/HO-ATTEN-ACTIONS--child-widens-actions.json +345 -0
  257. package/tests/agentic-board-suite/fixtures/invalid/HO-ATTEN-BUDGET--child-raises-limit.json +132 -0
  258. package/tests/agentic-board-suite/fixtures/invalid/HO-ATTEN-BUDGET--currency-changed.json +132 -0
  259. package/tests/agentic-board-suite/fixtures/invalid/HO-BLOCK-AS-CHALLENGE--block-sent-as-return.json +184 -0
  260. package/tests/agentic-board-suite/fixtures/invalid/HO-CHAIN-CUSTODY--parent-sender-continues-unstopped.json +187 -0
  261. package/tests/agentic-board-suite/fixtures/invalid/HO-CHAIN-CUSTODY--reply-from-outside-chain.json +132 -0
  262. package/tests/agentic-board-suite/fixtures/invalid/HO-CHAIN-PARENT--hop-skips.json +132 -0
  263. package/tests/agentic-board-suite/fixtures/invalid/HO-CHAIN-TIME--sent-before-parent.json +132 -0
  264. package/tests/agentic-board-suite/fixtures/invalid/HO-DESTINATION-TIER--delegate-to-live-guard.json +132 -0
  265. package/tests/agentic-board-suite/fixtures/invalid/HO-DESTINATION-TIER--delegate-to-untiered-agent.json +132 -0
  266. package/tests/agentic-board-suite/fixtures/invalid/HO-ESCALATE-TO-PRINCIPAL--escalate-to-agent.json +131 -0
  267. package/tests/agentic-board-suite/fixtures/invalid/HO-ESCALATE-TO-PRINCIPAL--escalated-delegate-to-agent.json +132 -0
  268. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-CLASS--classification-lowered.json +346 -0
  269. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-DEADLINE--deadline-extended.json +132 -0
  270. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-MATERIALITY--flag-dropped.json +434 -0
  271. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-MAX-HOPS--max-hops-raised.json +132 -0
  272. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-PROHIBITIONS--prohibition-dropped.json +131 -0
  273. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-SPENT--one-counter-decreases.json +346 -0
  274. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-SPENT--spent-counter-reset.json +346 -0
  275. package/tests/agentic-board-suite/fixtures/invalid/HO-MONO-TIER--tier-lowered.json +346 -0
  276. package/tests/agentic-board-suite/fixtures/invalid/HO-NO-EXECUTION--deploy-authorized.json +133 -0
  277. package/tests/agentic-board-suite/fixtures/invalid/HO-NO-SELF-ADDRESS--challenge-to-self.json +187 -0
  278. package/tests/agentic-board-suite/fixtures/invalid/HO-ONE-CORRELATION--two-correlations.json +132 -0
  279. package/tests/agentic-board-suite/fixtures/invalid/HO-ONE-ROOT--second-root-beside-a-decision.json +410 -0
  280. package/tests/agentic-board-suite/fixtures/invalid/HO-ONE-ROOT--two-roots.json +190 -0
  281. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-CHALLENGE--challenge-without-payload.json +180 -0
  282. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-DECISION-RECORD--record-without-payload.json +331 -0
  283. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-DECISION-REQUEST--request-without-payload.json +324 -0
  284. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-ESCALATE--escalate-without-payload.json +128 -0
  285. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-EXCLUSIVE--return-with-escalation.json +136 -0
  286. package/tests/agentic-board-suite/fixtures/invalid/HO-PAYLOAD-RETURN--return-without-verdict.json +124 -0
  287. package/tests/agentic-board-suite/fixtures/invalid/HO-REASON-HAS-STATUS--reason-on-completed.json +134 -0
  288. package/tests/agentic-board-suite/fixtures/invalid/HO-RETURN-EVIDENCE--pass-on-inference-only.json +132 -0
  289. package/tests/agentic-board-suite/fixtures/invalid/HO-RETURN-EVIDENCE--pass-over-blocking-gap.json +137 -0
  290. package/tests/agentic-board-suite/fixtures/invalid/HO-RETURN-EVIDENCE--pass-with-no-evidence.json +127 -0
  291. package/tests/agentic-board-suite/fixtures/invalid/HO-RETURN-TO-SENDER--return-skips-the-delegator.json +185 -0
  292. package/tests/agentic-board-suite/fixtures/invalid/HO-ROOT-PARENT--root-with-parent.json +132 -0
  293. package/tests/agentic-board-suite/fixtures/invalid/HO-SCHEMA--unknown-credential-field.json +133 -0
  294. package/tests/agentic-board-suite/fixtures/invalid/HO-STOP-NOTE--conflict-without-note.json +133 -0
  295. package/tests/agentic-board-suite/fixtures/invalid/HO-STOPPED-HAS-REASON--stopped-without-reason.json +132 -0
  296. package/tests/agentic-board-suite/fixtures/invalid/HO-UNIQUE-TASK-ID--duplicate-task-id.json +198 -0
  297. package/tests/agentic-board-suite/fixtures/invalid/STOP-BUDGET--budget-undeclared.json +123 -0
  298. package/tests/agentic-board-suite/fixtures/invalid/STOP-BUDGET--return-overspent.json +132 -0
  299. package/tests/agentic-board-suite/fixtures/invalid/STOP-CHALLENGE-ROUNDS--revisions-carried-in-rebuttal-challenges.json +461 -0
  300. package/tests/agentic-board-suite/fixtures/invalid/STOP-CHALLENGE-ROUNDS--third-round.json +454 -0
  301. package/tests/agentic-board-suite/fixtures/invalid/STOP-CHALLENGE-ROUNDS--third-version-by-return-naming-the-original.json +461 -0
  302. package/tests/agentic-board-suite/fixtures/invalid/STOP-DEADLINE--sent-after-deadline.json +132 -0
  303. package/tests/agentic-board-suite/fixtures/invalid/STOP-DEPTH--depth-undeclared.json +182 -0
  304. package/tests/agentic-board-suite/fixtures/invalid/STOP-INSTRUCTION-BAIT--claimed-approval-dispatched.json +132 -0
  305. package/tests/agentic-board-suite/fixtures/invalid/STOP-LIVE-GUARD--mutation-dispatched.json +130 -0
  306. package/tests/agentic-board-suite/fixtures/invalid/STOP-MISSING-EVIDENCE--blocking-gap-dispatched.json +137 -0
  307. package/tests/agentic-board-suite/fixtures/invalid/STOP-MISSING-EVIDENCE--decision-request-blocking-gap.json +351 -0
  308. package/tests/agentic-board-suite/fixtures/invalid/STOP-ROUTING-LOOP--exchanges-on-the-original-envelope.json +381 -0
  309. package/tests/agentic-board-suite/fixtures/invalid/STOP-ROUTING-LOOP--loop-undeclared.json +182 -0
  310. package/tests/agentic-board-suite/fixtures/invalid/STOP-ROUTING-LOOP--revisit-to-challenger-of-another-party.json +256 -0
  311. package/tests/agentic-board-suite/fixtures/invalid/STOP-ROUTING-LOOP--second-revisit-to-challenger.json +540 -0
  312. package/tests/agentic-board-suite/fixtures/invalid/STOP-TERMINAL--challenge-answers-stopped-delegate.json +247 -0
  313. package/tests/agentic-board-suite/fixtures/invalid/STOP-TERMINAL--decision-on-escalated-request.json +353 -0
  314. package/tests/agentic-board-suite/fixtures/invalid/STOP-TERMINAL--decision-on-stopped-request.json +422 -0
  315. package/tests/agentic-board-suite/fixtures/invalid/STOP-TERMINAL--reply-to-stopped-delegate.json +244 -0
  316. package/tests/agentic-board-suite/fixtures/invalid/VC-BENEFIT-CEILING--benefit-above-baseline.json +436 -0
  317. package/tests/agentic-board-suite/fixtures/invalid/VC-COST-SIGN--negative-risk-loss.json +436 -0
  318. package/tests/agentic-board-suite/fixtures/invalid/VC-COST-SIGN--risk-loss-low-minus-one.json +436 -0
  319. package/tests/agentic-board-suite/fixtures/invalid/VC-COUNTERFACTUALS--single-agent-counterfactual-missing.json +427 -0
  320. package/tests/agentic-board-suite/fixtures/invalid/VC-COUNTERFACTUALS--workflow-counterfactual-missing.json +427 -0
  321. package/tests/agentic-board-suite/fixtures/invalid/VC-NAV-ARITHMETIC--nav-off-by-one.json +436 -0
  322. package/tests/agentic-board-suite/fixtures/invalid/VC-ORDER--inverted-range.json +436 -0
  323. package/tests/agentic-board-suite/fixtures/invalid/VC-OVERSIGHT--free-oversight.json +436 -0
  324. package/tests/agentic-board-suite/fixtures/invalid/VC-PROMOTION--measured-but-not-incremental.json +436 -0
  325. package/tests/agentic-board-suite/fixtures/invalid/VC-PROMOTION--promoted-on-estimate.json +436 -0
  326. package/tests/agentic-board-suite/fixtures/invalid/VC-RECOMMEND-INCREMENTAL--recommend-non-incremental.json +436 -0
  327. package/tests/agentic-board-suite/fixtures/invalid/VC-RUNS-BOUND--more-successes-than-runs.json +436 -0
  328. package/tests/agentic-board-suite/fixtures/invalid/VC-SYNTHETIC--synthetic-claims-measured.json +436 -0
  329. package/tests/agentic-board-suite/fixtures/invalid/VC-UNIT-COST--no-successes-nonzero-cost.json +436 -0
  330. package/tests/agentic-board-suite/fixtures/invalid/VC-UNIT-COST--unit-cost-mismatch.json +436 -0
  331. package/tests/agentic-board-suite/fixtures/valid/v01-r0-single-review.json +129 -0
  332. package/tests/agentic-board-suite/fixtures/valid/v01x-block-return-on-inference.json +125 -0
  333. package/tests/agentic-board-suite/fixtures/valid/v01x-block-return-to-principal.json +125 -0
  334. package/tests/agentic-board-suite/fixtures/valid/v01x-pass-return-documentation-evidence.json +129 -0
  335. package/tests/agentic-board-suite/fixtures/valid/v01x-pass-return-mixed-evidence.json +133 -0
  336. package/tests/agentic-board-suite/fixtures/valid/v01x-pass-return-nonblocking-gap.json +134 -0
  337. package/tests/agentic-board-suite/fixtures/valid/v02-r2-architecture-dissent.json +343 -0
  338. package/tests/agentic-board-suite/fixtures/valid/v02x-challenge-after-decision.json +407 -0
  339. package/tests/agentic-board-suite/fixtures/valid/v02x-challenge-after-record-same-second.json +464 -0
  340. package/tests/agentic-board-suite/fixtures/valid/v02x-redispatch-after-cured-stop.json +534 -0
  341. package/tests/agentic-board-suite/fixtures/valid/v02x-security-proposal-returned-to-its-delegator.json +407 -0
  342. package/tests/agentic-board-suite/fixtures/valid/v03-investment-loses-to-workflow.json +433 -0
  343. package/tests/agentic-board-suite/fixtures/valid/v04-r3-hard-gate-risk-acceptance.json +354 -0
  344. package/tests/agentic-board-suite/fixtures/valid/v04x-live-hard-gate-recommend-escalate.json +354 -0
  345. package/tests/agentic-board-suite/fixtures/valid/v04x-live-hard-gate-recommend-reject.json +354 -0
  346. package/tests/agentic-board-suite/fixtures/valid/v05-live-guard-escalation.json +129 -0
  347. package/tests/agentic-board-suite/fixtures/valid/v06-r5-dual-approval.json +430 -0
  348. package/tests/agentic-board-suite/fixtures/valid/v06x-co-sign-same-second.json +430 -0
  349. package/tests/agentic-board-suite/fixtures/valid/v06x-re-asked-r5-decision-co-signed-again.json +662 -0
  350. package/tests/agentic-board-suite/fixtures/valid/v06x-unsigned-approve-superseded-by-co-signed-reject.json +588 -0
  351. package/tests/agentic-board-suite/fixtures/valid/v07-depth-and-routing-loop.json +180 -0
  352. package/tests/agentic-board-suite/fixtures/valid/v08-budget-exhausted.json +186 -0
  353. package/tests/agentic-board-suite/fixtures/valid/v09-instruction-bait.json +131 -0
  354. package/tests/agentic-board-suite/fixtures/valid/v09x-requester-restates-after-bait.json +251 -0
  355. package/tests/agentic-board-suite/fixtures/valid/v10-r1-optional-challenge.json +184 -0
  356. package/tests/agentic-board-suite/fixtures/valid/v10x-report-up-after-challenge.json +246 -0
  357. package/tests/agentic-board-suite/fixtures/valid/v11-two-rounds-hard-gate-withdrawn.json +478 -0
  358. package/tests/agentic-board-suite/fixtures/valid/v11x-designated-challenge-first-answer-over-cap.json +598 -0
  359. package/tests/agentic-board-suite/fixtures/valid/v11x-interleaved-challenger-round-two.json +604 -0
  360. package/tests/agentic-board-suite/fixtures/valid/v11x-proposal-handed-to-the-challenger-capped.json +860 -0
  361. package/tests/agentic-board-suite/fixtures/valid/v11x-round-three-escalation-path.json +747 -0
  362. package/tests/agentic-board-suite/fixtures/valid/v11x-same-second-round-two.json +478 -0
  363. package/tests/agentic-board-suite/fixtures/valid/v11x-two-findings-one-round.json +549 -0
  364. package/tests/agentic-board-suite/fixtures/valid/v12-superseded-request.json +497 -0
  365. package/tests/agentic-board-suite/fixtures/valid/v12x-challenge-after-the-first-decision.json +566 -0
  366. package/tests/agentic-board-suite/fixtures/valid/v12x-late-challenge-cured-by-supersede.json +566 -0
  367. package/tests/agentic-board-suite/fixtures/valid/v12x-supersede-in-the-same-second.json +497 -0
  368. package/tests/agentic-board-suite/test_agentic_board.py +502 -0
  369. package/tests/fixtures/README.md +1 -1
  370. package/tests/fuzz-properties.test.mjs +254 -0
  371. package/tests/mods.test.mjs +768 -0
  372. package/tests/validate-agent-tool-tiers.py +7 -5
  373. package/tests/validate-asset-integrity.py +1 -0
  374. package/tests/validate-mods.py +821 -0
@@ -0,0 +1,50 @@
1
+ // Pure record logic for audit-trail. The hook adds the file I/O around it.
2
+ import { canonicalJson, sha256Hex } from './common.mjs'
3
+
4
+ export const SESSION_RE = /^[A-Za-z0-9._-]{1,128}$/
5
+
6
+ const isObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v)
7
+
8
+ // Picks the session id: env override, then the payload, then a shared fallback log.
9
+ export function rawSessionId(envValue, payload) {
10
+ if (typeof envValue === 'string' && envValue) return envValue
11
+ const id = isObject(payload) ? payload.session_id : undefined
12
+ return typeof id === 'string' && id ? id : 'unsessioned'
13
+ }
14
+
15
+ // Ids become file names, so anything outside SESSION_RE is replaced by a hash prefix.
16
+ export async function safeSessionId(raw) {
17
+ return SESSION_RE.test(raw) ? raw : (await sha256Hex(raw)).slice(0, 32)
18
+ }
19
+
20
+ // Last sequence number in an existing log. A corrupt tail throws: restarting at 1 would fake
21
+ // a clean sequence.
22
+ export function lastSequence(text, name = '') {
23
+ if (text !== '' && !text.endsWith('\n')) throw new Error(`audit log ${name} has an unterminated last line`)
24
+ const lines = text.split('\n').filter((l) => l.trim() !== '')
25
+ if (lines.length === 0) return 0
26
+ let n
27
+ try {
28
+ n = JSON.parse(lines[lines.length - 1]).sequence_number
29
+ } catch {
30
+ n = undefined
31
+ }
32
+ if (!Number.isInteger(n) || n < 1) throw new Error(`audit log ${name} has a corrupt last line`)
33
+ return n
34
+ }
35
+
36
+ // One log line. Hashes cover the canonical JSON of the arguments and the result; Kiro's
37
+ // stdin field names are undocumented, so there the whole payload is hashed.
38
+ export async function buildRecord({ timestamp, sessionId, agentId, payload, harness, sequence }) {
39
+ const p = isObject(payload) ? payload : {}
40
+ const kiro = harness === 'kiro'
41
+ return {
42
+ timestamp,
43
+ session_id: sessionId,
44
+ agent_id: agentId || null,
45
+ tool_name: typeof p.tool_name === 'string' ? p.tool_name : null,
46
+ args_hash: await sha256Hex(canonicalJson(kiro ? (payload ?? null) : (p.tool_input ?? null))),
47
+ result_hash: await sha256Hex(canonicalJson(kiro ? null : (p.tool_response ?? null))),
48
+ sequence_number: sequence,
49
+ }
50
+ }
@@ -0,0 +1,140 @@
1
+ // Command-hook runtime for the mods under mods/. scripts/generate-mods.mjs copies this file
2
+ // byte-for-byte to mods/<id>/lib/runtime.mjs, so edit it here and run `npm run mods:write`.
3
+ //
4
+ // A mod's bin/hook.mjs hands its handlers to runHook, which reads the harness payload from
5
+ // stdin and renders the handler's decision in that harness's protocol. It writes no files
6
+ // and reads no environment; Claude Code's native runtime never imports it.
7
+ import { readFileSync } from 'node:fs'
8
+ import { isAbsolute, resolve, sep } from 'node:path'
9
+
10
+ const HARNESSES = new Set(['claude-code', 'kiro', 'codex'])
11
+ const EVENTS = new Set(['PreToolUse', 'PostToolUse', 'SessionStart'])
12
+ const DECISIONS = new Set(['allow', 'deny', 'ask', 'replace', 'context'])
13
+
14
+ const nodeIo = () => ({
15
+ argv: process.argv.slice(2),
16
+ readStdin: () => readFileSync(0, 'utf8'),
17
+ out: (s) => process.stdout.write(s),
18
+ err: (s) => process.stderr.write(s),
19
+ })
20
+
21
+ const message = (err) => (err instanceof Error ? err.message : String(err))
22
+
23
+ // JSON.parse errors quote the text they failed on, and stdin can carry a tool's input or
24
+ // output: report the failure without echoing any of it.
25
+ function parseStdin(text) {
26
+ try {
27
+ return JSON.parse(text)
28
+ } catch {
29
+ throw new Error('stdin is not valid JSON')
30
+ }
31
+ }
32
+
33
+ export function parseArgs(argv) {
34
+ const usage = 'usage: hook.mjs --harness <claude-code|kiro|codex> --event <PreToolUse|PostToolUse|SessionStart>'
35
+ if (argv.length !== 4) throw new Error(usage)
36
+ const args = {}
37
+ for (let i = 0; i < 4; i += 2) {
38
+ const key = { '--harness': 'harness', '--event': 'event' }[argv[i]]
39
+ if (!key || key in args) throw new Error(usage)
40
+ args[key] = argv[i + 1]
41
+ }
42
+ if (!HARNESSES.has(args.harness)) throw new Error(`unknown harness ${args.harness}; ${usage}`)
43
+ if (!EVENTS.has(args.event)) throw new Error(`unknown event ${args.event}; ${usage}`)
44
+ return args
45
+ }
46
+
47
+ // Where an observer writes `path`. A relative path resolves against the project root when
48
+ // the hook runs from its install location <root>/.<harness dir>/vanguard-mods/<id>/bin/,
49
+ // otherwise against the working directory. Never CLAUDE_PROJECT_DIR: mods read only their
50
+ // own VANGUARD_<ID>_* variables.
51
+ export function projectPath(path, harness, binDir) {
52
+ if (isAbsolute(path)) return path
53
+ const parts = resolve(binDir).split(sep)
54
+ const n = parts.length
55
+ const dir = { 'claude-code': '.claude', kiro: '.kiro', codex: '.codex' }[harness]
56
+ const installed = n >= 5 && parts[n - 1] === 'bin' && parts[n - 3] === 'vanguard-mods' && parts[n - 4] === dir
57
+ return resolve(installed ? parts.slice(0, n - 4).join(sep) || sep : process.cwd(), path)
58
+ }
59
+
60
+ // Rejects decisions the harness cannot express, so a handler bug surfaces as an error
61
+ // instead of a silently dropped verdict.
62
+ function normalize(d, { harness, event }) {
63
+ if (d === undefined) return { decision: 'allow', log: [] }
64
+ if (d === null || typeof d !== 'object' || !DECISIONS.has(d.decision)) {
65
+ throw new Error(`handler returned an invalid decision: ${JSON.stringify(d)}`)
66
+ }
67
+ const log = d.log ?? []
68
+ if (!Array.isArray(log) || !log.every((l) => typeof l === 'string')) throw new Error('decision log must be a string array')
69
+ const needs = { deny: 'reason', ask: 'reason', context: 'text' }[d.decision]
70
+ if (needs && (typeof d[needs] !== 'string' || d[needs] === '')) throw new Error(`${d.decision} needs a ${needs}`)
71
+ if ((d.decision === 'deny' || d.decision === 'ask') && event !== 'PreToolUse') {
72
+ throw new Error(`${d.decision} is only valid for PreToolUse, not ${event}`)
73
+ }
74
+ if (d.decision === 'replace' && (harness !== 'claude-code' || event !== 'PostToolUse')) {
75
+ throw new Error(`replace is only valid for claude-code PostToolUse, not ${harness} ${event}`)
76
+ }
77
+ return { ...d, log }
78
+ }
79
+
80
+ function render(d, { harness, event }, io) {
81
+ const json = (hookSpecificOutput) => io.out(JSON.stringify({ hookSpecificOutput: { hookEventName: event, ...hookSpecificOutput } }) + '\n')
82
+ for (const line of d.log) io.err(line + '\n')
83
+ switch (d.decision) {
84
+ case 'allow':
85
+ return 0
86
+ case 'deny':
87
+ case 'ask':
88
+ if (harness === 'claude-code') {
89
+ json({ permissionDecision: d.decision, permissionDecisionReason: d.reason })
90
+ return 0
91
+ }
92
+ // Kiro and Codex have no confirmation step; exit 2 blocks the call in both.
93
+ io.err(d.decision === 'ask' ? `${d.reason} (confirmation unsupported on ${harness}; denied)\n` : `${d.reason}\n`)
94
+ return 2
95
+ case 'replace':
96
+ json({ updatedToolOutput: d.output })
97
+ return 0
98
+ case 'context':
99
+ if (harness === 'kiro') io.err(d.text + '\n')
100
+ else json({ additionalContext: d.text })
101
+ return 0
102
+ }
103
+ }
104
+
105
+ // Runs one hook invocation and returns the exit code; bin/hook.mjs sets process.exitCode
106
+ // with it rather than calling process.exit, so buffered stdout is never cut short.
107
+ //
108
+ // - Bad arguments, or an event this mod has no handler for: exit 2, which blocks a
109
+ // PreToolUse call in every harness (a broken install must not allow silently).
110
+ // - Invalid stdin or a throwing handler: `onError(err, payload, ctx)` supplies the decision
111
+ // (guards deny, the redactor withholds). Without onError: stderr and exit 1, which is
112
+ // loud but does not block.
113
+ // - An onError that throws or returns an invalid decision: exit 2.
114
+ export async function runHook({ name, handlers, onError }, io = nodeIo()) {
115
+ let ctx
116
+ try {
117
+ ctx = parseArgs(io.argv)
118
+ if (!Object.hasOwn(handlers, ctx.event)) throw new Error(`no handler for ${ctx.event}`)
119
+ } catch (err) {
120
+ io.err(`${name}: ${message(err)}\n`)
121
+ return 2
122
+ }
123
+ let payload
124
+ let decision
125
+ try {
126
+ payload = parseStdin(io.readStdin())
127
+ if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('stdin is not a JSON object')
128
+ decision = normalize(await handlers[ctx.event](payload, ctx), ctx)
129
+ } catch (err) {
130
+ io.err(`${name}: ${message(err)}\n`)
131
+ if (typeof onError !== 'function') return 1
132
+ try {
133
+ decision = normalize(await onError(err, payload, ctx), ctx)
134
+ } catch (err2) {
135
+ io.err(`${name}: error handler failed: ${message(err2)}\n`)
136
+ return 2
137
+ }
138
+ }
139
+ return render(decision, ctx, io)
140
+ }
@@ -0,0 +1,37 @@
1
+ [mod]
2
+ id = "audit-trail"
3
+ name = "Audit Trail"
4
+ description = "Appends one JSON line per tool call to a per-session log with sha256 hashes of the arguments and result."
5
+ version = "1.0.0"
6
+ reach_level = 1
7
+ events = ["PostToolUse"]
8
+
9
+ [footprint]
10
+ env_reads = ["VANGUARD_AUDIT_TRAIL_LOG_DIR", "VANGUARD_AUDIT_TRAIL_SESSION_ID", "VANGUARD_AUDIT_TRAIL_AGENT_ID"]
11
+ file_writes = [".audit/**"]
12
+ spawns = []
13
+ egress_hosts = []
14
+
15
+ [harnesses.claude-code]
16
+ supported = true
17
+ surfaces = ["classic"]
18
+ events = ["PostToolUse"]
19
+
20
+ [harnesses.claude-code.degraded]
21
+ classic = "Classic PostToolUse fires only after a tool succeeds, so failed tool calls are not logged."
22
+
23
+ [harnesses.claude-code.unsupported]
24
+ native = "The native mod API has no file append; rewriting the whole log would break append-only writes. Install the classic hook instead."
25
+
26
+ [harnesses.kiro]
27
+ supported = true
28
+ requires = ["node"]
29
+ events = ["PostToolUse"]
30
+
31
+ [harnesses.kiro.degraded]
32
+ PostToolUse = "Kiro does not document stdin field names; args_hash covers the whole payload, result_hash hashes null, and without VANGUARD_AUDIT_TRAIL_SESSION_ID every session shares the unsessioned log."
33
+
34
+ [harnesses.codex]
35
+ supported = true
36
+ requires = ["node"]
37
+ events = ["PostToolUse"]
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "blast-radius-guard",
3
+ "version": "1.0.0",
4
+ "description": "Asks before, or blocks, destructive shell commands and writes to system paths."
5
+ }
@@ -0,0 +1,53 @@
1
+ # Blast Radius Guard
2
+
3
+ ## Reach and declared footprint
4
+
5
+ **L0 (no declared file writes, subprocess spawns or network egress).** The manifest
6
+ only declares environment reads of `VANGUARD_BLAST_RADIUS_GUARD_MODE` and
7
+ `VANGUARD_BLAST_RADIUS_GUARD_PATTERNS`. The `file_writes`, `spawns` and `egress_hosts`
8
+ arrays are empty. This is capability disclosure, not enforced isolation.
9
+
10
+ ## What it does
11
+
12
+ Checks every tool call before it runs. It asks for confirmation, or blocks, when the call
13
+ looks destructive: a shell command from the built-in pattern list (recursive deletes of
14
+ system or home paths, SQL drops, force pushes to protected branches, piping a download
15
+ into a shell, and similar) or a write to a system path. Paths are normalized lexically
16
+ first, so `/tmp/../etc/passwd` is caught.
17
+
18
+ ## Configuration
19
+
20
+ - `VANGUARD_BLAST_RADIUS_GUARD_MODE`: `confirm` (the default when unset) asks. Any other
21
+ value, including a typo, blocks.
22
+ - `VANGUARD_BLAST_RADIUS_GUARD_PATTERNS`: optional path to a JSON file of extra patterns.
23
+ They are appended to the built-in list and never replace it. If the file is set but
24
+ cannot be read, is not valid JSON, is malformed, or reuses a built-in id, every call is
25
+ denied.
26
+
27
+ The extension file is an array of entries with the same keys as `patterns.json`: `id`
28
+ (kebab-case, unique), `name`, `description`, `regex`, `flags` (`""` or `"i"`), `context`
29
+ (`["bash"]` for shell text or `["file-write"]` for normalized paths) and `severity`.
30
+
31
+ ## Per-harness behavior
32
+
33
+ | Harness | Surface | Behavior |
34
+ | --- | --- | --- |
35
+ | Claude Code | native | `tool.check` guard. Never loosens the engine verdict; a hit asks (or denies in block mode). |
36
+ | Claude Code | classic | `PreToolUse` command hook; a hit asks (or denies in block mode). |
37
+ | Kiro | hook | Degraded. The whole payload is scanned, the system-path rule applies to the path keys whatever the tool, and confirm mode denies. |
38
+ | Codex | hook | Degraded. Codex rejects ask, so confirm mode denies with a reason. |
39
+
40
+ ## Limitations
41
+
42
+ - It scans every string in the tool input, so a file write whose content mentions a
43
+ destructive command also asks. This false positive is by design (fail closed).
44
+ - Relative paths are not resolved against a working directory.
45
+ - Kiro documents no stdin field names, so its whole payload is scanned.
46
+ - An `ask` is settled by the permission mode, so a bypass mode may auto-approve.
47
+ - Input over the size limit is not evaluated and counts as a hit.
48
+ - The native API is early access, verified against Claude Code 2.1.296.
49
+ - Classic, Kiro and Codex hooks need Node 22+.
50
+
51
+ ## Install
52
+
53
+ See `mods/README.md`.
@@ -0,0 +1,18 @@
1
+ // Command-hook entry for classic Claude Code, Kiro and Codex.
2
+ import { readFileSync } from 'node:fs'
3
+ import { checkToolUse, compileGuardPatterns, modeOf, toolUseFromPayload } from '../lib/core.mjs'
4
+ import { runHook } from '../lib/runtime.mjs'
5
+
6
+ const PreToolUse = (payload) => {
7
+ const mode = modeOf(process.env.VANGUARD_BLAST_RADIUS_GUARD_MODE)
8
+ const file = process.env.VANGUARD_BLAST_RADIUS_GUARD_PATTERNS
9
+ const patterns = compileGuardPatterns(file ? readFileSync(file, 'utf8') : undefined)
10
+ return checkToolUse(toolUseFromPayload(payload), patterns, mode)
11
+ }
12
+
13
+ const onError = (err) => ({
14
+ decision: 'deny',
15
+ reason: `blast-radius-guard: check failed; denied (${err instanceof Error ? err.message : String(err)})`,
16
+ })
17
+
18
+ process.exitCode = await runHook({ name: 'blast-radius-guard', handlers: { PreToolUse }, onError })
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/vanguard-mods/blast-radius-guard/bin/hook.mjs\" --harness claude-code --event PreToolUse"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ # Blast Radius Guard on Codex
2
+
3
+ <!-- Generated by scripts/generate-mods.mjs from mod.toml. Do not edit. -->
4
+
5
+ ## Degraded
6
+
7
+ - `PreToolUse`: Codex rejects ask; confirm mode denies with a reason.
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node .codex/vanguard-mods/blast-radius-guard/bin/hook.mjs --harness codex --event PreToolUse"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "modules": [
3
+ "./register.ts"
4
+ ]
5
+ }
@@ -0,0 +1,97 @@
1
+ import { test, expect, mock } from 'claude-code/testing'
2
+
3
+ const ENV_MODE = 'VANGUARD_BLAST_RADIUS_GUARD_MODE'
4
+ const ENV_PATTERNS = 'VANGUARD_BLAST_RADIUS_GUARD_PATTERNS'
5
+ const allow = () => ({ decision: 'allow' as const })
6
+ const bash = (command: string) => ({ tool: 'Bash', input: { command } })
7
+ const ext = (over: object = {}) =>
8
+ JSON.stringify([{ id: 'danger-cmd', name: 'danger cmd', regex: 'danger-cmd', flags: '', context: ['bash'], ...over }])
9
+
10
+ test('rm -rf / asks by default', async ($, on) => {
11
+ mock.env(on, {})
12
+ on('tool.check', allow)
13
+ expect((await $.tool.check(bash('rm -rf /'))).decision).toBe('ask')
14
+ })
15
+
16
+ test('block mode denies', async ($, on) => {
17
+ mock.env(on, { [ENV_MODE]: 'block' })
18
+ on('tool.check', allow)
19
+ expect((await $.tool.check(bash('rm -rf /'))).decision).toBe('deny')
20
+ })
21
+
22
+ test('unknown mode denies', async ($, on) => {
23
+ mock.env(on, { [ENV_MODE]: 'bogus' })
24
+ on('tool.check', allow)
25
+ expect((await $.tool.check(bash('rm -rf /'))).decision).toBe('deny')
26
+ })
27
+
28
+ test('clean command passes the engine verdict', async ($, on) => {
29
+ mock.env(on, {})
30
+ on('tool.check', allow)
31
+ expect((await $.tool.check(bash('ls'))).decision).toBe('allow')
32
+ })
33
+
34
+ test('engine deny stays deny', async ($, on) => {
35
+ mock.env(on, {})
36
+ on('tool.check', () => ({ decision: 'deny' as const, reason: 'engine' }))
37
+ expect((await $.tool.check(bash('ls'))).decision).toBe('deny')
38
+ })
39
+
40
+ test('Write to a traversal path to /etc asks', async ($, on) => {
41
+ mock.env(on, {})
42
+ on('tool.check', allow)
43
+ const v = await $.tool.check({ tool: 'Write', input: { file_path: '/tmp/../etc/passwd', content: 'x' } })
44
+ expect(v.decision).toBe('ask')
45
+ })
46
+
47
+ test('Read of /etc/passwd is allowed', async ($, on) => {
48
+ mock.env(on, {})
49
+ on('tool.check', allow)
50
+ const v = await $.tool.check({ tool: 'Read', input: { file_path: '/etc/passwd' } })
51
+ expect(v.decision).toBe('allow')
52
+ })
53
+
54
+ test('argv array is read as a command line', async ($, on) => {
55
+ mock.env(on, {})
56
+ on('tool.check', allow)
57
+ const v = await $.tool.check({ tool: 'Exec', input: { argv: ['rm', '-rf', '/'] } })
58
+ expect(v.decision).toBe('ask')
59
+ })
60
+
61
+ test('extension pattern asks', async ($, on) => {
62
+ mock.env(on, { [ENV_PATTERNS]: '/ext.json' })
63
+ on('tool.check', allow)
64
+ on('fs.read', () => ({ value: ext() }))
65
+ expect((await $.tool.check(bash('run danger-cmd now'))).decision).toBe('ask')
66
+ })
67
+
68
+ test('unreadable extension file denies', async ($, on) => {
69
+ mock.env(on, { [ENV_PATTERNS]: '/ext.json' })
70
+ on('tool.check', allow)
71
+ on('fs.read', () => {
72
+ throw new Error('nope')
73
+ })
74
+ expect((await $.tool.check(bash('ls'))).decision).toBe('deny')
75
+ })
76
+
77
+ test('extension that is not JSON denies', async ($, on) => {
78
+ mock.env(on, { [ENV_PATTERNS]: '/ext.json' })
79
+ on('tool.check', allow)
80
+ on('fs.read', () => ({ value: 'not json' }))
81
+ expect((await $.tool.check(bash('ls'))).decision).toBe('deny')
82
+ })
83
+
84
+ test('extension reusing a built-in id denies', async ($, on) => {
85
+ mock.env(on, { [ENV_PATTERNS]: '/ext.json' })
86
+ on('tool.check', allow)
87
+ on('fs.read', () => ({ value: ext({ id: 'rm-rf-root-or-home' }) }))
88
+ expect((await $.tool.check(bash('ls'))).decision).toBe('deny')
89
+ })
90
+
91
+ test('oversized command asks as too large', async ($, on) => {
92
+ mock.env(on, {})
93
+ on('tool.check', allow)
94
+ const v = await $.tool.check(bash('a'.repeat(70000)))
95
+ expect(v.decision).toBe('ask')
96
+ expect(v.reason).toContain('input too large to evaluate')
97
+ })
@@ -0,0 +1,13 @@
1
+ import type { Register } from 'claude-code'
2
+ import { checkToolUse, compileGuardPatterns, modeOf } from '../lib/core.mjs'
3
+
4
+ export const register: Register = (on) => {
5
+ on('tool.check', async ($, e, next) => {
6
+ const v = await next(e)
7
+ if (v.decision === 'deny') return v
8
+ const mode = modeOf(await $.env.get('VANGUARD_BLAST_RADIUS_GUARD_MODE'))
9
+ const file = await $.env.get('VANGUARD_BLAST_RADIUS_GUARD_PATTERNS')
10
+ const patterns = compileGuardPatterns(file ? await $.fs.read(file) : undefined)
11
+ return checkToolUse({ tool: e.tool, input: e.input }, patterns, mode) ?? v
12
+ }).catch(() => ({ decision: 'deny', reason: 'blast-radius-guard: check failed; denied' }))
13
+ }
@@ -0,0 +1,7 @@
1
+ # Blast Radius Guard on Kiro
2
+
3
+ <!-- Generated by scripts/generate-mods.mjs from mod.toml. Do not edit. -->
4
+
5
+ ## Degraded
6
+
7
+ - `PreToolUse`: Kiro documents no confirmation protocol or stdin field names; the whole payload is scanned, the system-path rule applies to the path keys whatever the tool, and confirm mode denies.
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": "v1",
3
+ "hooks": [
4
+ {
5
+ "name": "blast-radius-guard:PreToolUse",
6
+ "description": "Asks before, or blocks, destructive shell commands and writes to system paths.",
7
+ "trigger": "PreToolUse",
8
+ "action": {
9
+ "type": "command",
10
+ "command": "node .kiro/vanguard-mods/blast-radius-guard/bin/hook.mjs --harness kiro --event PreToolUse"
11
+ }
12
+ }
13
+ ]
14
+ }