failproofai 1.0.11 → 1.0.12-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +23 -23
  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/build-manifest.json +3 -3
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +3 -3
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  24. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/invite/route.js +5 -6
  26. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js +24 -24
  28. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/status/route.js +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +4 -5
  32. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +6 -7
  34. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route.js +6 -7
  36. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/status/route.js +5 -6
  38. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +2 -2
  40. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  42. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/audit/page.js +4 -4
  44. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/index.html +1 -1
  47. package/.next/standalone/.next/server/app/index.rsc +3 -3
  48. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  49. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  50. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  51. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  52. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  53. package/.next/standalone/.next/server/app/page.js +2 -2
  54. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  57. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  58. package/.next/standalone/.next/server/app/policies/page.js +7 -7
  59. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  62. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/page.js +7 -7
  64. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +8 -8
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  73. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  74. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  75. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +3 -3
  78. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  79. package/.next/standalone/.next/server/app/settings/page.js +10 -10
  80. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/chunks/{[externals]__0qkp92tjfvw8r._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
  83. package/.next/standalone/.next/server/chunks/[externals]__1x24dzcbbq6-g._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02vous7mpi2xm._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03i5o4a840guz._.js +3 -0
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0__a_je8fsn65._.js +3 -0
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_hsnj_a2mvl_._.js +3 -0
  88. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1izwqj_ij_9x_._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-oi4gdtmlo5._.js +3 -0
  90. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0swiyzbmvja2d._.js → [root-of-the-server]__0t9ufnv5hi2hq._.js} +5 -5
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wg9dk8x8bnau._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12kq3x-h7t7i1._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a5zkndjwlc13._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1r7kbfkiw2xmu._.js → [root-of-the-server]__1aaw59g-5uty7._.js} +12 -12
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hqpg6vgmfexv._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/_09dz7xva4ce_2._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +1 -1
  102. package/.next/standalone/.next/server/chunks/_1vslkoe35wok6._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/{_1ixl-kxq_ctsa._.js → _1y590mjzpkbos._.js} +2 -2
  104. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
  106. package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01j58mdcdw0vn._.js +3 -0
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +3 -0
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_a8z2ldp2v3n._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__0b78b0j8iyv6l._.js} +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ffu2-38y03ic._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g8b0kyorya-t._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__075uro8t92s0_._.js → [root-of-the-server]__0pwj2h2efe5f-._.js} +3 -3
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zn1d4blhd3bu._.js +3 -0
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17r4zrwanhp6r._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1lnfv02ucky7m._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mc9-zlnh656_._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1s2-ck3k3rsoo._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1l1qgu5mfapkh._.js → [root-of-the-server]__1vx7o8ukefudh._.js} +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_1_ozgifqv1ssi._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _1a3tubds7s7ow._.js} +10 -9
  138. package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
  140. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
  142. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +1 -1
  144. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1f_ymmaonkksc.js → node_modules_html-to-image_es_index_1i8oid1gc7s1f.js} +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
  149. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
  150. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
  152. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
  153. package/.next/standalone/.next/server/middleware-build-manifest.js +23 -23
  154. package/.next/standalone/.next/server/middleware.js +2 -2
  155. package/.next/standalone/.next/server/pages/404.html +1 -1
  156. package/.next/standalone/.next/server/pages/500.html +1 -1
  157. package/.next/standalone/.next/server/preview-props.json +3 -3
  158. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  159. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  160. package/.next/standalone/.next/static/chunks/{0frxv4_m-m2u5.js → 063al_q720fkj.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/0_zfad4co-t2z.js +1 -0
  162. package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +1 -0
  163. package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +1 -0
  164. package/.next/standalone/.next/static/chunks/{37stoe-_yd852.js → 18433qz-8njv3.js} +1 -1
  165. package/.next/standalone/.next/static/chunks/{1kwj8uqxuv85t.js → 1b32-afqacaen.js} +1 -1
  166. package/.next/standalone/.next/static/chunks/{44a7k2_62qc-9.js → 1p-hsi58i-6h0.js} +2 -2
  167. package/.next/standalone/.next/static/chunks/{302llgh97ce6w.js → 2-tf6_7u623kd.js} +1 -1
  168. package/.next/standalone/.next/static/chunks/{1_c0dc7zqj1n7.js → 24w3x2da9sk49.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/2tjfp9dirxnku.js +1 -0
  170. package/.next/standalone/.next/static/chunks/{16z6_d7sp61jm.js → 3-pku9qwv1yfu.js} +1 -1
  171. package/.next/standalone/.next/static/chunks/{15rm15u5a6dua.js → 42e8zab314654.js} +1 -1
  172. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +41 -0
  173. package/.next/standalone/fp-cloud-cli/README.md +42 -1
  174. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +117 -12
  175. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +285 -19
  176. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
  177. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +170 -6
  178. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
  179. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +259 -21
  180. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
  181. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
  182. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +17 -9
  183. package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +692 -0
  184. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
  185. package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
  186. package/.next/standalone/fp-cloud-cli/uv.lock +6 -6
  187. package/.next/standalone/hermes-plugin/README.md +6 -4
  188. package/.next/standalone/hermes-plugin/__init__.py +16 -0
  189. package/.next/standalone/hermes-plugin/client.py +5 -1
  190. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  191. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  192. package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/context.js +72 -0
  193. package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/diag.js +84 -0
  194. package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/metrics.js +50 -0
  195. package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/propagation.js +80 -0
  196. package/.next/standalone/node_modules/@opentelemetry/api/build/src/api/trace.js +70 -0
  197. package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/context-helpers.js +52 -0
  198. package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/internal/baggage-impl.js +44 -0
  199. package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/internal/symbol.js +12 -0
  200. package/.next/standalone/node_modules/@opentelemetry/api/build/src/baggage/utils.js +41 -0
  201. package/.next/standalone/node_modules/@opentelemetry/api/build/src/context/NoopContextManager.js +27 -0
  202. package/.next/standalone/node_modules/@opentelemetry/api/build/src/context/context.js +52 -0
  203. package/.next/standalone/node_modules/@opentelemetry/api/build/src/context-api.js +16 -0
  204. package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/ComponentLogger.js +47 -0
  205. package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/consoleLogger.js +73 -0
  206. package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/internal/logLevelLogger.js +34 -0
  207. package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag/types.js +33 -0
  208. package/.next/standalone/node_modules/@opentelemetry/api/build/src/diag-api.js +20 -0
  209. package/.next/standalone/node_modules/@opentelemetry/api/build/src/index.js +71 -0
  210. package/.next/standalone/node_modules/@opentelemetry/api/build/src/internal/global-utils.js +60 -0
  211. package/.next/standalone/node_modules/@opentelemetry/api/build/src/internal/semver.js +111 -0
  212. package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/Metric.js +18 -0
  213. package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/NoopMeter.js +118 -0
  214. package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics/NoopMeterProvider.js +20 -0
  215. package/.next/standalone/node_modules/@opentelemetry/api/build/src/metrics-api.js +17 -0
  216. package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation/NoopTextMapPropagator.js +23 -0
  217. package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation/TextMapPropagator.js +36 -0
  218. package/.next/standalone/node_modules/@opentelemetry/api/build/src/propagation-api.js +17 -0
  219. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NonRecordingSpan.js +58 -0
  220. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NoopTracer.js +68 -0
  221. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/NoopTracerProvider.js +21 -0
  222. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/ProxyTracer.js +46 -0
  223. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/ProxyTracerProvider.js +46 -0
  224. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/SamplingResult.js +33 -0
  225. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/context-utils.js +71 -0
  226. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/tracestate-impl.js +94 -0
  227. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/tracestate-validators.js +35 -0
  228. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/internal/utils.js +16 -0
  229. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/invalid-span-constants.js +25 -0
  230. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/span_kind.js +38 -0
  231. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/spancontext-utils.js +66 -0
  232. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/status.js +29 -0
  233. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace/trace_flags.js +18 -0
  234. package/.next/standalone/node_modules/@opentelemetry/api/build/src/trace-api.js +17 -0
  235. package/.next/standalone/node_modules/@opentelemetry/api/build/src/version.js +10 -0
  236. package/.next/standalone/node_modules/@opentelemetry/api/package.json +96 -0
  237. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  238. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +2 -2
  239. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +2 -2
  240. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +1 -1
  241. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  242. package/.next/standalone/node_modules/next/dist/server/app-render/async-local-storage.js +1 -1
  243. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  244. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  245. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  246. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  247. package/.next/standalone/node_modules/next/dist/server/lib/generate-agent-files.js +3 -3
  248. package/.next/standalone/node_modules/next/dist/server/lib/router-server.js +1 -1
  249. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  250. package/.next/standalone/node_modules/next/dist/server/lib/trace/tracer.js +1 -1
  251. package/.next/standalone/node_modules/next/dist/server/typescript/rules/config.js +2 -2
  252. package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +1 -1
  253. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  254. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  255. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  256. package/.next/standalone/node_modules/next/package.json +11 -11
  257. package/.next/standalone/package.json +11 -11
  258. package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
  259. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  260. package/.next/standalone/sdk/python/pyproject.toml +19 -0
  261. package/.next/standalone/sdk/python/uv.lock +520 -301
  262. package/.next/standalone/sdk/typescript/CHANGELOG.md +13 -0
  263. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +4 -13
  264. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +2 -1
  265. package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +4 -13
  266. package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +3 -0
  267. package/.next/standalone/sdk/typescript/package-lock.json +178 -162
  268. package/.next/standalone/sdk/typescript/package.json +1 -1
  269. package/.next/standalone/sdk/typescript/src/version.ts +1 -1
  270. package/.next/standalone/server.js +1 -1
  271. package/bin/failproofai.mjs +17 -1
  272. package/dist/cli.mjs +2866 -1124
  273. package/dist/worker.mjs +1450 -145
  274. package/hermes-plugin/README.md +6 -4
  275. package/hermes-plugin/__init__.py +16 -0
  276. package/hermes-plugin/client.py +5 -1
  277. package/hermes-plugin/plugin.yaml +1 -1
  278. package/package.json +11 -11
  279. package/src/hooks/agent-roster.ts +172 -0
  280. package/src/hooks/agent-targets.ts +60 -0
  281. package/src/hooks/cloud-connection.ts +97 -27
  282. package/src/hooks/cloud-enrollment-cli.ts +7 -4
  283. package/src/hooks/cloud-managed-policies.ts +380 -20
  284. package/src/hooks/cloud-policy-errors.ts +481 -0
  285. package/src/hooks/config-status.ts +1 -1
  286. package/src/hooks/custom-hooks-loader.ts +26 -3
  287. package/src/hooks/daemon-client.ts +9 -4
  288. package/src/hooks/daemon-service.ts +2 -1
  289. package/src/hooks/effective-reviewers.ts +22 -1
  290. package/src/hooks/fp-home.ts +6 -0
  291. package/src/hooks/handler.ts +235 -18
  292. package/src/hooks/health-probe.ts +15 -0
  293. package/src/hooks/integrations.ts +18 -11
  294. package/src/hooks/jev-cli.ts +197 -0
  295. package/src/hooks/jev-cloud-connection.ts +92 -0
  296. package/src/hooks/manager.ts +122 -16
  297. package/src/hooks/pack-failclosed.ts +2 -0
  298. package/src/hooks/pack-manifest.ts +8 -1
  299. package/src/hooks/policy-evaluator.ts +5 -1
  300. package/src/hooks/policy-registry.ts +3 -2
  301. package/src/hooks/policy-reviewability.ts +21 -5
  302. package/src/hooks/semantic/cloud-jev-health.ts +227 -0
  303. package/src/hooks/semantic/cloud-jev.ts +677 -0
  304. package/src/hooks/semantic/jev-client.ts +18 -1
  305. package/src/hooks/semantic/jev-config.ts +173 -3
  306. package/src/hooks/semantic/jev-review.ts +129 -10
  307. package/src/hooks/semantic/pack-policies.ts +2 -1
  308. package/src/hooks/semantic/types.ts +8 -0
  309. package/src/hooks/session-pause-cli.ts +1 -1
  310. package/src/hooks/worker-server.ts +5 -1
  311. package/.next/standalone/.next/server/chunks/[externals]__0hjaw70fchx66._.js +0 -3
  312. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07l68zv0hqdiu._.js +0 -3
  313. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0car70pylmvor._.js +0 -3
  314. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0u3i025_setk_._.js +0 -3
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10a2f6s636yc7._.js +0 -3
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qt5u58xrcuk6._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_077n_4ip5__fu._.js +0 -3
  318. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0mqvvxcn6v0op._.js +0 -3
  319. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_0pb7ds5g0vzgn._.js +0 -3
  320. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1gasm5xkeqmm3._.js +0 -3
  321. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1q1heqmkhh8l4._.js +0 -3
  322. package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
  323. package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
  324. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0888k4s6spfmh._.js +0 -3
  325. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_bflu046ep5j._.js +0 -3
  326. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_lbstb5x2t15._.js +0 -3
  327. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0fdwgktjfhedo._.js +0 -3
  328. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s4deza787asx._.js +0 -3
  329. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__18qe-hu0gwqm4._.js +0 -3
  330. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_p_9xe520fex._.js +0 -3
  331. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fmhm8_s9i4re._.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1g76awtnmimd1._.js +0 -3
  333. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pczevlpk5lxh._.js +0 -3
  334. package/.next/standalone/.next/server/chunks/ssr/_0dc-of_bn0cim._.js +0 -3
  335. package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
  336. package/.next/standalone/.next/static/chunks/0m-tv1y6ghhtk.js +0 -1
  337. package/.next/standalone/.next/static/chunks/0ol2gj4ntfn56.js +0 -1
  338. package/.next/standalone/.next/static/chunks/3hj5_7i3nw4zj.js +0 -1
  339. package/.next/standalone/.next/static/chunks/3iyoapeoq_hup.js +0 -1
  340. /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_buildManifest.js +0 -0
  341. /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_clientMiddlewareManifest.js +0 -0
  342. /package/.next/standalone/.next/static/{kEFP3ybXwZdriNEQmWwlH → xq8UgF5yFxty1VWxY2hyn}/_ssgManifest.js +0 -0
@@ -165,23 +165,25 @@ decision, because otherwise corrupted retry state could block a turn forever.
165
165
  ## Local protocol
166
166
 
167
167
  Requests and responses use the existing length-prefixed failproofaid Unix
168
- socket protocol, version 1.
168
+ socket protocol, version 2. A v1 daemon must be upgraded before it can safely
169
+ evaluate agent/profile-scoped policies.
169
170
 
170
171
  ```json
171
172
  {
172
173
  "type": "policyEvaluation",
173
- "protocolVersion": 1,
174
+ "protocolVersion": 2,
174
175
  "integration": "hermes",
175
176
  "event": "pre_tool_call",
176
177
  "payload": {},
177
- "cwd": "/workspace/project"
178
+ "cwd": "/workspace/project",
179
+ "agentSettingsPath": "/path/to/the/hermes/profile"
178
180
  }
179
181
  ```
180
182
 
181
183
  ```json
182
184
  {
183
185
  "type": "policyResult",
184
- "protocolVersion": 1,
186
+ "protocolVersion": 2,
185
187
  "decision": "instruct",
186
188
  "policyNames": ["custom/approved-write-route"],
187
189
  "reason": "Use the approved write route.",
@@ -102,6 +102,21 @@ def _profile_name() -> str:
102
102
  return "default"
103
103
 
104
104
 
105
+ def _profile_settings_path(profile: str) -> str | None:
106
+ """Evidence of the profile running this plugin, not a transcript guess."""
107
+ configured = os.environ.get("HERMES_HOME", "").strip()
108
+ if configured:
109
+ selected = Path(configured).expanduser() / "config.yaml"
110
+ elif profile == "default":
111
+ selected = Path.home() / ".hermes" / "config.yaml"
112
+ elif re.fullmatch(r"[A-Za-z0-9._-]{1,80}", profile):
113
+ selected = Path.home() / ".hermes" / "profiles" / profile / "config.yaml"
114
+ else:
115
+ return None
116
+ # An unresolvable profile does not acquire some other profile's ID.
117
+ return str(selected.absolute()) if selected.is_file() else None
118
+
119
+
105
120
  def _string(value: object) -> str:
106
121
  return value if isinstance(value, str) else ""
107
122
 
@@ -660,6 +675,7 @@ class FailproofAIPlugin:
660
675
  event=event,
661
676
  payload=payload,
662
677
  cwd=cwd,
678
+ agent_settings_path=_profile_settings_path(self.profile),
663
679
  connect_timeout_ms=self.connect_timeout_ms,
664
680
  evaluation_timeout_ms=timeout_ms or self.evaluation_timeout_ms,
665
681
  )
@@ -11,7 +11,7 @@ from dataclasses import dataclass
11
11
  from pathlib import Path
12
12
  from typing import Any, Mapping, Sequence
13
13
 
14
- PROTOCOL_VERSION = 1
14
+ PROTOCOL_VERSION = 2
15
15
  MAX_FRAME_BYTES = 16 * 1024 * 1024
16
16
  MAX_REQUEST_BYTES = 1024 * 1024
17
17
 
@@ -75,6 +75,7 @@ def evaluate_policy(
75
75
  event: str,
76
76
  payload: Mapping[str, Any],
77
77
  cwd: str | None,
78
+ agent_settings_path: str | None = None,
78
79
  connect_timeout_ms: int = 250,
79
80
  evaluation_timeout_ms: int = 12_000,
80
81
  ) -> PolicyVerdict:
@@ -85,6 +86,9 @@ def evaluate_policy(
85
86
  "event": event,
86
87
  "payload": dict(payload),
87
88
  "cwd": cwd,
89
+ # An unknown profile is explicit. Omitting this field would let a new
90
+ # daemon's warm worker attribute the call to its own Hermes home.
91
+ "agentSettingsPath": agent_settings_path or "",
88
92
  }
89
93
  try:
90
94
  body = json.dumps(
@@ -1,5 +1,5 @@
1
1
  name: failproofai
2
- version: 1.0.11
2
+ version: 1.0.12-beta.1
3
3
  description: Real-time FailproofAI policy enforcement for Hermes
4
4
  manifest_version: 2
5
5
  api_version: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.11",
3
+ "version": "1.0.12-beta.1",
4
4
  "description": "Observability and enforcement for AI agent harnesses. 40 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs",
@@ -81,7 +81,7 @@
81
81
  "access": "public"
82
82
  },
83
83
  "devDependencies": {
84
- "@anthropic-ai/sdk": "^0.133.0",
84
+ "@anthropic-ai/sdk": "^0.135.0",
85
85
  "@mdx-js/mdx": "^3.1.1",
86
86
  "@tailwindcss/postcss": "^4.3.1",
87
87
  "@tanstack/react-virtual": "^3.14.10",
@@ -117,7 +117,7 @@
117
117
  "nanoid": "3.3.18",
118
118
  "postcss": "8.5.26",
119
119
  "eslint-plugin-react-hooks": "7.0.1",
120
- "vite": "8.0.16",
120
+ "vite": "8.1.6",
121
121
  "undici": "7.29.1",
122
122
  "brace-expansion": "5.0.12",
123
123
  "sharp": "0.35.5",
@@ -125,15 +125,15 @@
125
125
  "browserslist": "4.28.8"
126
126
  },
127
127
  "optionalDependencies": {
128
- "@failproofai/failproofaid-linux-x64": "1.0.11",
129
- "@failproofai/failproofaid-linux-arm64": "1.0.11",
130
- "@failproofai/failproofaid-darwin-x64": "1.0.11",
131
- "@failproofai/failproofaid-darwin-arm64": "1.0.11"
128
+ "@failproofai/failproofaid-linux-x64": "1.0.12-beta.1",
129
+ "@failproofai/failproofaid-linux-arm64": "1.0.12-beta.1",
130
+ "@failproofai/failproofaid-darwin-x64": "1.0.12-beta.1",
131
+ "@failproofai/failproofaid-darwin-arm64": "1.0.12-beta.1"
132
132
  },
133
133
  "failproofaidBinaries": {
134
- "linux-x64": "49b11d95dafeb2dfecc3b92d7e18243e6b93706ebc2baf44fee9d8875ee1f90a",
135
- "linux-arm64": "720702120b1a2427e8ccf2d53f2e782f1f549c4b2da8880832af5b468eeefab6",
136
- "darwin-x64": "8861a67d2155831fa6a3699c59ebcfe7eeabf6c6234ef57b2165b52b78e7e3ca",
137
- "darwin-arm64": "4acf268dd92bc928d29feb90a3ca7162bf7505af781ae51f56e7b3899420327a"
134
+ "linux-x64": "df15e65b60066248038c8ac6d423bffcb0e38637e8a42fa22250afa111fd46ac",
135
+ "linux-arm64": "59d7ae758c8981c5e0a89eb64be18ebd278213525dd4447891a51be6bb018ed9",
136
+ "darwin-x64": "b0d3d39abaa02ed0a14dddc68277975a7a23e200830b7edbffdedde3af81ce73",
137
+ "darwin-arm64": "a6d343b5926e84c0eb437af307f85b69b3128f94c6e34acbf9d0b13a7c7e9d63"
138
138
  }
139
139
  }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Read the daemon-owned profile roster. The telemetry agent_id and transcript
3
+ * path are not profile evidence. A scoped Cloud assignment is withheld unless
4
+ * this invocation resolves to one of the daemon's recorded config paths.
5
+ */
6
+ import { lstatSync, readFileSync, statSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { dirname, resolve } from "node:path";
9
+ import { agentRosterFile } from "./fp-home";
10
+ import { validAgentIdentity, type AgentIdentity } from "./agent-targets";
11
+ import type { HookScope, IntegrationType } from "./types";
12
+
13
+ /** `MAX_AGENTS` / `MAX_ROSTER_BYTES` in the daemon's `agent_roster.rs`. */
14
+ export const MAX_ROSTER_AGENTS = 256;
15
+ export const MAX_ROSTER_BYTES = 4_000_000;
16
+
17
+ const USER_SETTINGS: Partial<Record<IntegrationType, string>> = {
18
+ claude: ".claude/settings.json",
19
+ codex: ".codex/hooks.json",
20
+ copilot: ".copilot/hooks/failproofai.json",
21
+ cursor: ".cursor/hooks.json",
22
+ opencode: ".config/opencode/opencode.json",
23
+ pi: ".pi/agent/settings.json",
24
+ factory: ".factory/hooks.json",
25
+ devin: ".config/devin/config.json",
26
+ antigravity: ".gemini/config/hooks.json",
27
+ goose: ".agents/plugins/failproofai/hooks/hooks.json",
28
+ };
29
+
30
+ const PROJECT_SETTINGS: Partial<Record<IntegrationType, string[]>> = {
31
+ claude: [".claude/settings.local.json", ".claude/settings.json"],
32
+ codex: [".codex/hooks.json"],
33
+ copilot: [".github/hooks/failproofai.json"],
34
+ cursor: [".cursor/hooks.json"],
35
+ opencode: [".opencode/opencode.json"],
36
+ pi: [".pi/settings.json"],
37
+ factory: [".factory/hooks.json"],
38
+ devin: [".devin/config.json"],
39
+ antigravity: [".agents/hooks.json"],
40
+ goose: [".agents/plugins/failproofai/hooks/hooks.json"],
41
+ };
42
+
43
+ function installedHookAt(path: string, cli: IntegrationType): boolean {
44
+ try {
45
+ const stat = statSync(path);
46
+ if (!stat.isFile() || stat.size > 131_072) return false;
47
+ const text = readFileSync(path, "utf8");
48
+ if (text.includes("failproofai")) return true;
49
+ // Pi's project entry is portable and relative to `.pi/settings.json`:
50
+ // `../pi-extension` contains no product name. Missing it here let a
51
+ // simultaneously installed user hook masquerade as this project's Pi.
52
+ if (cli !== "pi") return false;
53
+ const config: unknown = JSON.parse(text);
54
+ if (!config || typeof config !== "object" || Array.isArray(config)) return false;
55
+ const packages = (config as { packages?: unknown }).packages;
56
+ return Array.isArray(packages) && packages.some(
57
+ (entry) => typeof entry === "string" && /(?:^|\/)pi-extension\/?$/.test(entry),
58
+ );
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /** Called in the originating hook process; the worker's env may be different. */
65
+ export function runtimeAgentSettingsPath(
66
+ cli: IntegrationType,
67
+ cwd?: string,
68
+ userHome = homedir(),
69
+ hookScope?: HookScope,
70
+ ): string | null {
71
+ if (cli === "hermes") {
72
+ if (hookScope && hookScope !== "user") return null;
73
+ const home = process.env.HERMES_HOME;
74
+ return resolve(home?.trim() ? home : resolve(userHome, ".hermes"), "config.yaml");
75
+ }
76
+ if (cli === "openclaw") {
77
+ if (hookScope && hookScope !== "user") return null;
78
+ const config = process.env.OPENCLAW_CONFIG_PATH;
79
+ if (config?.trim()) return resolve(config);
80
+ const state = process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_HOME;
81
+ return resolve(state?.trim() ? state : resolve(userHome, ".openclaw"), "openclaw.json");
82
+ }
83
+ const relative = USER_SETTINGS[cli];
84
+ if (!relative) return null;
85
+ const override = cli === "codex" ? process.env.CODEX_HOME
86
+ : cli === "claude" ? process.env.CLAUDE_CONFIG_DIR : undefined;
87
+ const user = resolve(override?.trim() ? override : userHome, override?.trim()
88
+ ? cli === "codex" ? "hooks.json" : "settings.json"
89
+ : relative);
90
+ if (hookScope === "user") return user;
91
+
92
+ const scopeFiles = hookScope === "local"
93
+ ? cli === "claude" ? [".claude/settings.local.json"] : []
94
+ : hookScope === "project"
95
+ ? (PROJECT_SETTINGS[cli] ?? []).filter((name) => name !== ".claude/settings.local.json")
96
+ : PROJECT_SETTINGS[cli] ?? [];
97
+ const matches = new Set<string>();
98
+ if (hookScope === undefined && installedHookAt(user, cli)) matches.add(user);
99
+ let dir = resolve(cwd ?? process.cwd());
100
+ const userRoot = resolve(userHome);
101
+ for (let depth = 0; depth < 16 && dir !== userRoot; depth++) {
102
+ for (const candidate of scopeFiles) {
103
+ const path = resolve(dir, candidate);
104
+ if (installedHookAt(path, cli)) matches.add(path);
105
+ }
106
+ const parent = dirname(dir);
107
+ if (parent === dir) break;
108
+ dir = parent;
109
+ }
110
+ // No source hint can tell WHICH installed hook fired if both user and
111
+ // project/local scopes contain us. A guessed profile would let an exact
112
+ // assignment match another installation: withhold it instead.
113
+ if (matches.size > 1) return null;
114
+ if (hookScope === "project" || hookScope === "local") return matches.values().next().value ?? null;
115
+ return matches.values().next().value ?? user;
116
+ }
117
+
118
+ export function readRuntimeAgentIdentity(cli: IntegrationType, settingsPath: string | null): AgentIdentity | null {
119
+ if (!settingsPath) return null;
120
+ try {
121
+ const path = agentRosterFile();
122
+ const stat = lstatSync(path);
123
+ if (!stat.isFile() || (stat.mode & 0o077) !== 0 || stat.size > MAX_ROSTER_BYTES) return null;
124
+ const roster: unknown = JSON.parse(readFileSync(path, "utf8"));
125
+ if (!roster || typeof roster !== "object" || Array.isArray(roster)) return null;
126
+ const data = roster as Record<string, unknown>;
127
+ if (data.schemaVersion !== 1 || !Array.isArray(data.agents) || data.agents.length > MAX_ROSTER_AGENTS) return null;
128
+ const absolute = resolve(settingsPath);
129
+ const agent = data.agents.find((entry: unknown) => {
130
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
131
+ const record = entry as Record<string, unknown>;
132
+ return record.integration === cli && record.settingsPath === absolute &&
133
+ validAgentIdentity({ integration: record.integration, instanceId: record.instanceId });
134
+ });
135
+ const instanceId = (agent as Record<string, unknown> | undefined)?.instanceId;
136
+ return validAgentIdentity({ integration: cli, instanceId })
137
+ ? { integration: cli, instanceId: instanceId as string }
138
+ : null;
139
+ } catch {
140
+ // Unavailable, malformed or old roster: never infer a profile from cwd,
141
+ // transcript, or the display name and widen a scoped deployment.
142
+ return null;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Display labels from the daemon's roster, by `agt_` id (`claude / .claude-work`
148
+ * style: integration, then the profile label). For listings only; never an
149
+ * identity — `readRuntimeAgentIdentity` is the only thing that decides which
150
+ * profile a hook is. Empty when the roster cannot be read.
151
+ */
152
+ export function readRosterLabels(): Map<string, string> {
153
+ const out = new Map<string, string>();
154
+ try {
155
+ const path = agentRosterFile();
156
+ const stat = lstatSync(path);
157
+ if (!stat.isFile() || (stat.mode & 0o077) !== 0 || stat.size > MAX_ROSTER_BYTES) return out;
158
+ const roster: unknown = JSON.parse(readFileSync(path, "utf8"));
159
+ if (!roster || typeof roster !== "object" || Array.isArray(roster)) return out;
160
+ const data = roster as Record<string, unknown>;
161
+ if (data.schemaVersion !== 1 || !Array.isArray(data.agents) || data.agents.length > MAX_ROSTER_AGENTS) return out;
162
+ for (const entry of data.agents) {
163
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
164
+ const record = entry as Record<string, unknown>;
165
+ if (typeof record.instanceId !== "string" || typeof record.profileLabel !== "string" || record.profileLabel === "") continue;
166
+ out.set(record.instanceId, record.profileLabel.slice(0, 80));
167
+ }
168
+ } catch {
169
+ // No roster: the bare ids are shown.
170
+ }
171
+ return out;
172
+ }
@@ -0,0 +1,60 @@
1
+ /** Agent identity for enforcement, separate from the project-derived telemetry agent_id. */
2
+ import { INTEGRATION_TYPES, type IntegrationType } from "./types";
3
+
4
+ export interface AgentIdentity {
5
+ integration: IntegrationType;
6
+ instanceId: string;
7
+ }
8
+
9
+ export interface AgentTarget {
10
+ integration: IntegrationType;
11
+ instanceId?: string;
12
+ }
13
+
14
+ const INSTANCE_ID_RE = /^agt_[0-9a-f]{16,32}$/;
15
+ const integrations = new Set<string>(INTEGRATION_TYPES);
16
+
17
+ export function validAgentIdentity(value: unknown): value is AgentIdentity {
18
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
19
+ const agent = value as Record<string, unknown>;
20
+ return Object.keys(agent).every((key) => key === "integration" || key === "instanceId") &&
21
+ typeof agent.integration === "string" && integrations.has(agent.integration) &&
22
+ typeof agent.instanceId === "string" && INSTANCE_ID_RE.test(agent.instanceId);
23
+ }
24
+
25
+ /** Null/absent means all; a malformed selector is never silently treated as all. */
26
+ export function parseAgentTargets(value: unknown, schemaVersion: number): AgentTarget[] | undefined {
27
+ if (value === undefined || value === null) return undefined;
28
+ if (schemaVersion < 3 || !Array.isArray(value) || value.length < 1 || value.length > 32) {
29
+ throw new Error("invalid agentTargets in cloud-managed active manifest");
30
+ }
31
+ const seen = new Set<string>();
32
+ return value.map((raw) => {
33
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
34
+ throw new Error("invalid agentTargets in cloud-managed active manifest");
35
+ }
36
+ const entry = raw as Record<string, unknown>;
37
+ if (Object.keys(entry).some((key) => key !== "integration" && key !== "instanceId") ||
38
+ typeof entry.integration !== "string" || !integrations.has(entry.integration) ||
39
+ (entry.instanceId !== undefined &&
40
+ (typeof entry.instanceId !== "string" || !INSTANCE_ID_RE.test(entry.instanceId)))) {
41
+ throw new Error("invalid agentTargets in cloud-managed active manifest");
42
+ }
43
+ const key = `${entry.integration}\0${entry.instanceId ?? ""}`;
44
+ if (seen.has(key)) throw new Error("duplicate agentTargets in cloud-managed active manifest");
45
+ seen.add(key);
46
+ return {
47
+ integration: entry.integration as IntegrationType,
48
+ ...(entry.instanceId !== undefined ? { instanceId: entry.instanceId as string } : {}),
49
+ };
50
+ });
51
+ }
52
+
53
+ /** An unknown runtime identity cannot widen any scoped assignment. */
54
+ export function agentTargetsMatch(targets: readonly AgentTarget[] | undefined, agent: AgentIdentity | null): boolean {
55
+ if (targets === undefined) return true;
56
+ if (!validAgentIdentity(agent)) return false;
57
+ return targets.some((target) =>
58
+ target.integration === agent.integration &&
59
+ (target.instanceId === undefined || target.instanceId === agent.instanceId));
60
+ }
@@ -63,7 +63,7 @@ import {
63
63
  type CollectorSettings,
64
64
  } from "./collector-config";
65
65
  import { recoverCredentialRefusals } from "./credential-recovery";
66
- import { clearActiveCloudManagedPolicies } from "./cloud-managed-policies";
66
+ import { clearActiveCloudManagedPolicies, readCloudJevState } from "./cloud-managed-policies";
67
67
  import { rmSync } from "node:fs";
68
68
  import { backfillRequestPath } from "./backfill-request";
69
69
 
@@ -180,11 +180,18 @@ export interface JevConnectOutcome {
180
180
  * is stored; `jev setup --provider failproofai` switches Jev on.
181
181
  */
182
182
  optIn?: true;
183
+ /**
184
+ * A `--no-transcripts` connection removed this `jev.json`: it named the
185
+ * FailproofAI Cloud provider and was not switched off (the rule `config
186
+ * --disconnect` uses), so Jev through FailproofAI Cloud stays off here.
187
+ * Set with `optIn`.
188
+ */
189
+ removedCloudConfig?: string;
183
190
  /**
184
191
  * A `--no-transcripts` connection found a `jev.json` already in place that
185
- * keeps Jev running through FailproofAI Cloud, in this mode. It is left
186
- * alone like any existing file (never overwritten), but a connection that
187
- * asked for decisions only is told that Jev still sends more than that.
192
+ * keeps Jev running through FailproofAI Cloud, in this mode, and could not
193
+ * remove it (see `removedCloudConfig`). A connection that asked for
194
+ * decisions only is told that Jev still sends more than that.
188
195
  */
189
196
  stillOn?: "observe" | "enforce";
190
197
  /**
@@ -316,7 +323,12 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
316
323
  prev.ingest ? { token: prev.ingest.key, url: prev.ingest.url } : undefined,
317
324
  prev.jev ? { token: prev.jev.key, url: prev.jev.url } : undefined,
318
325
  ].some((slot) => slot && (slot.token !== input.token || !sameOrigin(slot.url)));
319
- const orgSwitch = hadConnection && (
326
+ // Both sides name their organisation: that alone decides. The same org on
327
+ // a new URL (http → https, a new host for the same Cloud) is a reconnect,
328
+ // not a switch — clearing its deployment would drop enforcement until the
329
+ // next poll, and "Switched from X to X" says nothing true.
330
+ const sameKnownOrg = Boolean(prev.org?.id && known?.orgId && prev.org.id === known.orgId);
331
+ const orgSwitch = hadConnection && !sameKnownOrg && (
320
332
  (prev.org?.id && known?.orgId && prev.org.id !== known.orgId) ||
321
333
  ((!prev.org?.id || !known?.orgId) && keyChanged) ||
322
334
  (previousPolicy && !sameOrigin(previousPolicy.url)) ||
@@ -425,23 +437,37 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
425
437
  writeJevCloudCredential({ url: new URL(input.url).origin, key: input.token });
426
438
  // Loaded here and nowhere earlier: a connect whose key does not carry Jev
427
439
  // pulls in none of the Jev modules.
428
- const { cloudJevRunningMode, existingJevConfig, writeCloudJevConfigIfAbsent } = await import("./jev-cloud-connection");
440
+ const { cloudJevRunningMode, existingJevConfig, removeCloudJevConfig, writeCloudJevConfig } = await import(
441
+ "./jev-cloud-connection"
442
+ );
429
443
  if (input.sessions === true) {
430
- outcome.jev = { ok: true, config: writeCloudJevConfigIfAbsent(input.url) };
444
+ outcome.jev = { ok: true, config: writeCloudJevConfig(input.url) };
431
445
  } else {
432
446
  // `--no-transcripts` never switches Jev on (the user's decision). Jev
433
447
  // sends each checked tool call and the recent prompt to FailproofAI
434
448
  // Cloud, and someone who just asked for decisions only has not asked
435
449
  // for that. The key is stored all the same, so opting in later is one
436
- // command with no key to paste; jev.json is not written. One that is
437
- // already there is somebody's decision and is reported as ever — and
438
- // when it keeps Jev on through FailproofAI Cloud, that is said too, or
439
- // "Decisions only." would be the last word while Jev keeps sending.
440
- const existing = existingJevConfig(input.url);
441
- const stillOn = existing ? cloudJevRunningMode() : null;
442
- outcome.jev = existing
443
- ? { ok: true, config: existing, ...(stillOn ? { stillOn } : {}) }
444
- : { ok: true, optIn: true };
450
+ // command with no key to paste; jev.json is not written.
451
+ //
452
+ // And a jev.json naming the FailproofAI Cloud provider — left by an
453
+ // earlier connect with transcripts on — is removed, by exactly the rule
454
+ // `config --disconnect` uses (`removeCloudJevConfig`: the Cloud's file,
455
+ // not switched off). Kept, it went on sending this machine's tool calls
456
+ // to FailproofAI Cloud with no Cloud mode set (e2e-c10 O2, user
457
+ // decision). A BYOK file is its owner's and stays, reported as ever; so
458
+ // does a Cloud file switched off, the owner's opt-out.
459
+ const removal = removeCloudJevConfig();
460
+ if (removal.status === "removed") {
461
+ outcome.jev = { ok: true, optIn: true, removedCloudConfig: removal.path };
462
+ } else {
463
+ const existing = existingJevConfig(input.url);
464
+ // Still sending (the removal failed): said, or "Decisions only."
465
+ // would be the last word while Jev keeps sending.
466
+ const stillOn = existing ? cloudJevRunningMode() : null;
467
+ outcome.jev = existing
468
+ ? { ok: true, config: existing, ...(stillOn ? { stillOn } : {}) }
469
+ : { ok: true, optIn: true };
470
+ }
445
471
  }
446
472
  } else if (known) {
447
473
  // Introspect ANSWERED, and the key does not carry Jev. This connection
@@ -491,6 +517,14 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
491
517
  * this machine now, and — the one case worth a second line — why an existing
492
518
  * `jev.json` was not touched.
493
519
  */
520
+ function safeCloudJev(): ReturnType<typeof readCloudJevState> {
521
+ try {
522
+ return readCloudJevState();
523
+ } catch {
524
+ return { jevMode: null, deployment: null, errors: [] };
525
+ }
526
+ }
527
+
494
528
  export function jevLines(outcome: ConnectOutcome): string[] {
495
529
  const jev = outcome.jev;
496
530
  if (!jev) return [];
@@ -503,6 +537,11 @@ export function jevLines(outcome: ConnectOutcome): string[] {
503
537
  if (!jev.ok) {
504
538
  return [` Jev not through FailproofAI Cloud: ${jev.reason ?? `that key does not carry \`${PERMISSION_JEV}\``}.`];
505
539
  }
540
+ if (jev.optIn && jev.removedCloudConfig) {
541
+ return [
542
+ ` Jev removed ${jev.removedCloudConfig}, which sent each checked tool call and the recent prompt to FailproofAI Cloud: Jev via FailproofAI Cloud stays off on this machine (--no-transcripts). To switch it on: \`failproofai jev setup --provider failproofai\`.`,
543
+ ];
544
+ }
506
545
  if (jev.optIn) {
507
546
  // One line, and nothing that reads as "on": this connection asked for
508
547
  // decisions only, and Jev would send more than that.
@@ -511,20 +550,51 @@ export function jevLines(outcome: ConnectOutcome): string[] {
511
550
  ];
512
551
  }
