failproofai 1.0.7-beta.2 → 1.0.7

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 (252) 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 +4 -4
  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 +7 -8
  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 +5 -7
  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 +11 -13
  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 +7 -8
  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 +6 -7
  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 +8 -41
  63. package/.next/standalone/.next/server/app/settings/page.js +9 -12
  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]__1j-zsg5._.js → [externals]__1_bftcl._.js} +1 -1
  67. package/.next/standalone/.next/server/chunks/{[externals]__19_pzeq._.js → [externals]__1msfs-h._.js} +1 -1
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l3yhx4._.js +2 -2
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1bf34x4._.js → [root-of-the-server]__1_r2rbg._.js} +7 -5
  71. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
  72. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/{_1q5i8mb._.js → _1c3k-8x._.js} +2 -2
  75. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
  76. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +2 -4
  79. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +4 -0
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  83. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l44ual._.js → [root-of-the-server]__0bd3mje._.js} +2 -2
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +5 -0
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +3 -0
  86. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1mf3zp6._.js → [root-of-the-server]__0cxe_2_._.js} +3 -3
  87. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +22 -0
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +5 -0
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +5 -0
  98. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/{_1w_5l7t._.js → _0h_douw._.js} +1 -1
  100. package/.next/standalone/.next/server/chunks/ssr/{_1mel6y1._.js → _1-i_gzc._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/{_1v-jvrv._.js → _166t73i._.js} +1 -1
  102. package/.next/standalone/.next/server/chunks/ssr/{_1q46vxx._.js → _1_qswah._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifp._.js → _1es2j7i._.js} +5 -5
  104. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +3 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +10 -0
  113. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.js +1 -1
  116. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  117. package/.next/standalone/.next/server/pages/404.html +1 -1
  118. package/.next/standalone/.next/server/pages/500.html +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/server-reference-manifest.json +23 -56
  121. package/.next/standalone/.next/static/chunks/094xgi4owxaqf.js +1 -0
  122. package/.next/standalone/.next/static/chunks/{129ag2bw93bdh.js → 0__8a7m868fvf.js} +1 -1
  123. package/.next/standalone/.next/static/chunks/{3ugmd_7dyn0id.js → 0o6qlkgubtoex.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/{0fqd7m_u81mi5.js → 13i7-9is-vhys.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/1rz20_pz828f3.js +6 -0
  126. package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +1 -0
  127. package/.next/standalone/.next/static/chunks/{2_pltstd8-xgs.js → 2klitrtzpaoe0.js} +1 -1
  128. package/.next/standalone/.next/static/chunks/{3brze37td_wnc.js → 2mdh397ghgnvv.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/2rshywgeqsyzk.css +2 -0
  130. package/.next/standalone/.next/static/chunks/3pzx4chkhko9k.js +1 -0
  131. package/.next/standalone/.next/static/chunks/3rh5o7e16irrm.js +69 -0
  132. package/.next/standalone/.next/static/chunks/{1qd741hzlmjbo.js → 43ufqrz8qo3h-.js} +1 -1
  133. package/.next/standalone/SECURITY.md +53 -0
  134. package/.next/standalone/app/actions/pack-actions.ts +0 -12
  135. package/.next/standalone/app/policies/hooks-client.tsx +0 -9
  136. package/.next/standalone/app/settings/page.tsx +1 -20
  137. package/.next/standalone/app/settings/settings-client.tsx +1 -27
  138. package/.next/standalone/app/settings/settings.css +0 -79
  139. package/.next/standalone/package.json +10 -10
  140. package/.next/standalone/sdk/typescript/CHANGELOG.md +15 -1
  141. package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +10 -30
  142. package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +3 -0
  143. package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +4 -16
  144. package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +3 -0
  145. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +13 -132
  146. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +4 -0
  147. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +4 -142
  148. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +4 -0
  149. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +1396 -1016
  150. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +9 -0
  151. package/.next/standalone/server.js +1 -1
  152. package/README.md +2 -2
  153. package/bin/failproofai.mjs +2 -115
  154. package/dist/cli.mjs +6543 -13872
  155. package/dist/index.js +1 -19
  156. package/dist/worker.mjs +2022 -8055
  157. package/package.json +10 -10
  158. package/pi-extension/index.ts +0 -11
  159. package/scripts/build-policy-pack.mjs +2 -53
  160. package/src/audit/features.ts +2 -3
  161. package/src/hooks/builtin-policies.ts +8 -177
  162. package/src/hooks/cloud-enrollment-cli.ts +1 -1
  163. package/src/hooks/cloud-managed-policies.ts +0 -22
  164. package/src/hooks/custom-hooks-loader.ts +7 -45
  165. package/src/hooks/custom-hooks-registry.ts +1 -45
  166. package/src/hooks/first-run-gate.ts +0 -5
  167. package/src/hooks/fp-home.ts +0 -23
  168. package/src/hooks/handler.ts +6 -265
  169. package/src/hooks/hook-activity-store.ts +1 -105
  170. package/src/hooks/hook-telemetry.ts +0 -41
  171. package/src/hooks/loader-utils.ts +0 -6
  172. package/src/hooks/manager.ts +1 -1
  173. package/src/hooks/pack-cli.ts +19 -412
  174. package/src/hooks/pack-manifest.ts +7 -479
  175. package/src/hooks/pack-store.ts +11 -157
  176. package/src/hooks/policy-catalog.ts +0 -204
  177. package/src/hooks/policy-evaluator.ts +796 -940
  178. package/src/hooks/policy-registry.ts +0 -25
  179. package/src/hooks/policy-types.ts +0 -126
  180. package/src/hooks/types.ts +1 -1
  181. package/src/hooks/worker-server.ts +26 -119
  182. package/src/index.ts +0 -6
  183. package/.next/standalone/.next/server/chunks/src_hooks_01frwmb._.js +0 -5
  184. package/.next/standalone/.next/server/chunks/src_hooks_18qtd42._.js +0 -3
  185. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +0 -3
  186. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +0 -4
  187. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +0 -4
  188. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +0 -3
  189. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +0 -22
  190. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +0 -4
  191. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +0 -3
  192. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qxnccm._.js +0 -5
  193. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +0 -4
  194. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s_yomn._.js +0 -4
  195. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0soxz2z._.js +0 -3
  196. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +0 -3
  197. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +0 -4
  198. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +0 -3
  199. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +0 -3
  200. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +0 -4
  201. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +0 -4
  202. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +0 -3
  203. package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +0 -3
  204. package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +0 -3
  205. package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +0 -3
  206. package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +0 -5
  207. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +0 -3
  209. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +0 -151
  210. package/.next/standalone/.next/server/chunks/ssr/src_hooks_095a_79._.js +0 -5
  211. package/.next/standalone/.next/server/chunks/ssr/src_hooks_15t8kqj._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/ssr/src_hooks_18k8rl0._.js +0 -12
  213. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1fm2w5z._.js +0 -3
  214. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1j0zy3v._.js +0 -3
  215. package/.next/standalone/.next/static/chunks/043j99m8ykg__.css +0 -2
  216. package/.next/standalone/.next/static/chunks/2c8j9l6j_b1ci.js +0 -1
  217. package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +0 -1
  218. package/.next/standalone/.next/static/chunks/3-k569wzcli8q.js +0 -1
  219. package/.next/standalone/.next/static/chunks/3otmypm6j_xfo.js +0 -6
  220. package/.next/standalone/.next/static/chunks/3yxro_r2_o9ad.js +0 -69
  221. package/.next/standalone/PROBE-FOLLOWUP.md +0 -186
  222. package/.next/standalone/app/actions/get-jev-config.ts +0 -424
  223. package/.next/standalone/app/actions/update-jev-config.ts +0 -420
  224. package/.next/standalone/app/components/jev-notices.tsx +0 -96
  225. package/.next/standalone/app/settings/jev-panel.tsx +0 -473
  226. package/src/hooks/effective-reviewers.ts +0 -79
  227. package/src/hooks/jev-activity.ts +0 -385
  228. package/src/hooks/jev-cli.ts +0 -1249
  229. package/src/hooks/policy-authority.ts +0 -333
  230. package/src/hooks/policy-reviewability.ts +0 -229
  231. package/src/hooks/semantic/combine.ts +0 -541
  232. package/src/hooks/semantic/compile.ts +0 -176
  233. package/src/hooks/semantic/decide.ts +0 -392
  234. package/src/hooks/semantic/envelope.ts +0 -1296
  235. package/src/hooks/semantic/evaluator.ts +0 -547
  236. package/src/hooks/semantic/facts.ts +0 -292
  237. package/src/hooks/semantic/intent.ts +0 -1190
  238. package/src/hooks/semantic/jev-client.ts +0 -643
  239. package/src/hooks/semantic/jev-config.ts +0 -594
  240. package/src/hooks/semantic/jev-review.ts +0 -374
  241. package/src/hooks/semantic/jev-stats.ts +0 -289
  242. package/src/hooks/semantic/jev-throttle.ts +0 -421
  243. package/src/hooks/semantic/pack-policies.ts +0 -251
  244. package/src/hooks/semantic/policies.ts +0 -596
  245. package/src/hooks/semantic/precondition-names.ts +0 -60
  246. package/src/hooks/semantic/preconditions.ts +0 -58
  247. package/src/hooks/semantic/redact.ts +0 -2910
  248. package/src/hooks/semantic/types.ts +0 -145
  249. package/src/hooks/semver-precedence.ts +0 -128
  250. /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_buildManifest.js +0 -0
  251. /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_clientMiddlewareManifest.js +0 -0
  252. /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_ssgManifest.js +0 -0
@@ -1,473 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * The Jev panel on /settings — the dashboard's half of `failproofai jev setup`.
5
- *
6
- * ## What it is for
7
- *
8
- * Jev is the optional second evaluation tier. It is on when, and only when, a
9
- * valid `~/.failproofai/jev.json` exists; without it the hook path is byte for
10
- * byte what it was before. So this panel has two jobs, in this order:
11
- *
12
- * 1. say whether it is on, in which mode, against which provider and model,
13
- * how much of the enabled policy set it is allowed to clear, and — once it
14
- * has run — how often it fell back to the regex engine. A panel that only
15
- * took input would leave "is this thing working" unanswerable from the
16
- * dashboard.
17
- * 2. take the endpoint and the token.
18
- *
19
- * Each fact is stated ONCE. The status block says nothing the form below it
20
- * already holds: the endpoint and the account id are form values, and printing
21
- * them above the fields put a per-account URL — and the id inside it — twice on
22
- * one screen. The config file's path and permission bits are gone for a
23
- * different reason: nobody repairs a 0644 from a browser, and the loader's own
24
- * refusal already names the file when it matters.
25
- *
26
- * It is a full-width cell in the same hairline console as the scheduled-audit
27
- * panel, using the same tokens, the same `.btn-press` action and the same
28
- * `toast()` on success. No new colour and no new control that did not already
29
- * exist on this page, except a `<select>` — five providers do not fit a switch.
30
- *
31
- * ## The token is write-only
32
- *
33
- * The field is always blank on load, whatever is stored. `JevSettingsView`
34
- * carries presence and nothing else, so the panel can say "configured" — or
35
- * that the key is read from the environment — and no more; the value itself
36
- * never leaves the machine's filesystem, and neither does a fragment of it.
37
- * Leaving the field empty on save KEEPS the key where it is — the stored one, or
38
- * the environment's for a config that takes it from there — which the server
39
- * decides, not this component.
40
- *
41
- * The stored MODEL is shown the same way when it does not look like a model id,
42
- * for the same reason: it is the one routing field someone can paste a key into.
43
- * The panel gets a `JevModelView`, never the string, so there is nothing here to
44
- * leak even by accident. It is display-only — the form does not offer the field,
45
- * and a save leaves whatever is stored alone.
46
- *
47
- * Client-side validation here is a convenience only. Every rule — the URL
48
- * scheme, plain http being refused outside shadow mode, cloudflare's account id
49
- * — is enforced server-side by the same `validateJevConfig` the loader runs.
50
- */
51
-
52
- import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
53
- import {
54
- getJevSettingsAction,
55
- type JevModelView,
56
- type JevSettingsView,
57
- } from "@/app/actions/get-jev-config";
58
- import {
59
- removeJevConfigAction,
60
- saveJevConfigAction,
61
- } from "@/app/actions/update-jev-config";
62
- import { toast } from "@/app/components/toast";
63
-
64
- const PROVIDERS = [
65
- { value: "typesafe", label: "typesafe" },
66
- { value: "openrouter", label: "openrouter" },
67
- { value: "vercel", label: "vercel ai gateway" },
68
- { value: "cloudflare", label: "cloudflare workers ai" },
69
- { value: "custom", label: "custom endpoint" },
70
- ] as const;
71
-
72
- const MODES = [
73
- { value: "enforce", label: "enforce — jev's verdict counts" },
74
- { value: "shadow", label: "shadow — log only, regex decides" },
75
- ] as const;
76
-
77
- /** "24h" / "7d" / "30m", for the stats window. */
78
- function fmtWindow(ms: number): string {
79
- const h = ms / 3_600_000;
80
- if (Number.isInteger(h) && h >= 1) return h % 24 === 0 ? `${h / 24}d` : `${h}h`;
81
- return `${Math.max(1, Math.round(ms / 60_000))}m`;
82
- }
83
-
84
- /** A rate as a whole percent. 0.0417 → "4%". */
85
- function fmtRate(rate: number): string {
86
- return `${Math.round(rate * 100)}%`;
87
- }
88
-
89
- /**
90
- * The model row. `withheld` is the case worth having a row for at all: the file
91
- * holds something in that slot that does not look like a model id, which is
92
- * what a key pasted one field off looks like, so it is described and not
93
- * printed — and the server never sent it here to print. The loader's own reason
94
- * and its fix are already on the page above, in `view.problem`.
95
- */
96
- function fmtModel(model: JevModelView): string {
97
- switch (model.kind) {
98
- case "id":
99
- return model.id;
100
- case "withheld":
101
- return "set to something that is not a model id — not shown here, in case it is a key";
102
- default:
103
- return "the provider's default";
104
- }
105
- }
106
-
107
- interface FormState {
108
- provider: string;
109
- baseUrl: string;
110
- accountId: string;
111
- mode: string;
112
- }
113
-
114
- function formFrom(view: JevSettingsView): FormState {
115
- return {
116
- provider: view.provider ?? "typesafe",
117
- baseUrl: view.baseUrl,
118
- accountId: view.accountId,
119
- mode: view.mode,
120
- };
121
- }
122
-
123
- /** One labelled control, laid out like the interval row above it. */
124
- function Field({
125
- id,
126
- label,
127
- hint,
128
- children,
129
- }: {
130
- id: string;
131
- label: string;
132
- hint?: string;
133
- children: ReactNode;
134
- }) {
135
- return (
136
- <div className="set-field">
137
- <label className="set-field-label" htmlFor={id}>
138
- {label}
139
- </label>
140
- {children}
141
- {hint ? <span className="set-hint">{hint}</span> : null}
142
- </div>
143
- );
144
- }
145
-
146
- export default function JevPanel({ initial }: { initial: JevSettingsView | null }) {
147
- const [view, setView] = useState<JevSettingsView | null>(initial);
148
- const [form, setForm] = useState<FormState>(
149
- initial ? formFrom(initial) : { provider: "typesafe", baseUrl: "", accountId: "", mode: "enforce" },
150
- );
151
- const [token, setToken] = useState("");
152
- const [busy, setBusy] = useState(false);
153
- const [problem, setProblem] = useState<string | null>(null);
154
- /**
155
- * The server's `needsToken`: the one refusal whose remedy is this field, so it
156
- * is marked rather than leaving the person to match the message to a control.
157
- * The refusals that no token can settle deliberately do not set it.
158
- */
159
- const [needsToken, setNeedsToken] = useState(false);
160
- const mounted = useRef(true);
161
- /**
162
- * Whether the user has touched the form since the last read. A focus refresh
163
- * must not overwrite a half-typed endpoint with what is still on disk — the
164
- * page re-reads on every `visibilitychange`, which includes the tab hide that
165
- * happens when somebody alt-tabs to their password manager mid-edit.
166
- */
167
- const dirty = useRef(false);
168
-
169
- const reload = useCallback(async () => {
170
- try {
171
- const next = await getJevSettingsAction();
172
- if (!mounted.current) return;
173
- setView(next);
174
- if (!dirty.current) setForm(formFrom(next));
175
- } catch {
176
- // Leave what is on screen: it describes real machine state, and blanking
177
- // it would report something less true than what is already there.
178
- }
179
- }, []);
180
-
181
- useEffect(() => {
182
- mounted.current = true;
183
- void reload();
184
- return () => {
185
- mounted.current = false;
186
- };
187
- }, [reload]);
188
-
189
- useEffect(() => {
190
- // Same reason as the scheduled-audit panel: `failproofai jev setup` in a
191
- // terminal changes the same file, and a page left open would otherwise keep
192
- // showing the old state until it was reloaded by hand.
193
- const onFocus = () => void reload();
194
- window.addEventListener("focus", onFocus);
195
- document.addEventListener("visibilitychange", onFocus);
196
- return () => {
197
- window.removeEventListener("focus", onFocus);
198
- document.removeEventListener("visibilitychange", onFocus);
199
- };
200
- }, [reload]);
201
-
202
- const set = useCallback(<K extends keyof FormState>(key: K, value: FormState[K]) => {
203
- dirty.current = true;
204
- setForm((f) => ({ ...f, [key]: value }));
205
- }, []);
206
-
207
- const onSave = useCallback(async () => {
208
- setBusy(true);
209
- setProblem(null);
210
- try {
211
- // The form does not offer a model, so it says nothing about one: the
212
- // server keeps a stored model when the provider is unchanged, and drops it
213
- // for the new provider's default when it is not — the same rule `jev setup`
214
- // applies. `JevConfigInput` has no model field for this to get wrong.
215
- const res = await saveJevConfigAction({
216
- provider: form.provider,
217
- baseUrl: form.baseUrl,
218
- accountId: form.accountId,
219
- mode: form.mode,
220
- token,
221
- });
222
- if (!res.ok) {
223
- setProblem(res.problem);
224
- setNeedsToken(res.needsToken === true);
225
- return;
226
- }
227
- dirty.current = false;
228
- setNeedsToken(false);
229
- setToken("");
230
- setView(res.view);
231
- setForm(formFrom(res.view));
232
- toast(res.view.on ? "jev is on." : "saved.");
233
- } catch {
234
- setProblem("could not save that.");
235
- } finally {
236
- setBusy(false);
237
- }
238
- }, [form, token]);
239
-
240
- const onRemove = useCallback(async () => {
241
- setBusy(true);
242
- setProblem(null);
243
- setNeedsToken(false);
244
- try {
245
- const res = await removeJevConfigAction();
246
- if (!res.ok) {
247
- setProblem(res.problem);
248
- return;
249
- }
250
- dirty.current = false;
251
- setToken("");
252
- setView(res.view);
253
- setForm(formFrom(res.view));
254
- toast("jev is off. hooks run the regex policies.");
255
- } catch {
256
- setProblem("could not turn that off.");
257
- } finally {
258
- setBusy(false);
259
- }
260
- }, []);
261
-
262
- const configured = view !== null && view.status !== "absent";
263
- const stored = view?.token;
264
- const tokenHint = needsToken
265
- ? "needed for this save — type the token for this endpoint"
266
- : stored
267
- ? stored.source === "env"
268
- ? "configured, read from the environment — leave blank to keep it"
269
- : // Presence and the keep rule, which is everything this field's reader
270
- // has to decide. Naming the last four characters of the stored key
271
- // here put that fragment on the page a second time, next to the input
272
- // it would be re-typed into.
273
- "configured — leave blank to keep it"
274
- : // The file names the environment as the key's source and the variable is
275
- // unset in this process. Leaving the field blank keeps that arrangement,
276
- // which is not the same thing as "there is no key" — the endpoint and the
277
- // mode are editable from here without one.
278
- view?.status === "key-missing"
279
- ? "read from the environment, which is not set in this shell — leave blank to keep it that way"
280
- : "sent as a bearer token; stored owner-only at 0600";
281
-
282
- return (
283
- <div className="set-cell set-jev">
284
- <h2 className="set-cell-title">jev · semantic evaluation</h2>
285
-
286
- <div className="set-row set-row-main">
287
- <div className="set-row-copy">
288
- <span className={view?.on ? "set-strong" : "set-dim"}>
289
- {view === null
290
- ? "reading…"
291
- : view.on
292
- ? `on. every judged command is also asked of ${view.provider}.`
293
- : "off. hooks run the regex policies exactly as before."}
294
- </span>
295
- <span className="set-dim">
296
- {view?.on
297
- ? view.mode === "shadow"
298
- ? "shadow: jev's answers are logged, the regex result is what gets enforced."
299
- : "enforce: jev's answers can clear a reviewable deny."
300
- : "a second opinion on the commands the regex policies flag. needs your own endpoint and token."}
301
- </span>
302
- </div>
303
- </div>
304
-
305
- {/* Where requests GO is not in this block, deliberately. The editable
306
- field below holds it, and for Cloudflare that URL is
307
- `/accounts/<id>/ai/run` — so a read-only row above the form printed the
308
- same per-account address, and the account id inside it, a second time
309
- on one screen. The path of the config file and its mode were here too;
310
- nobody acts on either from a browser. */}
311
- {view && configured && (
312
- <dl className="set-how-list">
313
- <div className="set-how-row">
314
- <dt className="set-how-label">model</dt>
315
- <dd className="set-how-body">{fmtModel(view.model)}</dd>
316
- </div>
317
- {/* Presence, and where it came from. Not four characters of it: that
318
- is a recognisable fragment of a live credential on a page with no
319
- authentication, and it tells the reader nothing they cannot get by
320
- re-pasting the key. The server no longer sends it either. */}
321
- <div className="set-how-row">
322
- <dt className="set-how-label">token</dt>
323
- <dd className="set-how-body">
324
- {stored
325
- ? stored.source === "env"
326
- ? "from FAILPROOFAI_JEV_API_KEY"
327
- : "configured"
328
- : "none stored"}
329
- </dd>
330
- </div>
331
- {view.reviewable && (
332
- <div className="set-how-row">
333
- <dt className="set-how-label">reviewable</dt>
334
- {/* The count, in the server's words — the same sentence
335
- `failproofai jev status` prints, because the two surfaces must
336
- not disagree about what Jev is allowed to clear. */}
337
- <dd className="set-how-body">{view.reviewable.summary}</dd>
338
- </div>
339
- )}
340
- {view.stats && view.stats.total > 0 && (
341
- <div className="set-how-row">
342
- <dt className="set-how-label">fallbacks</dt>
343
- {/* One string, not an interpolated fragment: a sentence split
344
- across text nodes is a sentence a screen reader and a test both
345
- have to reassemble. */}
346
- <dd className="set-how-body">
347
- {`${fmtRate(view.stats.fallbackRate)} of ${view.stats.total} calls in the last ${fmtWindow(view.stats.windowMs)} fell back to the regex engine.`}
348
- </dd>
349
- </div>
350
- )}
351
- </dl>
352
- )}
353
-
354
- {view?.problem && (
355
- <p className="set-warn">
356
- {view.problem}
357
- {view.fix ? ` — ${view.fix}` : ""}
358
- </p>
359
- )}
360
-
361
- {/* Jev is on, answering, and cannot clear a single verdict. Nothing else
362
- on this page shows it: a policy set with no authority marks behaves
363
- exactly like a healthy one until you notice that nothing is ever
364
- cleared. The server sends the cause and the remedy together. */}
365
- {view?.reviewable?.problem && <p className="set-warn">{view.reviewable.problem}</p>}
366
-
367
- <div className="set-rule" />
368
-
369
- <div className="set-jev-fields">
370
- <Field id="jev-provider" label="provider">
371
- <select
372
- id="jev-provider"
373
- className="set-select"
374
- value={form.provider}
375
- disabled={busy}
376
- onChange={(e) => set("provider", e.target.value)}
377
- >
378
- {PROVIDERS.map((p) => (
379
- <option key={p.value} value={p.value}>
380
- {p.label}
381
- </option>
382
- ))}
383
- </select>
384
- </Field>
385
-
386
- <Field
387
- id="jev-url"
388
- label="endpoint url"
389
- hint={
390
- form.provider === "custom"
391
- ? "required — https, or http to localhost in shadow mode"
392
- : "leave blank for the provider's own api"
393
- }
394
- >
395
- <input
396
- id="jev-url"
397
- type="text"
398
- className="set-text"
399
- inputMode="url"
400
- autoComplete="off"
401
- spellCheck={false}
402
- placeholder={form.provider === "custom" ? "https://…" : "the provider's own api"}
403
- value={form.baseUrl}
404
- disabled={busy}
405
- onChange={(e) => set("baseUrl", e.target.value)}
406
- />
407
- </Field>
408
-
409
- {form.provider === "cloudflare" && (
410
- <Field id="jev-account" label="account id" hint="32 lowercase hex characters">
411
- <input
412
- id="jev-account"
413
- type="text"
414
- className="set-text"
415
- autoComplete="off"
416
- spellCheck={false}
417
- value={form.accountId}
418
- disabled={busy}
419
- onChange={(e) => set("accountId", e.target.value)}
420
- />
421
- </Field>
422
- )}
423
-
424
- <Field id="jev-token" label="token" hint={tokenHint}>
425
- <input
426
- id="jev-token"
427
- type="password"
428
- className="set-text"
429
- autoComplete="off"
430
- spellCheck={false}
431
- placeholder={stored ? "•••• kept" : ""}
432
- value={token}
433
- disabled={busy}
434
- aria-invalid={needsToken || undefined}
435
- onChange={(e) => {
436
- dirty.current = true;
437
- setToken(e.target.value);
438
- }}
439
- />
440
- </Field>
441
-
442
- <Field id="jev-mode" label="mode">
443
- <select
444
- id="jev-mode"
445
- className="set-select"
446
- value={form.mode}
447
- disabled={busy}
448
- onChange={(e) => set("mode", e.target.value)}
449
- >
450
- {MODES.map((m) => (
451
- <option key={m.value} value={m.value}>
452
- {m.label}
453
- </option>
454
- ))}
455
- </select>
456
- </Field>
457
- </div>
458
-
459
- {problem && <p className="set-warn">{problem}</p>}
460
-
461
- <div className="set-actions set-jev-actions">
462
- <button type="button" className="btn btn-press" disabled={busy} onClick={() => void onSave()}>
463
- {busy ? "[ saving… ]" : configured ? "[ save changes ]" : "[ turn jev on ]"}
464
- </button>
465
- {configured && (
466
- <button type="button" className="set-link" disabled={busy} onClick={() => void onRemove()}>
467
- turn jev off
468
- </button>
469
- )}
470
- </div>
471
- </div>
472
- );
473
- }
@@ -1,79 +0,0 @@
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
- }