failproofai 1.0.6-beta.1 → 1.0.7-beta.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 (258) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +4 -4
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js +4 -4
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/run/route.js +8 -7
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  33. package/.next/standalone/.next/server/app/audit/page.js +7 -5
  34. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/index.html +1 -1
  37. package/.next/standalone/.next/server/app/index.rsc +15 -15
  38. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  39. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  40. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  42. package/.next/standalone/.next/server/app/page.js +6 -6
  43. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  46. package/.next/standalone/.next/server/app/policies/page.js +13 -11
  47. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/page.js +8 -7
  51. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  54. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  55. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +7 -7
  56. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  59. package/.next/standalone/.next/server/app/projects/page.js +7 -6
  60. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +41 -8
  63. package/.next/standalone/.next/server/app/settings/page.js +12 -9
  64. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/chunks/{[externals]__1msfs-h._.js → [externals]__19_pzeq._.js} +1 -1
  67. package/.next/standalone/.next/server/chunks/{[externals]__1_bftcl._.js → [externals]__1j-zsg5._.js} +1 -1
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1_r2rbg._.js → [root-of-the-server]__1bf34x4._.js} +5 -7
  70. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
  71. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
  74. package/.next/standalone/.next/server/chunks/{_1c3k-8x._.js → _1q5i8mb._.js} +2 -2
  75. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  76. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  77. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/src_hooks_01frwmb._.js +5 -0
  79. package/.next/standalone/.next/server/chunks/src_hooks_18qtd42._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +4 -2
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +4 -0
  83. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__056wjo4._.js} +3 -3
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0576iuy._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +3 -0
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +22 -0
  87. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0n0xg95._.js} +3 -3
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qxnccm._.js +5 -0
  90. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__0rwtwpm._.js} +3 -3
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s_yomn._.js +4 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0soxz2z._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__11mayhe._.js} +3 -3
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0cxe_2_._.js → [root-of-the-server]__1mf3zp6._.js} +3 -3
  98. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1x3j9mj._.js → [root-of-the-server]__1p28m0u._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__1pprgri._.js} +3 -3
  100. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__1q4p5b8._.js} +3 -3
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/ssr/{_0qlygw6._.js → _0cf1olg._.js} +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/ssr/{_1_ddpm3._.js → _135-c3y._.js} +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +5 -0
  110. package/.next/standalone/.next/server/chunks/ssr/{_1es2j7i._.js → _1gb0ifp._.js} +5 -5
  111. package/.next/standalone/.next/server/chunks/ssr/{_1_qswah._.js → _1q46vxx._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/{_0h_douw._.js → _1w_5l7t._.js} +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +4 -4
  115. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  119. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/ssr/lib_utils_ts_0az0sfq._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0drixxt._.js → node_modules_next_dist_0w6mzq5._.js} +4 -4
  122. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +151 -0
  123. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  124. package/.next/standalone/.next/server/chunks/ssr/src_hooks_095a_79._.js +5 -0
  125. package/.next/standalone/.next/server/chunks/ssr/src_hooks_15t8kqj._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/src_hooks_18k8rl0._.js +12 -0
  127. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1fm2w5z._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1j0zy3v._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.js +1 -1
  132. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  133. package/.next/standalone/.next/server/pages/404.html +1 -1
  134. package/.next/standalone/.next/server/pages/500.html +1 -1
  135. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/server-reference-manifest.json +56 -23
  137. package/.next/standalone/.next/static/chunks/{06fjx8-0_qufs.js → 00i9thnpht4l1.js} +1 -1
  138. package/.next/standalone/.next/static/chunks/{3ht0xsco46pul.js → 0289oapk24zql.js} +1 -1
  139. package/.next/standalone/.next/static/chunks/{1qf2uiipatr80.js → 0pyy9m5cm8zku.js} +1 -1
  140. package/.next/standalone/.next/static/chunks/15heikmy3yjzc.js +1 -0
  141. package/.next/standalone/.next/static/chunks/285spx855h_3r.css +2 -0
  142. package/.next/standalone/.next/static/chunks/{02vwr5js1x28j.js → 2plg9qae1yejv.js} +1 -1
  143. package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +1 -0
  144. package/.next/standalone/.next/static/chunks/{2x776s0x3n_tu.js → 2tqszh84mp_2l.js} +1 -1
  145. package/.next/standalone/.next/static/chunks/34jpdb2xjm-x7.js +1 -0
  146. package/.next/standalone/.next/static/chunks/3cdkxrukvlvul.js +1 -0
  147. package/.next/standalone/.next/static/chunks/3dnd-_29uiaj3.js +6 -0
  148. package/.next/standalone/.next/static/chunks/{0y227_ohdqwvn.js → 3vp7c3knkh8mr.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/{27_bwbg8sv6vj.js → 3vwnzfx9q80yl.js} +1 -1
  150. package/.next/standalone/app/actions/get-jev-config.ts +409 -0
  151. package/.next/standalone/app/actions/pack-actions.ts +12 -0
  152. package/.next/standalone/app/actions/update-jev-config.ts +420 -0
  153. package/.next/standalone/app/components/jev-notices.tsx +96 -0
  154. package/.next/standalone/app/policies/hooks-client.tsx +9 -0
  155. package/.next/standalone/app/settings/jev-panel.tsx +469 -0
  156. package/.next/standalone/app/settings/page.tsx +20 -1
  157. package/.next/standalone/app/settings/settings-client.tsx +27 -1
  158. package/.next/standalone/app/settings/settings.css +79 -0
  159. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +51 -3
  160. package/.next/standalone/fp-cloud-cli/README.md +1 -1
  161. package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -1
  162. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +44 -0
  163. package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +226 -5
  164. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +11 -0
  165. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +68 -1
  166. package/.next/standalone/fp-cloud-cli/pyproject.toml +1 -1
  167. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +17 -2
  168. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +7 -4
  169. package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +184 -0
  170. package/.next/standalone/fp-cloud-cli/uv.lock +3 -3
  171. package/.next/standalone/package.json +11 -11
  172. package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
  173. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  174. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +2 -0
  175. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +24 -1
  176. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +41 -6
  177. package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +88 -0
  178. package/.next/standalone/sdk/python/uv.lock +15 -15
  179. package/.next/standalone/server.js +1 -1
  180. package/bin/failproofai.mjs +115 -2
  181. package/dist/cli.mjs +10982 -3787
  182. package/dist/index.js +19 -1
  183. package/dist/worker.mjs +13761 -7834
  184. package/package.json +11 -11
  185. package/pi-extension/index.ts +11 -0
  186. package/scripts/build-policy-pack.mjs +39 -2
  187. package/src/hooks/builtin-policies.ts +21 -3
  188. package/src/hooks/cloud-enrollment-cli.ts +1 -1
  189. package/src/hooks/cloud-managed-policies.ts +22 -0
  190. package/src/hooks/custom-hooks-loader.ts +45 -7
  191. package/src/hooks/custom-hooks-registry.ts +45 -1
  192. package/src/hooks/daemon-client.ts +128 -50
  193. package/src/hooks/daemon-service.ts +30 -0
  194. package/src/hooks/effective-reviewers.ts +79 -0
  195. package/src/hooks/first-run-gate.ts +5 -0
  196. package/src/hooks/fp-home.ts +23 -0
  197. package/src/hooks/handler.ts +265 -6
  198. package/src/hooks/hook-activity-store.ts +105 -1
  199. package/src/hooks/hook-telemetry.ts +41 -0
  200. package/src/hooks/integrations.ts +3 -2
  201. package/src/hooks/jev-activity.ts +385 -0
  202. package/src/hooks/jev-cli.ts +1193 -0
  203. package/src/hooks/loader-utils.ts +6 -0
  204. package/src/hooks/manager.ts +4 -4
  205. package/src/hooks/pack-cli.ts +237 -11
  206. package/src/hooks/pack-manifest.ts +479 -7
  207. package/src/hooks/pack-store.ts +156 -10
  208. package/src/hooks/policy-authority.ts +333 -0
  209. package/src/hooks/policy-catalog.ts +65 -0
  210. package/src/hooks/policy-evaluator.ts +940 -796
  211. package/src/hooks/policy-registry.ts +25 -0
  212. package/src/hooks/policy-reviewability.ts +229 -0
  213. package/src/hooks/policy-types.ts +126 -0
  214. package/src/hooks/semantic/combine.ts +541 -0
  215. package/src/hooks/semantic/compile.ts +176 -0
  216. package/src/hooks/semantic/decide.ts +392 -0
  217. package/src/hooks/semantic/envelope.ts +1296 -0
  218. package/src/hooks/semantic/evaluator.ts +547 -0
  219. package/src/hooks/semantic/facts.ts +292 -0
  220. package/src/hooks/semantic/intent.ts +1190 -0
  221. package/src/hooks/semantic/jev-client.ts +643 -0
  222. package/src/hooks/semantic/jev-config.ts +594 -0
  223. package/src/hooks/semantic/jev-review.ts +374 -0
  224. package/src/hooks/semantic/jev-stats.ts +289 -0
  225. package/src/hooks/semantic/jev-throttle.ts +421 -0
  226. package/src/hooks/semantic/pack-policies.ts +251 -0
  227. package/src/hooks/semantic/policies.ts +596 -0
  228. package/src/hooks/semantic/precondition-names.ts +60 -0
  229. package/src/hooks/semantic/preconditions.ts +58 -0
  230. package/src/hooks/semantic/redact.ts +2910 -0
  231. package/src/hooks/semantic/types.ts +145 -0
  232. package/src/hooks/semver-precedence.ts +128 -0
  233. package/src/hooks/worker-server.ts +119 -26
  234. package/src/index.ts +6 -0
  235. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +0 -3
  236. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +0 -4
  237. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +0 -4
  238. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +0 -5
  239. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +0 -3
  240. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +0 -22
  241. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +0 -5
  243. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +0 -3
  244. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +0 -3
  246. package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +0 -5
  247. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +0 -3
  248. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +0 -10
  250. package/.next/standalone/.next/static/chunks/0cbvytf8epn34.js +0 -1
  251. package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +0 -2
  252. package/.next/standalone/.next/static/chunks/1p4xauf-zu6zk.js +0 -1
  253. package/.next/standalone/.next/static/chunks/1uc_s_dftgxiq.js +0 -6
  254. package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +0 -1
  255. package/.next/standalone/.next/static/chunks/2lvr2pt70qn14.js +0 -1
  256. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_buildManifest.js +0 -0
  257. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_clientMiddlewareManifest.js +0 -0
  258. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_ssgManifest.js +0 -0
@@ -1070,6 +1070,36 @@ export async function probeDaemonEndToEnd(): Promise<boolean> {
1070
1070
  return (await probeDaemon()).ok;
1071
1071
  }
1072
1072
 
1073
+ /**
1074
+ * Proves that the daemon supports the structured request native plugins use.
1075
+ *
1076
+ * This deliberately follows the ordinary end-to-end hook probe. Besides
1077
+ * retaining its startup-race retry, that makes the failure meaningful: an old
1078
+ * daemon can be fully healthy for shell hooks while still lacking the
1079
+ * `policyEvaluation` variant added for Hermes. Protocol v1 predates that
1080
+ * variant, so version equality alone cannot establish the capability.
1081
+ */
1082
+ export async function probeDaemonPolicyEvaluation(): Promise<boolean> {
1083
+ if (!(await probeDaemonEndToEnd())) return false;
1084
+ try {
1085
+ const { attemptDaemonPolicyEvaluation } = await import("./daemon-client");
1086
+ const attempt = await attemptDaemonPolicyEvaluation(
1087
+ {
1088
+ integration: "hermes",
1089
+ event: "on_session_start",
1090
+ payload: {
1091
+ hook_event_name: "on_session_start",
1092
+ source: "failproofai-native-policy-health-probe",
1093
+ },
1094
+ },
1095
+ { responseTimeoutMs: DAEMON_PROBE_TIMEOUT_MS },
1096
+ );
1097
+ return attempt.ok;
1098
+ } catch {
1099
+ return false;
1100
+ }
1101
+ }
1102
+
1073
1103
  /**
1074
1104
  * Waits for the service to report running and to HOLD it — a `Type=simple` unit
1075
1105
  * is active the moment it forks, so one optimistic reading passes a daemon that
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The semantic checks a `reviewedBy` may name ON THIS MACHINE.
3
+ *
4
+ * ## Why this is not simply `SEMANTIC_REVIEWER_NAMES`
5
+ *
6
+ * A pack that declares its own `semantic` entries replaces the compiled-in
7
+ * `SEMANTIC_POLICIES` wholesale (`semantic/pack-policies.ts`). So on that
8
+ * machine the sixteen builtin names are checks that will never be asked, and the
9
+ * pack's own names are the only ones that will — while
10
+ * `resolvePolicyAuthority`, judging against the builtin set, would read the
11
+ * pack's `reviewedBy: ["its-own-check"]` as naming a check "this build does not
12
+ * have" and downgrade the policy to `hard` with nothing but a warning in the
13
+ * hook log. That is a rewritten policy set arriving with its reviewability
14
+ * quietly removed: the deny half of the two-tier evaluator working exactly as
15
+ * documented while the clearing half cannot fire once — the failure
16
+ * `policy-reviewability.ts` was written to make audible, reintroduced one layer
17
+ * down.
18
+ *
19
+ * The names come from the MANIFEST, which is already where `authority` and
20
+ * `reviewedBy` themselves are read from, so this adds no new trust and imports
21
+ * no semantic module.
22
+ *
23
+ * ## Why it is a module of its own, and cached the way it is
24
+ *
25
+ * `registerPolicy` asks this for every policy that declares an authority — forty
26
+ * times per hook event — and the honest answer costs a digest verification per
27
+ * installed pack. So it is cached, and the cache is dropped by `clearPolicies()`:
28
+ * one read per registration pass, and the answer's lifetime is exactly the
29
+ * registry's. That is the right coupling rather than a convenient one — this set
30
+ * describes the policies sitting in the registry, and the handler rebuilds both
31
+ * together, so they cannot come to describe different machines.
32
+ *
33
+ * It lives beside `policy-registry.ts` rather than inside `pack-manifest.ts`
34
+ * because the registry may not depend on the whole manifest reader: several
35
+ * tests isolate themselves from the running machine's real packs by mocking
36
+ * `pack-manifest` down to `readInstalledPacks` and `hasInstalledPacks`, and a
37
+ * registration path reaching for a third export of that module breaks them all
38
+ * — loudly here, but it is the shape of a change that would later break them
39
+ * silently.
40
+ */
41
+ import { readInstalledPacks } from "./pack-manifest";
42
+ import { SEMANTIC_REVIEWER_NAMES } from "./policy-authority";
43
+
44
+ let cached: ReadonlySet<string> | null = null;
45
+
46
+ /**
47
+ * The installed packs' semantic policy names when any pack declares some, and
48
+ * this build's compiled-in set otherwise.
49
+ *
50
+ * Never throws: an unreadable manifest declares nothing, and the builtin set
51
+ * stands. That is the same fail-open posture every other reader of the pack
52
+ * manifest takes, and here it also fails in the safe direction — a pack's
53
+ * `reviewedBy` will not match a builtin name, so its policies stay `hard`
54
+ * rather than being cleared by a question nobody could read.
55
+ */
56
+ export function effectiveReviewerNames(): ReadonlySet<string> {
57
+ if (cached) return cached;
58
+ let names: ReadonlySet<string> = SEMANTIC_REVIEWER_NAMES;
59
+ try {
60
+ const declared = readInstalledPacks().packs.flatMap((p) => (p.semantic ?? []).map((s) => s.name));
61
+ // Only when a pack actually declared some. A machine whose packs carry only
62
+ // the regex floor still runs the compiled-in semantic set, so its builtin
63
+ // reviewer names are the live ones.
64
+ if (declared.length > 0) names = new Set(declared);
65
+ } catch {
66
+ // See above: silence here is the builtin set, not an empty one.
67
+ }
68
+ cached = names;
69
+ return names;
70
+ }
71
+
72
+ /**
73
+ * Drop the cached answer. Called by `clearPolicies()`, which every evaluation
74
+ * runs before it registers anything — so a pack installed under a long-lived
75
+ * warm worker is picked up on the next event rather than at the next restart.
76
+ */
77
+ export function forgetEffectiveReviewerNames(): void {
78
+ cached = null;
79
+ }
@@ -35,6 +35,11 @@ export const FIRST_RUN_EXEMPT_SUBCOMMANDS: readonly string[] = [
35
35
  // Same reason: a backfill is an explicit instruction about an already-set-up
36
36
  // machine, and interrupting it to offer setup answers a question nobody asked.
37
37
  "backfill",
38
+ // `jev setup` is configuration in its own right, and `jev status` / `test` /
39
+ // `remove` inspect or undo it. It is also the command most likely to be
40
+ // scripted with a key on stdin — a wizard in front of it would read that key
41
+ // as the answer to its first question.
42
+ "jev",
38
43
  ];
39
44
 
40
45
  export function shouldOfferFirstRun(args: readonly string[]): boolean {
@@ -51,6 +51,7 @@
51
51
  * VERSION layout / cli / daemon versions
52
52
  * config.json 0644 non-secret: mode, daemon, collector prefs
53
53
  * credentials.json 0600 every token
54
+ * jev.json 0600 the customer's own Jev endpoint + key (BYOK opt-in)
54
55
  * policies-config.json the builtin enable/disable set + params
55
56
  * bin/ downloaded daemon binaries, one per version
56
57
  * policies/ every policy: the user's *.mjs sit directly here
@@ -148,6 +149,14 @@ export const configFile = (home?: string) => atHome(home, "config.json");
148
149
  */
149
150
  export const credentialsFile = (home?: string) => atHome(home, "credentials.json");
150
151
 
152
+ /**
153
+ * The customer's own Jev endpoint and key (BYOK), owner-only. Its presence is
154
+ * the whole opt-in to the two-tier evaluator: absent, hooks run the regex
155
+ * engine exactly as before. GLOBAL only — a repository can never set it. See
156
+ * `src/hooks/semantic/jev-config.ts`.
157
+ */
158
+ export const jevConfigFile = (home?: string) => atHome(home, "jev.json");
159
+
151
160
  // ── Daemon binaries ──────────────────────────────────────────────────────────
152
161
 
153
162
  export const binDir = (home?: string) => atHome(home, "bin");
@@ -389,6 +398,12 @@ export const failedDir = (home?: string) => resolve(stateDir(home), "failed");
389
398
  export const collectorHealthFile = (home?: string) => resolve(stateDir(home), "collector-health.json");
390
399
  /** Per-session enforcement pauses, keyed by a hash of the session id. */
391
400
  export const sessionPauseDir = () => resolve(stateDir(), "sessions");
401
+ /**
402
+ * The semantic (Jev) evaluator's local state: `sessions/<id>.json` holds the
403
+ * human prompts recorded at `UserPromptSubmit`, and `verdicts.jsonl` one row
404
+ * per semantic evaluation. Written only when a Jev config (BYOK) is present.
405
+ */
406
+ export const semanticDir = () => resolve(stateDir(), "semantic");
392
407
  /**
393
408
  * When the scheduled audit last ran, and when the next one is due.
394
409
  *
@@ -554,6 +569,9 @@ export const HOME_CLASSES: readonly { path: (home?: string) => string; class: Da
554
569
  // policy silently — it keeps enforcing whatever it last had, reports healthy,
555
570
  // and never reconciles again. Nothing re-derives a token.
556
571
  { path: credentialsFile, class: "user-typed" },
572
+ // The Jev BYOK config. A person typed the key; nothing regenerates it, and
573
+ // losing it silently turns the two-tier evaluator off.
574
+ { path: jevConfigFile, class: "user-typed" },
557
575
  // Holds `daemon.configured` (the flag that makes the machine fail closed),
558
576
  // the collector preferences, `[audit] auto`, the telemetry opt-out, and
559
577
  // `collector.sources.*.extra_paths` — the entire output of `harness add-path`.
@@ -642,6 +660,11 @@ export const HOME_CLASSES: readonly { path: (home?: string) => string; class: Da
642
660
  { path: codexSessionPathsFile, class: "derived" },
643
661
  { path: shimsDir, class: "derived" },
644
662
  { path: sessionPauseDir, class: "derived" },
663
+ // Recorded prompts (a six-hour window that only ever lets Jev clear a
664
+ // reviewable policy) and a local diagnostic log of semantic verdicts that is
665
+ // never shipped. Losing either costs a clear and some history, never
666
+ // enforcement.
667
+ { path: semanticDir, class: "derived" },
645
668
  { path: logsDir, class: "derived" },
646
669
  { path: lastVersionFile, class: "derived" },
647
670
  { path: launcherMarker, class: "derived" },
@@ -24,18 +24,26 @@ import {
24
24
  OPENCLAW_EVENT_MAP,
25
25
  ANTIGRAVITY_EVENT_MAP,
26
26
  } from "./types";
27
+ import { existsSync } from "node:fs";
27
28
  import { canonicalizeToolName, canonicalizeToolInput } from "./tool-name-canonicalize";
28
29
  import { normalizeCliPayload } from "./normalize-cli-payload";
29
30
  import type { PolicyFunction, PolicyResult, HooksConfig } from "./policy-types";
30
31
  import { readMergedHooksConfig } from "./hooks-config";
31
32
  import { registerBuiltinPolicies } from "./builtin-policies";
32
33
  import { evaluatePolicies } from "./policy-evaluator";
34
+ // Types only: the semantic (Jev) modules are loaded with a dynamic import, and
35
+ // only once a Jev config exists, so an unconfigured machine never loads them.
36
+ import type { TwoTierReview } from "./semantic/combine";
37
+ import type { JevActivityFields } from "./semantic/combine";
38
+ import type { JevConfig } from "./semantic/jev-config";
33
39
  import { clearPolicies, registerPolicy, getPoliciesForEvent } from "./policy-registry";
34
40
  import { loadAllCustomHooks } from "./custom-hooks-loader";
41
+ import { authorityDeclarationFor, resolvePolicyAuthority, warnAuthority } from "./policy-authority";
35
42
  import type { CustomHook } from "./policy-types";
36
43
  import { persistHookActivity } from "./hook-activity-store";
37
44
  import { deliveryHealth, deliveryHealthLine } from "./delivery-health";
38
45
  import { trackHookEvent, flushHookTelemetry } from "./hook-telemetry";
46
+ import * as hookTelemetry from "./hook-telemetry";
39
47
  import { resolveCwd } from "./resolve-cwd";
40
48
  import { resolvePermissionMode } from "./resolve-permission-mode";
41
49
  import { resolveTranscriptPath } from "./resolve-transcript-path";
@@ -46,6 +54,7 @@ import { readActiveCloudManagedPolicies, type CloudManagedPolicyArtifact } from
46
54
  import { hasInstalledPacks, readInstalledPacks, type PackError, type ResolvedPack } from "./pack-manifest";
47
55
  import { missingGuards, packFailureReason, combinedGuardMatch, guardsCover } from "./pack-failclosed";
48
56
  import { readActivePause, type ActivePause } from "./session-pause";
57
+ import { jevConfigFile } from "./fp-home";
49
58
  import { layoutWarningForHook } from "./fp-reset";
50
59
 
51
60
  /**
@@ -153,6 +162,16 @@ export interface EvaluateHookEventOptions {
153
162
  * written for.
154
163
  */
155
164
  fallbackCwd?: string;
165
+ /**
166
+ * The warm worker's hook into its request queue. Called at most once, and
167
+ * only on the two-tier path, at the point this evaluation stops reading the
168
+ * process-global policy registry and starts waiting on Jev's network answer
169
+ * — so the next queued hook can run instead of every hook on the machine
170
+ * queueing behind that round trip. After it is called nothing in this
171
+ * evaluation reads the registry again. The one-shot path has no queue and
172
+ * leaves it unset.
173
+ */
174
+ releaseRegistry?: () => void;
156
175
  }
157
176
 
158
177
  /**
@@ -186,6 +205,211 @@ async function runObserved(
186
205
  }
187
206
  }
188
207
 
208
+ // ── Two-tier (Jev) opt-in ────────────────────────────────────────────────────
209
+ //
210
+ // Jev reviews a call only when ALL of these hold, and the regex engine runs
211
+ // alone — exactly as it did before two tiers existed — otherwise:
212
+ //
213
+ // - a valid BYOK config exists (`~/.failproofai/jev.json`, global only);
214
+ // - `FAILPROOFAI_EVALUATOR` is not `legacy` (see "Turning Jev off" below);
215
+ // - this is not the fail-closed `forceDecision` path and no session pause is
216
+ // active — a pause suspends local policy, and Jev must not become a way to
217
+ // evaluate what the pause switched off;
218
+ // - the event is a gate (`PreToolUse` / `PermissionRequest`) for a named tool
219
+ // that the AGENT requested (see `isHumanAuthoredGate`).
220
+ //
221
+ // There is no cloud-deployment exclusion: cloud policies default to `hard`, so
222
+ // Jev can only make a centrally managed machine stricter, never weaker.
223
+ //
224
+ // Turning Jev off. The switch that works everywhere is the config file itself:
225
+ // it is read on every event, here, in whichever process evaluates — the
226
+ // daemon's warm worker included — so `failproofai jev remove` (or a `mode:
227
+ // "shadow"` config, which keeps enforcing the regex result) applies from the
228
+ // next tool call, with no restart. `FAILPROOFAI_EVALUATOR=legacy` is the dev
229
+ // escape hatch, read from the EVALUATING process's environment: it covers a
230
+ // one-shot hook (no daemon) and a worker whose own environment sets it, but
231
+ // not a variable exported in a shell on a daemon machine — the daemon
232
+ // forwards a hook's event, cli, stdin and cwd to the worker, never the hook
233
+ // process's environment. `failproofai jev status` says so when it sees the
234
+ // variable. A per-session switch for daemon machines would be a new field in
235
+ // the daemon protocol; there is none today.
236
+
237
+ /** The two gate events Jev reviews. Everything else is regex-only. */
238
+ const JEV_GATE_EVENTS: ReadonlySet<string> = new Set(["PreToolUse", "PermissionRequest"]);
239
+
240
+ /**
241
+ * A gate event that carries a command the HUMAN typed, not one the agent
242
+ * requested: Pi's `user_bash` (`!cmd` at Pi's prompt) canonicalizes to
243
+ * `PreToolUse` like an agent's `tool_call`. Jev judges an agent's request
244
+ * against what the human asked for, and says so to the model ("A coding agent
245
+ * has REQUESTED the tool call"); the human's own command is the human asking,
246
+ * and never appears in `user_said`, so Jev would judge it as an unrequested
247
+ * agent action and could block the human's own command. The regex policies
248
+ * still run on it exactly as they always have.
249
+ */
250
+ function isHumanAuthoredGate(rawEventType: string | undefined, cli: IntegrationType): boolean {
251
+ return cli === "pi" && rawEventType === "user_bash";
252
+ }
253
+
254
+ function jevForcedOff(opts: EvaluateHookEventOptions | undefined): boolean {
255
+ return process.env.FAILPROOFAI_EVALUATOR === "legacy" || !!opts?.forceDecision;
256
+ }
257
+
258
+ /**
259
+ * The BYOK config (with the build's default mode, D2), or null (Jev off). A
260
+ * config that cannot be read is off, never a failure. Logged at info, like
261
+ * every configured-but-broken Jev path: warn-level lines reach the hook's
262
+ * stderr, which is the deny text itself on some CLIs (Factory's exit 2).
263
+ * `failproofai jev status` is where a broken config is made visible.
264
+ *
265
+ * The file is checked for BEFORE the import, and that order is the point. With
266
+ * no `jev.json` the answer is null however the module would have read it — so
267
+ * the stat decides it, and a machine that never configured Jev never evaluates
268
+ * a line of the semantic modules to be told what their absence already says.
269
+ * The same stat-rather-than-load trade `warnAuthority` makes, for the same
270
+ * reason. It short-circuits nothing but a null `loadJevConfig` would have
271
+ * returned itself: that function reads this path, and an absent file, an
272
+ * unreadable directory above it and a path that is not a file are all null
273
+ * there too.
274
+ */
275
+ async function readJevConfig(): Promise<{ config: JevConfig; defaultMode: TwoTierReview["mode"] } | null> {
276
+ try {
277
+ if (!existsSync(jevConfigFile())) return null;
278
+ const { loadJevConfig, DEFAULT_JEV_MODE } = await import("./semantic/jev-config");
279
+ const config = loadJevConfig();
280
+ return config ? { config, defaultMode: DEFAULT_JEV_MODE } : null;
281
+ } catch (err) {
282
+ hookLogInfo(
283
+ `Jev config could not be read (${err instanceof Error ? err.message : String(err)}); the regex engine decides alone`,
284
+ );
285
+ return null;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Start Jev's review of this call — before the regex policies run, so the two
291
+ * proceed in parallel — or return null for the regex-only path.
292
+ */
293
+ async function startTwoTier(
294
+ canonicalEventType: string,
295
+ parsed: Record<string, unknown>,
296
+ session: SessionMetadata,
297
+ cli: IntegrationType,
298
+ opts: EvaluateHookEventOptions | undefined,
299
+ activePause: ActivePause | null,
300
+ ): Promise<TwoTierReview | null> {
301
+ if (!JEV_GATE_EVENTS.has(canonicalEventType)) return null;
302
+ if (isHumanAuthoredGate(session.rawHookEventName, cli)) return null;
303
+ if (typeof parsed.tool_name !== "string" || parsed.tool_name.length === 0) return null;
304
+ if (jevForcedOff(opts) || activePause) return null;
305
+ const loaded = await readJevConfig();
306
+ if (!loaded) return null;
307
+ const cfg = loaded.config;
308
+ try {
309
+ const { startJevReview } = await import("./semantic/jev-review");
310
+ return startJevReview(cfg, {
311
+ eventType: canonicalEventType,
312
+ toolName: parsed.tool_name,
313
+ toolInput: parsed.tool_input,
314
+ cwd: session.cwd,
315
+ permissionMode: session.permissionMode,
316
+ sessionId: session.sessionId,
317
+ cli,
318
+ });
319
+ } catch (err) {
320
+ // Configured but unable to start: that is a fallback, and it is recorded
321
+ // as one — every regex verdict hard, the regex result final. Recorded as
322
+ // `error`, one of the reason codes the activity store and the collector
323
+ // know (T8's closed list); an unknown code would ship as `other`. Logged
324
+ // at info, off the hook's stderr (see `readJevConfig`); the activity row
325
+ // carries the fallback.
326
+ hookLogInfo(`Jev review could not start (${err instanceof Error ? err.message : String(err)})`);
327
+ return {
328
+ mode: cfg.mode === "shadow" || cfg.mode === "enforce" ? cfg.mode : loaded.defaultMode,
329
+ review: Promise.resolve({ kind: "fallback", reason: "error", latencyMs: null, model: null }),
330
+ abort: () => {},
331
+ authorityOf: () => ({ authority: "hard", reviewedBy: [] }),
332
+ };
333
+ }
334
+ }
335
+
336
+ /**
337
+ * The Jev properties for `hook_policy_triggered`, from T8's
338
+ * `jevTelemetryProperties` (decisions, reason codes, policy and model names,
339
+ * a latency — never command or prompt text). `{}` when the two-tier path did
340
+ * not run, so an unconfigured machine's event is unchanged, and `{}` when the
341
+ * helper is not in this build (it is T8's, not a §7 contract) or throws:
342
+ * telemetry never costs a hook its answer.
343
+ *
344
+ * Only its `jev_`-prefixed keys are kept. These are spread into an event whose
345
+ * core properties (`decision`, `policy_name`, `event_type`, …) are what the
346
+ * rollout is read from, and a helper owned by another task must not be able to
347
+ * overwrite one of them — silently, and only on two-tier machines — by
348
+ * emitting a key of that name. The helper is treated as untrusted in every
349
+ * other respect (absent, non-object, throwing); this is the same guard for
350
+ * what it returns.
351
+ */
352
+ function jevTelemetry(activity: JevActivityFields | undefined): Record<string, unknown> {
353
+ if (!activity) return {};
354
+ try {
355
+ const build = (hookTelemetry as unknown as { jevTelemetryProperties?: (entry: JevActivityFields) => unknown })
356
+ .jevTelemetryProperties;
357
+ const props = typeof build === "function" ? build(activity) : null;
358
+ if (!props || typeof props !== "object" || Array.isArray(props)) return {};
359
+ return Object.fromEntries(Object.entries(props as Record<string, unknown>).filter(([key]) => key.startsWith("jev_")));
360
+ } catch {
361
+ return {};
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Record what the human just typed, for Jev to judge later calls against.
367
+ * Only when Jev is configured. Every canonical `UserPromptSubmit` is passed,
368
+ * except a prompt a policy denied on a CLI where that deny is known to hold
369
+ * (`ENFORCEMENT_CAPABILITY` says `block`): the agent never receives that
370
+ * prompt, so it cannot be what the agent is working on. Where a prompt deny
371
+ * is only observed — Goose, OpenCode, Antigravity — or not verified, the
372
+ * agent does get the prompt, and it is passed like any other. Which CLIs'
373
+ * prompt events count as human is decided inside `captureIntent` (T4).
374
+ * Never throws; a failure is logged at info (see `readJevConfig`).
375
+ *
376
+ * `captureIntent` gets the whole normalized payload (`payload`): the prompt
377
+ * text is in a different field on some harnesses (Goose: `message`), and the
378
+ * marks that tell a human's prompt from a subagent's or an extension's are
379
+ * elsewhere in it. `prompt` is §7's original field, still read by the
380
+ * contract stub; T4's implementation reads `payload` and ignores it.
381
+ */
382
+ async function captureJevIntent(
383
+ canonicalEventType: string,
384
+ decision: "allow" | "deny" | "instruct",
385
+ parsed: Record<string, unknown>,
386
+ session: SessionMetadata,
387
+ cli: IntegrationType,
388
+ opts: EvaluateHookEventOptions | undefined,
389
+ ): Promise<void> {
390
+ if (canonicalEventType !== "UserPromptSubmit" || jevForcedOff(opts)) return;
391
+ try {
392
+ if (!(await readJevConfig())) return;
393
+ if (decision === "deny") {
394
+ const { ENFORCEMENT_CAPABILITY } = await import("./enforcement-capability");
395
+ if (ENFORCEMENT_CAPABILITY[cli]?.UserPromptSubmit === "block") return;
396
+ }
397
+ const { captureIntent } = await import("./semantic/intent");
398
+ // Built first, then passed: the one object satisfies §7's shape and T4's.
399
+ const event = {
400
+ eventType: canonicalEventType,
401
+ sessionId: session.sessionId,
402
+ transcriptPath: session.transcriptPath,
403
+ cli,
404
+ payload: parsed,
405
+ prompt: parsed.prompt,
406
+ };
407
+ captureIntent(event);
408
+ } catch (err) {
409
+ hookLogInfo(`Jev intent capture failed: ${err instanceof Error ? err.message : String(err)}`);
410
+ }
411
+ }
412
+
189
413
  /**
190
414
  * The core hook-evaluation logic, decoupled from process stdin/stdout so it
191
415
  * can be called repeatedly inside a long-lived process (the daemon's warm
@@ -561,6 +785,14 @@ export async function evaluateHookEvent(
561
785
  ...(cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudVersion: cloudManaged.version } : {}),
562
786
  ...(pack ? { packId: pack.id, packVersion: pack.version } : {}),
563
787
  });
788
+ // Whether Jev may clear this policy's verdict. The cloud assignment or
789
+ // the pack manifest declares it for those routes, the hook itself only
790
+ // for the user's own files; anything unclear registers as hard. Said
791
+ // aloud when a `reviewable` claim is refused and Jev is configured, so
792
+ // an author is not left wondering why Jev never clears it.
793
+ const authority = authorityDeclarationFor(hook, { cloudManaged, pack });
794
+ const refused = resolvePolicyAuthority(authority).downgraded;
795
+ if (refused) warnAuthority(`${registeredName} asks to be reviewable, but ${refused} — it stays hard`);
564
796
  registerPolicy(
565
797
  registeredName,
566
798
  hook.description ?? "",
@@ -572,6 +804,7 @@ export async function evaluateHookEvent(
572
804
  // configured. Matched by the pack's own name for the policy, which is
573
805
  // the name before the `pack/<id>@<version>/` prefix is applied.
574
806
  pack?.policies.find((p) => p.name === hook.name)?.params,
807
+ authority,
575
808
  );
576
809
  }
577
810
 
@@ -660,19 +893,40 @@ export async function evaluateHookEvent(
660
893
  );
661
894
  }
662
895
 
896
+ // Two-tier: when a Jev config exists and this is a gate, Jev's request
897
+ // starts HERE, before any regex policy runs, and evaluatePolicies combines
898
+ // the two (see semantic/combine.ts). Otherwise this is null and the call
899
+ // below is exactly the regex-only evaluation it always was.
900
+ const twoTier = await startTwoTier(canonicalEventType, parsed, session, cli, opts, activePause);
901
+ // On the two-tier path the registry is read for the activity row BEFORE
902
+ // evaluating, because evaluatePolicies may hand the registry back to the
903
+ // warm worker's queue (`releaseRegistry`) while it waits on Jev, and the
904
+ // next request's clearPolicies() could then run under this one. Same
905
+ // cached lookup, same answer: nothing between here and there registers.
906
+ const matchedBeforeRelease = twoTier
907
+ ? getPoliciesForEvent(canonicalEventType, parsed.tool_name as string | undefined).map((p) => p.name)
908
+ : null;
909
+
663
910
  // Evaluate policies (use canonical PascalCase event type)
664
- const result = await evaluatePolicies(canonicalEventType, parsed, session, config);
911
+ const result = twoTier
912
+ ? await evaluatePolicies(canonicalEventType, parsed, session, config, {
913
+ ...twoTier,
914
+ releaseRegistry: opts?.releaseRegistry,
915
+ })
916
+ : await evaluatePolicies(canonicalEventType, parsed, session, config);
665
917
  const durationMs = Math.round(performance.now() - startTime);
666
918
  hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
667
919
 
920
+ await captureJevIntent(canonicalEventType, result.decision, parsed, session, cli, opts);
921
+
668
922
  // Which policies actually ran for this event, regardless of how they
669
923
  // decided. `result.policyName` names only the decider — null on a plain
670
924
  // allow — so without this a row cannot tell "your policy ran and allowed"
671
925
  // from "no policy covers this event". The lookup is the same cached call
672
926
  // the evaluator already made, so it costs nothing.
673
- const matchedPolicies = getPoliciesForEvent(canonicalEventType, parsed.tool_name as string | undefined).map(
674
- (p) => p.name,
675
- );
927
+ const matchedPolicies =
928
+ matchedBeforeRelease ??
929
+ getPoliciesForEvent(canonicalEventType, parsed.tool_name as string | undefined).map((p) => p.name);
676
930
 
677
931
  // Persist activity to disk (visible in /policies activity tab)
678
932
  const activityEntry = {
@@ -686,6 +940,8 @@ export async function evaluateHookEvent(
686
940
  decision: result.decision,
687
941
  reason: result.reason,
688
942
  durationMs,
943
+ // How Jev took part (§7 fields). Absent unless the two-tier path ran.
944
+ ...(result.twoTier ? result.twoTier.activity : {}),
689
945
  sessionId: session.sessionId,
690
946
  transcriptPath: session.transcriptPath,
691
947
  cwd: session.cwd,
@@ -693,8 +949,10 @@ export async function evaluateHookEvent(
693
949
  hookEventName: session.hookEventName,
694
950
  // Attribution. A builtin is anything registered that is not in the map,
695
951
  // so its absence is meaningful rather than missing — but only when a
696
- // policy actually decided; a plain allow names nobody.
697
- ...(result.policyName
952
+ // policy actually decided; a plain allow names nobody. When Jev's own
953
+ // verdict decided, no registered policy did: `evaluator` + `jevDecision`
954
+ // say so, and claiming "builtin" here would be false.
955
+ ...(result.policyName && !result.twoTier?.decidedByJev
698
956
  ? (() => {
699
957
  const attribution = policyAttribution.get(result.policyName);
700
958
  return {
@@ -751,6 +1009,7 @@ export async function evaluateHookEvent(
751
1009
  convention_scope: conventionScope,
752
1010
  has_custom_params: hasCustomParams,
753
1011
  param_keys_overridden: paramKeysOverridden,
1012
+ ...jevTelemetry(result.twoTier?.activity),
754
1013
  });
755
1014
  // Deny/instruct is exactly the response the daemon's warm worker
756
1015
  // needs to return fast — a live network POST to PostHog on this path