513
552
  const config = jev.config;
514
- if (!config || config.status === "written") {
515
- // Plain http (loopback only) is accepted in observe mode and nowhere else, so
516
- // the enforce command would be refused there, by the CLI and the dashboard.
517
- const plainHttp = config?.status === "written" && new URL(config.baseUrl).protocol === "http:";
553
+ if (!config || config.status === "written" || config.status === "repointed") {
554
+ // Plain http (loopback only) is accepted in observe mode and nowhere else:
555
+ // in enforce mode — set here, or by FailproofAI Cloud — Jev is OFF on this
556
+ // machine rather than enforcing, by the CLI and the dashboard alike.
557
+ const plainHttp = config !== undefined && new URL(config.baseUrl).protocol === "http:";
558
+ const httpLine = " Over plain http Jev runs only in observe mode: enforce (set here or by FailproofAI Cloud) turns it off on this machine. For enforce, reconnect with `failproofai config --token <key> --url https://…`.";
559
+ // What happened to jev.json, said first on a re-point; the mode lines
560
+ // below are the same for both (N1).
561
+ const lead =
562
+ config?.status === "repointed" ? [` Jev ${config.path} now points here instead of ${config.from}.`] : [];
563
+ const indent = lead.length > 0 ? " " : " Jev ";
564
+ // FailproofAI Cloud already set this machine's Jev mode (a reconnect, or a
565
+ // deployment that arrived first): that mode and Cloud's own checks decide,
566
+ // not the local jev.json or the packs installed here.
567
+ const cloudJev = safeCloudJev();
568
+ if (cloudJev.jevMode !== null) {
569
+ if (cloudJev.jevMode === "off") return [...lead, `${indent}off on this machine: FailproofAI Cloud set its Jev mode to off.`];
570
+ if (plainHttp && cloudJev.jevMode === "enforce") {
571
+ return [
572
+ ...lead,
573
+ `${indent}off on this machine: FailproofAI Cloud set enforce, and over plain http Jev runs only in observe mode. For enforce, reconnect with \`failproofai config --token <key> --url https://…\`.`,
574
+ ];
575
+ }
576
+ return [
577
+ ...lead,
578
+ `${indent}on through FailproofAI Cloud, mode ${cloudJev.jevMode} (set by FailproofAI Cloud); the Jev checks it deployed here run on FailproofAI Cloud.`,
579
+ ];
580
+ }
581
+ // No Cloud deployment has been read on this machine yet (a first connect,
582
+ // or the first after a disconnect): FailproofAI Cloud's Jev mode, when it
583
+ // sets one, arrives with that deployment and overrides jev.json — so the
584
+ // local mode is said as the fallback it is, not as a promise (N3).
585
+ const localMode = config?.status === "repointed" ? config.mode : "observe";
586
+ if (localMode === "off") {
587
+ return [...lead, `${indent}switched off in ${config?.path ?? "jev.json"} (mode off). FailproofAI Cloud's Jev mode, if it sets one, arrives with its first deployment. To turn it on: \`failproofai jev setup --mode observe\`.`];
588
+ }
518
589
  return [
519
- ` Jev on through FailproofAI Cloud, in observe mode: logged, not enforced (${config?.path ?? "jev.json"}).`,
520
- // The package ships no Jev checks, so "on" asks nothing until a pack
521
- // gives it some — said here, where the user just turned it on.
590
+ ...lead,
591
+ `${indent}configured through FailproofAI Cloud; waiting for FailproofAI Cloud's Jev mode, which arrives with this machine's first deployment (within ~30 s) and decides. Until then, and if Cloud sets none: ${localMode} (${config?.path ?? "jev.json"}). Check with \`failproofai jev status\`.`,
592
+ // The package ships no Jev checks, so local "on" asks nothing until a
593
+ // pack gives it some. Checks FailproofAI Cloud deploys run on Cloud.
522
594
  ...(jevChecksDeclared()
523
595
  ? []
524
- : [" It has no checks installed yet, so it asks nothing. To enable it: `failproofai policies add FailproofAI/jev-policies`."]),
525
- plainHttp
526
- ? " Enforce needs an https FailproofAI Cloud URL: reconnect with `failproofai config --token <key> --url https://…`. Over plain http Jev stays in observe mode."
527
- : " Enforce it with `failproofai jev setup --mode enforce`, or from the dashboard.",
596
+ : [" No Jev checks are installed on this machine; checks FailproofAI Cloud deploys here run on Cloud. To add local ones: `failproofai policies add FailproofAI/jev-policies`."]),
597
+ ...(plainHttp ? [httpLine] : []),
528
598
  ];
529
599
  }
530
600
  if (config.status === "kept") {
@@ -409,12 +409,13 @@ export function runDisconnectCommand(): CommandResult {
409
409
  // machine that had deliberately left its organisation went on being governed
410
410
  // by whatever deployment was current when it left, indefinitely, while
411
411
  // `--status` reported it as unconnected.
412
- const stoppedManaged = clearActiveCloudManagedPolicies();
413
412
  // Back to OSS. Leaving mode = "cloud" with no credentials would describe a
414
413
  // machine that does not exist, and every cloud code path keys off this flag
415
414
  // rather than off "is a token lying around" precisely so that a disconnected
416
415
  // machine is provably silent instead of silent-by-happenstance.
417
416
  updateConfig({ mode: "oss" });
417
+ const sharedPolicyRoot = process.env.FAILPROOFAI_CLOUD_POLICY_DIR !== undefined;
418
+ const stoppedManaged = clearActiveCloudManagedPolicies();
418
419
 
419
420
  const jevLines =
420
421
  jevConfig.status === "removed"
@@ -450,10 +451,12 @@ export function runDisconnectCommand(): CommandResult {
450
451
  const lines = [
451
452
  "Disconnected from FailproofAI Cloud.",
452
453
  "",
453
- stoppedManaged
454
- ? " Cloud-managed policies stop being enforced and stop being refreshed.\n" +
454
+ stoppedManaged || (sharedPolicyRoot && !!existing)
455
+ ? " Cloud-managed policies stop being enforced and stop being refreshed, and any Jev mode\n" +
456
+ " FailproofAI Cloud set goes with them (its Jev checks never ran on this machine).\n" +
455
457
  " Local builtin, custom and convention policies are unaffected."
456
458
  : " Local builtin, custom and convention policies are unaffected.",
459
+ ...(sharedPolicyRoot ? [" An overridden Cloud policy directory was left untouched; OSS mode ignores its old deployment."] : []),
457
460
  ...jevLines,
458
461
  ];
459
462
  if (removedIngest) {
@@ -589,4 +592,4 @@ export function connectionStatusLines(daemonStatus = daemonServiceStatus): strin
589
592
  kitRows(report.rows, opts),
590
593
  report.warnings.length > 0 ? warning(report.warnings, opts) : null,
591
594
  );
592
- }
595
+ }