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
@@ -13,7 +13,7 @@ import json as _json
13
13
  import math
14
14
  import re
15
15
  from datetime import datetime, timezone
16
- from typing import Any, List, Optional, Sequence
16
+ from typing import Any, Dict, List, Optional, Sequence
17
17
 
18
18
  import typer
19
19
  from rich.box import ROUNDED, SIMPLE_HEAD
@@ -490,7 +490,7 @@ _TOP_LEVEL_GROUPS = [
490
490
  ]),
491
491
  ("ENFORCE", [
492
492
  ("policies", "Write cloud-managed policies.", "list show publish test compose enable disable delete"),
493
- ("fleet", "Deploy policies to machines.", "list show deploy diff history rollback rename"),
493
+ ("fleet", "Deploy policies to machines.", "list show deploy jev-mode diff history rollback rename"),
494
494
  ("guardrails", "What enforcement actually blocked.", "summary timeline"),
495
495
  ]),
496
496
  ("MANAGE", [
@@ -5918,6 +5918,10 @@ def _policy_cell(ref: Any) -> Text:
5918
5918
  return t
5919
5919
 
5920
5920
 
5921
+ #: A policy version's kind, as `fp policies list` colours it.
5922
+ _KIND_STYLE = {"regex": theme.TEXT_DIM, "jev": theme.ACCENT, "both": theme.AMBER}
5923
+
5924
+
5921
5925
  def render_policies(items: Sequence[Any]) -> None:
5922
5926
  """``fp policies`` — every published VERSION, newest of each policy first.
5923
5927
 
@@ -5938,11 +5942,25 @@ def render_policies(items: Sequence[Any]) -> None:
5938
5942
  state = Text("archived", style=theme.FAINT)
5939
5943
  elif p.disabled:
5940
5944
  state = Text("disabled", style=theme.AMBER)
5945
+ kind = getattr(p, "kind", None) or "regex"
5946
+ names = list(getattr(p, "jev_names", None) or [])
5947
+ desc = Text()
5948
+ if names:
5949
+ # The checks first: for a `jev` policy they ARE the policy, and a
5950
+ # long description would otherwise push them off the ellipsis.
5951
+ desc.append(", ".join(names), style=theme.ACCENT)
5952
+ chars = getattr(p, "jev_chars", None)
5953
+ if chars is not None:
5954
+ desc.append(f" {chars:,} chars", style=theme.LABEL)
5955
+ if p.description:
5956
+ desc.append(" · ", style=theme.FAINT)
5957
+ desc.append(p.description or "", style=theme.TEXT_DIM)
5941
5958
  rows.append([
5942
5959
  Text(p.id, style=theme.TEXT),
5943
5960
  Text(f"v{p.version}", style=theme.TEXT_DIM),
5961
+ Text(kind, style=_KIND_STYLE.get(kind, theme.TEXT_DIM)),
5944
5962
  state,
5945
- Text(p.description or "", style=theme.TEXT_DIM),
5963
+ desc,
5946
5964
  ])
5947
5965
  distinct = len({p.id for p in items})
5948
5966
  title = Text()
@@ -5952,7 +5970,7 @@ def render_policies(items: Sequence[Any]) -> None:
5952
5970
  if len(rows) != distinct:
5953
5971
  title.append(" · ", style=theme.FAINT)
5954
5972
  title.append(f"{len(rows)} versions", style=theme.LABEL)
5955
- render_list_panel("policies", header=["policy", "version", "state", "description"],
5973
+ render_list_panel("policies", header=["policy", "version", "kind", "state", "description"],
5956
5974
  rows=rows, days=set(), order=None,
5957
5975
  empty_message="no policies published — `fp policies publish <id> <file>`",
5958
5976
  last_col="ellipsis", title=title)
@@ -5974,17 +5992,41 @@ def render_policy_published(p: Any, *, carriers: Optional[dict] = None,
5974
5992
  """
5975
5993
  line1 = Text(p.id, style=f"bold {theme.TEXT}")
5976
5994
  line1.append(f" v{p.version}", style=f"bold {theme.ACCENT}")
5995
+ kind = getattr(p, "kind", None) or "regex"
5996
+ line1.append(f" {kind}", style=_KIND_STYLE.get(kind, theme.TEXT_DIM))
5977
5997
  body = [line1]
5978
5998
  if p.description:
5979
5999
  body.append(Text(p.description, style=theme.TEXT))
6000
+ names = list(getattr(p, "jev_names", None) or [])
6001
+ if names:
6002
+ checks = Text("jev checks ", style=theme.LABEL)
6003
+ checks.append(", ".join(names), style=theme.ACCENT)
6004
+ chars = getattr(p, "jev_chars", None)
6005
+ if chars is not None:
6006
+ # What these checks take of the ONE Jev question budget a machine
6007
+ # has for every check it asks — Cloud's and its installed packs'.
6008
+ checks.append(" · ", style=theme.FAINT)
6009
+ checks.append(f"{chars:,} question chars", style=theme.LABEL)
6010
+ body.append(checks)
6011
+ if kind == "both":
6012
+ # Derived by the server from the kind, never sent: said so the author
6013
+ # knows what the JavaScript's verdict now is.
6014
+ body.append(Text("its JavaScript is reviewable by exactly these checks", style=theme.LABEL))
5980
6015
  body.append(Text())
5981
6016
 
5982
6017
  meta = Text()
5983
6018
  if source_bytes:
5984
6019
  meta.append(f"{source_bytes:,} bytes", style=theme.LABEL)
5985
6020
  meta.append(" · ", style=theme.FAINT)
5986
- meta.append("sha256 ", style=theme.LABEL)
5987
- meta.append((p.sha256 or "")[:12] + "…", style=theme.TEXT_DIM)
6021
+ if p.sha256:
6022
+ meta.append("sha256 ", style=theme.LABEL)
6023
+ meta.append((p.sha256 or "")[:12] + "…", style=theme.TEXT_DIM)
6024
+ semantic_sha = getattr(p, "semantic_sha256", None)
6025
+ if semantic_sha:
6026
+ if p.sha256:
6027
+ meta.append(" · ", style=theme.FAINT)
6028
+ meta.append("jev sha256 ", style=theme.LABEL)
6029
+ meta.append(semantic_sha[:12] + "…", style=theme.TEXT_DIM)
5988
6030
  body.append(meta)
5989
6031
  body.append(Text())
5990
6032
 
@@ -6053,8 +6095,7 @@ def render_fleet(machines: Sequence[Any]) -> None:
6053
6095
  Text(seen or "never", style=theme.FAINT if stale else theme.TEXT_DIM),
6054
6096
  Text(f"{m.event_count:,}" if m.event_count else "—",
6055
6097
  style=theme.TEXT_DIM if m.event_count else theme.FAINT),
6056
- Text("drifted" if m.drifted else ("ok" if m.deployed else "—"),
6057
- style=theme.AMBER if m.drifted else (theme.SUCCESS if m.deployed else theme.FAINT)),
6098
+ _machine_state(m),
6058
6099
  ])
6059
6100
  title = Text()
6060
6101
  title.append("fleet", style=f"bold {theme.ACCENT}")
@@ -6067,6 +6108,18 @@ def render_fleet(machines: Sequence[Any]) -> None:
6067
6108
  empty_message="no machines have checked in yet", title=title)
6068
6109
 
6069
6110
 
6111
+ def _machine_state(m: Any) -> Text:
6112
+ """`fp fleet list`'s state cell. Reported policy errors outrank drift: a
6113
+ machine that says it cannot apply part of its set is not enforcing it,
6114
+ whichever generation it is on."""
6115
+ errors = getattr(m, "policy_errors", None) or []
6116
+ if errors:
6117
+ n = len(errors)
6118
+ return Text(f"{n} error{'s' if n != 1 else ''}", style=theme.ERROR)
6119
+ return Text("drifted" if m.drifted else ("ok" if m.deployed else "—"),
6120
+ style=theme.AMBER if m.drifted else (theme.SUCCESS if m.deployed else theme.FAINT))
6121
+
6122
+
6070
6123
  def _compact_age(ms: Optional[int]) -> str:
6071
6124
  """`5h`, `7d`, `just now` — the column form of `_epoch_age`.
6072
6125
 
@@ -6097,7 +6150,33 @@ def _epoch_age(ms: Optional[int]) -> str:
6097
6150
  return _relative_age(datetime.fromtimestamp(ms / 1000, tz=timezone.utc).isoformat())
6098
6151
 
6099
6152
 
6100
- def render_machine_policies(machine_id: str, dep: Any, machine: Any = None) -> None:
6153
+ def _agent_target_label(targets: Any, labels: Optional[Dict[str, str]] = None,
6154
+ *, with_id: bool = False) -> str:
6155
+ """One assignment's scope, unscoped only when the field is absent.
6156
+
6157
+ ``labels`` maps an exact profile's ``agt_…`` id to the profile label the
6158
+ machine reported (`.claude-work`), so the target reads as the profile a
6159
+ person recognises. ``with_id`` keeps the id beside it, for the views a
6160
+ selector is copied from. An id with no label stays the bare id.
6161
+ """
6162
+ if not targets:
6163
+ return "all agents"
6164
+ labels = labels or {}
6165
+
6166
+ def one(target: Dict[str, Any]) -> str:
6167
+ instance = target.get("instanceId")
6168
+ if not instance:
6169
+ return f"all {target['integration']}"
6170
+ name = labels.get(instance)
6171
+ if not name:
6172
+ return f"{target['integration']}/{instance}"
6173
+ return f"{target['integration']}/{name}" + (f" ({instance})" if with_id else "")
6174
+
6175
+ return ", ".join(one(target) for target in targets)
6176
+
6177
+
6178
+ def render_machine_policies(machine_id: str, dep: Any, machine: Any = None,
6179
+ agent_labels: Optional[Dict[str, str]] = None) -> None:
6101
6180
  """``fp fleet show`` — what a machine is told to run, and whether it has it.
6102
6181
 
6103
6182
  The first version printed the id, the generation and the policy list, which
@@ -6148,6 +6227,27 @@ def render_machine_policies(machine_id: str, dep: Any, machine: Any = None) -> N
6148
6227
  act.append(f" · {machine.event_count} events", style=theme.LABEL)
6149
6228
  field("last seen", act)
6150
6229
 
6230
+ mode = (getattr(dep, "jev_mode", None) if dep is not None else None) or (
6231
+ getattr(machine, "jev_mode", None) if machine is not None else None
6232
+ )
6233
+ jev = Text(mode or "local", style=theme.ACCENT if mode else theme.TEXT_DIM)
6234
+ jev.append(" · set by FailproofAI Cloud" if mode else " · the machine's own", style=theme.LABEL)
6235
+ field("jev mode", jev)
6236
+
6237
+ errors = (getattr(machine, "policy_errors", None) or []) if machine is not None else []
6238
+ if errors:
6239
+ body.append(Text())
6240
+ head = Text(f"{len(errors)} policy error{'s' if len(errors) != 1 else ''}", style=f"bold {theme.ERROR}")
6241
+ head.append(" · reported by the machine — these are not enforcing", style=theme.LABEL)
6242
+ body.append(head)
6243
+ for e in errors:
6244
+ line = Text(" ")
6245
+ ver = e.get("version")
6246
+ line.append(f"{e.get('id', '?')}{f' v{ver}' if ver is not None else ''}", style=theme.TEXT)
6247
+ line.append(f" {e.get('kind', '')}", style=theme.FAINT)
6248
+ line.append(f" {e.get('message', '')}", style=theme.TEXT_DIM)
6249
+ body.append(line)
6250
+
6151
6251
  body.append(Text())
6152
6252
  pols = sorted(dep.policies, key=lambda x: x.id) if dep is not None else []
6153
6253
  if pols:
@@ -6156,13 +6256,15 @@ def render_machine_policies(machine_id: str, dep: Any, machine: Any = None) -> N
6156
6256
  # padded to the header's width — otherwise the effect column steps left
6157
6257
  # by one on every row and the table reads as misaligned.
6158
6258
  vwidth = max(3, max(len(f"v{p.version}") for p in pols))
6159
- head = Text(f" {'policy'.ljust(width)} {'ver'.ljust(vwidth)} effect", style=theme.LABEL)
6259
+ head = Text(f" {'policy'.ljust(width)} {'ver'.ljust(vwidth)} effect agents", style=theme.LABEL)
6160
6260
  body.append(head)
6161
6261
  for p in pols:
6162
6262
  row = Text(" ")
6163
6263
  row.append(p.id.ljust(width), style=theme.TEXT)
6164
6264
  row.append(f" {f'v{p.version}'.ljust(vwidth)} ", style=theme.TEXT_DIM)
6165
6265
  row.append_text(_effect(p.effect))
6266
+ row.append(" " + _agent_target_label(getattr(p, "agent_targets", None), agent_labels,
6267
+ with_id=True), style=theme.TEXT_DIM)
6166
6268
  body.append(row)
6167
6269
  else:
6168
6270
  body.append(Text(" no policies deployed", style=theme.FAINT))
@@ -6183,18 +6285,24 @@ def render_deploy_plan(plan: Any, *, applied: bool = False) -> None:
6183
6285
  lines = []
6184
6286
  for p in plan.added:
6185
6287
  t = Text(" + ", style=theme.SUCCESS); t.append_text(_policy_cell(p))
6186
- t.append(" "); t.append_text(_effect(p.effect)); lines.append(t)
6288
+ t.append(" "); t.append_text(_effect(p.effect))
6289
+ t.append(" " + _agent_target_label(getattr(p, "agent_targets", None)), style=theme.TEXT_DIM)
6290
+ lines.append(t)
6187
6291
  for was, now in plan.changed:
6188
6292
  t = Text(" ~ ", style=theme.AMBER); t.append_text(_policy_cell(now))
6189
6293
  t.append(" "); t.append_text(_effect(now.effect))
6190
- t.append(f" (was v{was.version} {was.effect})", style=theme.FAINT); lines.append(t)
6294
+ t.append(" " + _agent_target_label(getattr(now, "agent_targets", None)), style=theme.TEXT_DIM)
6295
+ t.append(f" (was v{was.version} {was.effect}; {_agent_target_label(getattr(was, 'agent_targets', None))})", style=theme.FAINT)
6296
+ lines.append(t)
6191
6297
  for p in plan.removed:
6192
6298
  t = Text(" - ", style=theme.ERROR)
6193
6299
  t.append(p.id, style=theme.TEXT_DIM); t.append(f" v{p.version}", style=theme.FAINT)
6194
6300
  lines.append(t)
6195
6301
  for p in plan.unchanged:
6196
6302
  t = Text(" = ", style=theme.FAINT); t.append_text(_policy_cell(p))
6197
- t.append(" "); t.append_text(_effect(p.effect)); lines.append(t)
6303
+ t.append(" "); t.append_text(_effect(p.effect))
6304
+ t.append(" " + _agent_target_label(getattr(p, "agent_targets", None)), style=theme.TEXT_DIM)
6305
+ lines.append(t)
6198
6306
  if not lines:
6199
6307
  lines = [Text(" (no policies)", style=theme.FAINT)]
6200
6308
 
@@ -6204,6 +6312,16 @@ def render_deploy_plan(plan: Any, *, applied: bool = False) -> None:
6204
6312
  footer.append(f"polic{'y' if n == 1 else 'ies'} after this change", style=theme.LABEL)
6205
6313
  lines.append(Text())
6206
6314
  lines.append(footer)
6315
+ if getattr(plan, "jev_mode", None) is not None:
6316
+ before = plan.jev_mode_before or "local"
6317
+ after = plan.jev_mode_after or "local"
6318
+ jev = Text(" jev mode ", style=theme.LABEL)
6319
+ if plan.jev_mode_changes:
6320
+ jev.append(f"{before} → ", style=theme.FAINT)
6321
+ jev.append(after, style=f"bold {theme.ACCENT}")
6322
+ else:
6323
+ jev.append(f"{after} (unchanged)", style=theme.TEXT_DIM)
6324
+ lines.append(jev)
6207
6325
 
6208
6326
  head = Text(plan.machine_id, style=f"bold {theme.TEXT}")
6209
6327
  if plan.base is not None:
@@ -6333,7 +6451,18 @@ def deployment_applied(machine_id: str, generation: int, count: int) -> None:
6333
6451
  _notice_box(body, color=theme.SUCCESS, title="deployed")
6334
6452
 
6335
6453
 
6336
- def deployment_rolled_back(machine_id: str, restored: int, generation: int) -> None:
6454
+ def deployment_rolled_back(
6455
+ machine_id: str,
6456
+ restored: int,
6457
+ generation: int,
6458
+ *,
6459
+ jev_from: Optional[str] = None,
6460
+ jev_to: Optional[str] = None,
6461
+ ) -> None:
6462
+ """``✓ restored the set from #R on <machine> · minted as #G``, plus the Jev
6463
+ mode the rollback brought back when it changed — a rollback restores the
6464
+ generation's mode too, and saying nothing is how `off` silently became
6465
+ `enforce` again."""
6337
6466
  body = Text()
6338
6467
  body.append("✓ ", style=theme.SUCCESS)
6339
6468
  body.append("restored the set from ", style=theme.TEXT)
@@ -6342,9 +6471,76 @@ def deployment_rolled_back(machine_id: str, restored: int, generation: int) -> N
6342
6471
  body.append(machine_id, style=theme.ACCENT)
6343
6472
  body.append(" · ", style=theme.FAINT)
6344
6473
  body.append(f"minted as deployment #{generation}", style=theme.LABEL)
6474
+ if jev_from is not None and jev_to is not None:
6475
+ body.append(" · ", style=theme.FAINT)
6476
+ if jev_from != jev_to:
6477
+ body.append("jev mode ", style=theme.LABEL)
6478
+ body.append(f"{jev_from} → ", style=theme.FAINT)
6479
+ body.append(jev_to, style=f"bold {theme.AMBER}")
6480
+ else:
6481
+ body.append(f"jev mode {jev_to} (unchanged)", style=theme.LABEL)
6345
6482
  _notice_box(body, color=theme.SUCCESS, title="rolled back")
6346
6483
 
6347
6484
 
6485
+ def render_jev_mode_plan(rows: Sequence[dict]) -> None:
6486
+ """The mode-only change, per machine: ``from → to``, the set untouched."""
6487
+ out = []
6488
+ for r in rows:
6489
+ changed = bool(r.get("changed"))
6490
+ mode = Text()
6491
+ if changed:
6492
+ mode.append(f"{r.get('from')} → ", style=theme.FAINT)
6493
+ mode.append(str(r.get("to")), style=f"bold {theme.ACCENT}")
6494
+ else:
6495
+ mode.append(f"{r.get('to')} (unchanged)", style=theme.TEXT_DIM)
6496
+ out.append([Text(str(r.get("machineId")), style=theme.TEXT), mode])
6497
+ n = sum(1 for r in rows if r.get("changed"))
6498
+ title = Text()
6499
+ title.append("jev mode", style=f"bold {theme.ACCENT}")
6500
+ title.append(" · ", style=theme.FAINT)
6501
+ title.append(f"{n} of {len(rows)} to change", style=theme.LABEL)
6502
+ title.append(" · policy sets untouched", style=theme.FAINT)
6503
+ render_list_panel("jev mode", header=["machine", "jev mode"], rows=out, days=set(), order=None,
6504
+ empty_message="no machines", title=title)
6505
+
6506
+
6507
+ def jev_mode_applied(rows: Sequence[dict]) -> None:
6508
+ """``✓ jev mode observe → enforce on N machines``, and any that failed."""
6509
+ done = [r for r in rows if r.get("changed") and not r.get("error")]
6510
+ failed = [r for r in rows if r.get("error")]
6511
+ for r in done:
6512
+ body = Text()
6513
+ body.append("✓ ", style=theme.SUCCESS)
6514
+ body.append("jev mode ", style=theme.TEXT)
6515
+ body.append(f"{r.get('from')} → ", style=theme.FAINT)
6516
+ body.append(str(r.get("to")), style=f"bold {theme.ACCENT}")
6517
+ body.append(" on ", style=theme.TEXT)
6518
+ body.append(str(r.get("machineId")), style=theme.ACCENT)
6519
+ if r.get("deployment") is not None:
6520
+ body.append(" · ", style=theme.FAINT)
6521
+ body.append(f"now on deployment #{r.get('deployment')}", style=theme.LABEL)
6522
+ _notice_box(body, color=theme.SUCCESS, title="jev mode")
6523
+ for r in failed:
6524
+ body = Text()
6525
+ body.append("✗ ", style=theme.ERROR)
6526
+ body.append(str(r.get("machineId")), style=theme.ACCENT)
6527
+ body.append(" · ", style=theme.FAINT)
6528
+ body.append(str(r.get("error")), style=theme.TEXT)
6529
+ _notice_box(body, color=theme.ERROR, title="jev mode not changed")
6530
+
6531
+
6532
+ def jev_mode_unchanged(machine_ids: Sequence[str], mode: str) -> None:
6533
+ """Every machine already has the mode. Calm, like a no-op deploy."""
6534
+ body = Text()
6535
+ body.append("= ", style=theme.FAINT)
6536
+ body.append(", ".join(machine_ids) if len(machine_ids) <= 3 else f"{len(machine_ids)} machines",
6537
+ style=theme.ACCENT)
6538
+ body.append(f" already {'has' if len(machine_ids) == 1 else 'have'} jev mode {mode}", style=theme.TEXT)
6539
+ body.append(" · ", style=theme.FAINT)
6540
+ body.append("nothing changed", style=theme.LABEL)
6541
+ _notice_box(body, color=theme.ACCENT, title="no change")
6542
+
6543
+
6348
6544
  def machine_renamed(machine_id: str, label: str) -> None:
6349
6545
  body = Text()
6350
6546
  body.append("✓ ", style=theme.SUCCESS)
@@ -6581,7 +6777,8 @@ def policy_published_brief(p: Any) -> None:
6581
6777
  _notice_box(body, color=theme.SUCCESS, title="published")
6582
6778
 
6583
6779
 
6584
- def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
6780
+ def render_deployment_history(machine_id: str, entries: Sequence[dict],
6781
+ agent_labels: Optional[Dict[str, str]] = None) -> None:
6585
6782
  """``fp fleet history`` — one row per generation, newest first.
6586
6783
 
6587
6784
  Was a bare `print` per line, which put an unaligned wall of timestamps and
@@ -6595,6 +6792,7 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
6595
6792
  """
6596
6793
  rows = []
6597
6794
  prev = None
6795
+ prev_mode = "local"
6598
6796
  # oldest first so each row can be diffed against the one before it, then
6599
6797
  # reversed for display — newest first is how you read a history.
6600
6798
  ordered = sorted(entries, key=lambda e: e.get("deployment") or 0)
@@ -6605,17 +6803,28 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
6605
6803
  # that STOPPED BLOCKING, and it rendered as "no change". It also split a
6606
6804
  # version bump into a "+x" and a "-x" for the same policy, which reads
6607
6805
  # as removed-and-re-added rather than moved.
6608
- cur = {p.get("id"): (p.get("version"), p.get("effect"))
6806
+ cur = {p.get("id"): (p.get("version"), p.get("effect"),
6807
+ tuple(sorted(
6808
+ (t.get("integration") or "", t.get("instanceId") or "")
6809
+ for t in (p.get("agentTargets") or [])
6810
+ )))
6609
6811
  for p in (e.get("policies") or [])}
6812
+ scope = {p.get("id"): _agent_target_label(p.get("agentTargets"), agent_labels)
6813
+ for p in (e.get("policies") or [])}
6814
+ mode = e.get("jevMode") or "local"
6610
6815
  if prev is None:
6611
- diffs[e.get("deployment")] = [("+", i) for i in sorted(cur)]
6816
+ diffs[e.get("deployment")] = [("+", f"{i} → {scope[i]}") for i in sorted(cur)]
6612
6817
  else:
6613
6818
  diffs[e.get("deployment")] = (
6614
- [("+", i) for i in sorted(set(cur) - set(prev))]
6819
+ [("+", f"{i} → {scope[i]}") for i in sorted(set(cur) - set(prev))]
6615
6820
  + [("-", i) for i in sorted(set(prev) - set(cur))]
6616
- + [("~", i) for i in sorted(set(cur) & set(prev)) if cur[i] != prev[i]]
6821
+ + [("~", f"{i} → {scope[i]}") for i in sorted(set(cur) & set(prev)) if cur[i] != prev[i]]
6822
+ # A mode-only generation (`fp fleet jev-mode`) read as "no
6823
+ # change", which is exactly the change a rollback brings back.
6824
+ + ([("~", f"jev {prev_mode}→{mode}")] if mode != prev_mode else [])
6617
6825
  )
6618
6826
  prev = cur
6827
+ prev_mode = mode
6619
6828
 
6620
6829
  newest_first = sorted(entries, key=lambda e: e.get("deployment") or 0, reverse=True)
6621
6830
  # The shared time column: clock time, with the date folded in only when the
@@ -6636,10 +6845,14 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
6636
6845
  change.append(ref, style=theme.TEXT_DIM)
6637
6846
  if not change.plain:
6638
6847
  change = Text("no change", style=theme.FAINT)
6848
+ mode = e.get("jevMode") or "local"
6639
6849
  rows.append([
6640
6850
  Text(f"#{gen}", style=theme.TEXT),
6641
6851
  Text(tcell or (e.get("updatedAt", "") or "-"), style=theme.TEXT_DIM),
6642
6852
  Text(str(len(pols)), style=theme.TEXT_DIM if pols else theme.FAINT),
6853
+ # The mode FailproofAI Cloud set with this generation — what a
6854
+ # rollback to it restores.
6855
+ Text(mode, style=theme.FAINT if mode == "local" else theme.TEXT),
6643
6856
  change,
6644
6857
  Text(", ".join(pols) or "(none)", style=theme.TEXT_DIM if pols else theme.FAINT),
6645
6858
  ])
@@ -6647,9 +6860,34 @@ def render_deployment_history(machine_id: str, entries: Sequence[dict]) -> None:
6647
6860
  title.append(machine_id, style=f"bold {theme.ACCENT}")
6648
6861
  title.append(" · ", style=theme.FAINT)
6649
6862
  title.append(f"{len(rows)} generations", style=theme.LABEL)
6650
- render_list_panel("history", header=["gen", "when", "n", "change", "policies"],
6863
+ header = ["gen", "when", "n", "jev", "change", "policies"]
6864
+ # Fit the panel to the terminal by giving up the expendable columns first.
6865
+ # Every cell is no_wrap, so a table wider than the terminal had Rich shave
6866
+ # ALL columns at once — and at 80 columns `gen`, `when` and `jev` were the
6867
+ # ones that vanished, though `gen` is the argument `fp fleet rollback` takes.
6868
+ # `policies` goes first (`n` still counts it, `change` says what moved),
6869
+ # then `change` wraps rather than squeezing the four short columns.
6870
+ last_col, last_col_max = "ellipsis", None
6871
+ if rows:
6872
+ def natural(i: int) -> int:
6873
+ return max([len(header[i])] + [r[i].cell_len for r in rows])
6874
+ # indent 2 + border 2 + panel padding 2, two spaces between columns,
6875
+ # and a little slack for Rich's rounding.
6876
+ chrome = 6 + 2
6877
+ fixed = sum(natural(i) for i in range(4)) + 2 * 4 + chrome
6878
+ room = _stdout.width - fixed
6879
+ change_w, policies_w = natural(4), natural(5)
6880
+ if change_w + 2 + policies_w > room:
6881
+ if room - change_w - 2 >= 12:
6882
+ last_col_max = room - change_w - 2
6883
+ else:
6884
+ header = header[:5]
6885
+ rows = [r[:5] for r in rows]
6886
+ last_col = "wrap"
6887
+ render_list_panel("history", header=header,
6651
6888
  rows=rows, days=days, order=None,
6652
- empty_message="no deployment history", last_col="ellipsis", title=title)
6889
+ empty_message="no deployment history", last_col=last_col,
6890
+ last_col_max=last_col_max, title=title)
6653
6891
 
6654
6892
 
6655
6893
  def render_fleet_diff(rows: Sequence[dict]) -> None:
@@ -446,14 +446,17 @@ def cloud_publish_problem(source: str) -> Optional[str]:
446
446
  if not source or len(source) > _MAX_SCAN:
447
447
  return None
448
448
  if _find_call(source, _SEMANTIC_CALL) != -1:
449
- return ("semanticPolicies.add does nothing in a cloud policy: Jev reads semantic checks only "
450
- "from a failproofai pack. Remove it to publish the rest, or ship the file with "
451
- "`failproofai publish`.")
449
+ return ("semanticPolicies.add does nothing in a cloud policy's JavaScript: Jev reads semantic "
450
+ "checks only from a failproofai pack or from the policy's own Jev declarations. Put the "
451
+ "check in a JSON file and publish with `--kind both --semantic checks.json` (or `--kind "
452
+ "jev`), or ship the file with `failproofai publish`.")
452
453
  # Every registration: any of them can carry a reviewable declaration.
453
454
  at = _find_call(source, _ADD_CALL)
454
455
  while at != -1:
455
456
  if _direct_props(source, at).get("authority") == "reviewable":
456
- return ("a cloud policy is always hard, so Jev never clears it and authority/reviewedBy "
457
- "are ignored. Remove them to publish it as a hard policy.")
457
+ return ("a cloud policy's JavaScript is always hard as written: its authority comes from "
458
+ "its kind, never from the source, so authority/reviewedBy here are ignored. Remove "
459
+ "them, and publish with `--kind both --semantic checks.json` to make it reviewable "
460
+ "by its own Jev checks.")
458
461
  at = _find_call(source, _ADD_CALL, at + len(_ADD))
459
462
  return None
@@ -196,8 +196,8 @@ you need a flag you don't already know.
196
196
  - `audits list|show|create|edit|delete|run|runs` — scheduled sweeps, referenced by **name**; `audits findings|finding` + the triage verbs `ack|mute|dismiss|resolve|reopen|assign` act on a finding **id**. `audits run <name>` only *queues* a run (poll `audits runs <name>` for completion). See §8.
197
197
 
198
198
  **Enforce (cloud-managed policy, session-only — see §2):**
199
- - `policies list|show|publish|enable|disable|delete|test|compose` — policy versions. `publish` mints a version from a local `.mjs`; `test` runs one against a synthetic context locally (it applies each policy's `match` filter, so a policy that does not cover the `--event`/`--tool` you pass is reported `skipped`, not run). `enable`/`disable`/`delete` take `--yes`.
200
- - `fleet list|show|deploy|diff|history|rollback|rename` — which machines run which policies. **`deploy` REPLACES a machine's whole set** (`--add`/`--remove` amend it, `--set` replaces, `--create` mints a deployment); it prints the plan and asks **only on an interactive terminal without `--json`** — under `--json` or with stdin redirected it applies immediately, so read `fleet show` first if you want review.
199
+ - `policies list|show|publish|enable|disable|delete|test|compose` — policy versions. `publish` mints a version of a kind — `regex` (a local `.mjs`), `jev` (`--semantic checks.json`: Jev declarations, no JavaScript) or `both` (the two, the JavaScript reviewable by its own checks); list/show print the kind and the Jev check names; `test` runs one against a synthetic context locally (it applies each policy's `match` filter, so a policy that does not cover the `--event`/`--tool` you pass is reported `skipped`, not run). `enable`/`disable`/`delete` take `--yes`.
200
+ - `fleet list|show|deploy|jev-mode|diff|history|rollback|rename` — which machines run which policies, and the policy errors each machine reports. **`deploy` REPLACES a machine's whole set** (`--add`/`--remove` amend it, `--set` replaces, `--create` mints a deployment, `--jev-mode off|observe|enforce|local` sets the machine's Jev mode from FailproofAI Cloud — overriding its own, machine-wide, and left alone when omitted; in `enforce` Jev's checks BLOCK calls as well as clear). `jev-mode <machine…|--all> <mode>` changes only the mode, never the set; `rollback` restores the generation's Jev mode too; it prints the plan and asks **only on an interactive terminal without `--json`** — under `--json` or with stdin redirected it applies immediately, so read `fleet show` first if you want review.
201
201
  - `guardrails summary|timeline` — what enforcement actually did; `--since 1h|6h|24h|7d`, `--machine`.
202
202
 
203
203
  **Analytics & assistant:**
@@ -220,22 +220,29 @@ Built-in assistant. Chats referenced by a **short chat-id** (first 8 hex; prefix
220
220
  Cloud-managed enforcement, split the way the dashboard splits it: `policies` writes a version, `fleet` decides which machines run it, `guardrails` reports what it blocked. Needs `policies:read` to read, `policies:write` to change anything.
221
221
 
222
222
  ### policies
223
- - `policies list` — one row per published VERSION (versions are immutable and all stay addressable), newest of each policy first; the title counts distinct policies and captions the version total. `state` is active / disabled / archived. JSON `{policies:[…]}` — also every version, so deduplicate on `id` for one row per policy.
224
- - `policies show <id>` — the NEWEST version, including the full `source`.
225
- - `policies publish <id> [SOURCE] [--description "…"] [--no-verify]` — mints a **new version**; never edits one. SOURCE is a path, `@path`, `-`, a pipe, or omitted to paste on a TTY (Ctrl-D ends). The source is **parse-checked with node** before it is sent; nothing downstream does this, and a broken policy otherwise fails on the machine at enforcement time. `--no-verify` skips it, and a host without node publishes with a warning rather than a block. A source with Jev fields a cloud policy never reads — `semanticPolicies.add(...)`, or `authority: "reviewable"` on a registration — is refused before sending (**exit 1**, not skipped by `--no-verify`; commented-out code does not count): Jev checks ship in a pack via `failproofai publish`. **Publishing deploys nothing** — the version is unused until `fleet deploy` puts it on a machine.
223
+ - `policies list` — one row per published VERSION (versions are immutable and all stay addressable), newest of each policy first; the title counts distinct policies and captions the version total. `kind` is `regex` / `jev` / `both`, and a `jev`/`both` row names its Jev checks before the description. `state` is active / disabled / archived. JSON `{policies:[…]}` — also every version, so deduplicate on `id` for one row per policy; each carries `kind`, and for `jev`/`both` `semantic`, `semanticSha256`, and the server-derived `authority`/`reviewedBy`.
224
+ - `policies show <id>` — the NEWEST version: its kind, its Jev check names, the full `source`, and the Jev declarations.
225
+ - `policies publish <id> [SOURCE] [--kind regex|jev|both] [--source FILE] [--semantic FILE] [--description "…"] [--no-verify]` — mints a **new version**; never edits one. A policy's **kind** says what it carries:
226
+ - `regex` — JavaScript only (SOURCE or `--source`): what publish has always done, and the default.
227
+ - `jev` — Jev checks only (`--semantic checks.json`, no JavaScript): a JSON array (or `{"semantic": [...]}`) of 1–24 Jev declarations, each exactly a failproofai pack manifest's semantic entry — `{name, title, appliesTo:[shell|write|read|network|other], mode:deny|instruct, userCanOverride, probes:[{id, instructions, criteria:{true,false}}] (≤6), guidance, precondition?, exempt?}`.
228
+ - `both` — JavaScript plus its own Jev checks, deployed and toggled as one. FailproofAI Cloud makes the JavaScript's verdict `reviewable` by exactly its own checks (`authority`/`reviewedBy` are derived by the server, never sent).
229
+
230
+ Without `--kind` it follows from what you give (`--semantic` alone is `jev`, with source `both`); a kind that contradicts its inputs is **exit 2**, before anything is sent. `fp` checks only the shape of the file (a non-empty list of named objects, ≤24); the server checks the rest — the limits, FailproofAI's 16 reserved check names (**422 `reserved_jev_name`**), and names unique across the org — and names the declaration it refused. SOURCE is a path, `@path`, `-`, a pipe, or omitted to paste on a TTY (Ctrl-D ends); `--semantic` takes a path, `@path` or `-` (only one of the two may be stdin). The JavaScript is **parse-checked with node** before it is sent; nothing downstream does this, and a broken policy otherwise fails on the machine at enforcement time. `--no-verify` skips it, and a host without node publishes with a warning rather than a block. JavaScript carrying Jev fields — `semanticPolicies.add(...)`, or `authority: "reviewable"` on a registration — is refused before sending (**exit 1**, not skipped by `--no-verify`; commented-out code does not count): put Jev checks in `--semantic` (`--kind both`), where they take effect. **Publishing deploys nothing** — the version is unused until `fleet deploy` puts it on a machine.
226
231
  - `policies enable <id>` · `policies disable <id> [-y]` — **disable REMOVES the policy from every deployment carrying it**, reissuing each affected machine at a new generation (visible in `fleet history`). `enable` is the exact inverse — it puts the policy back into every deployment it was removed from, reissuing those machines again. Nothing needs redeploying by hand, and `machinesUpdated` in the JSON reports the count for both directions.
227
232
  - `policies test [SOURCE] [--tool Bash] [--command "…"] [--file PATH] [--event PreToolUse] [--expect allow|deny|instruct]` — run a policy LOCALLY and print what it decides. Executes the real file (bare `import { deny } from "failproofai"` and all) against a synthetic context; nothing is published, nothing installed. Needs `node`. `--expect` asserts the decision and exits 1 when it differs — a correct `deny` is a PASSING test, so the decision alone never sets the exit code. JSON `{ok, decision, policies:[{name,decision,reason}], expected, met}`; `decision` is the strictest any registered policy returned.
228
233
  - `policies compose "<description>" [--out FILE] [--publish ID]` — the assistant drafts policy source from plain English. Prints it and stops by default: a generated policy that deploys itself is one nobody read. `--publish` still syntax-checks first. Needs `policies:write` for BOTH the draft and the publish — `POST /api/agent/compose-policy` is `withAuth("policies:write")`; `agent:use` gates the assistant's other routes and is not checked on this one. The composer has a **30s server-side limit** — a long or vague description simply does not finish, and raising `--timeout` does not help because the cut is not client-side. Retry with something shorter and more specific.
229
234
  - `policies delete <id> [-y]` — archives. **A machine already carrying the policy keeps enforcing it** until redeployed; `disable` is what stops enforcement everywhere.
230
235
 
231
236
  ### fleet
232
- - `fleet list` — `machine · label · pol · intended · applied · seen · events · state`. `intended` is the generation deployed, `applied` what the machine last collected (they differ until it polls), and `seen` how long since it last reported anything — a machine can be in sync and dead, or alive and behind, which are different problems. JSON `{machines, deployments}` with raw epoch-ms timestamps plus the computed `drifted`.
233
- - `fleet show <machine>` — the set the machine is told to run, **and whether it has collected it**. Reads both the deployment and the machine record, so it reports `not yet collected` / `machine is on #N` / `collected` alongside who deployed it, when, and last-seen. A machine can be told to run a policy it has never picked up; the policy list alone cannot tell you which. JSON `{machine, deployment}` with raw timestamps and both label fields; `deployment: null` when nothing is deployed.
234
- - `fleet deploy <machine> [--add REF]… [--remove ID]… [--set REF]… [--create] [-y]`
237
+ - `fleet list` — `machine · label · pol · intended · applied · seen · events · state`. `intended` is the generation deployed, `applied` what the machine last collected (they differ until it polls), and `seen` how long since it last reported anything — a machine can be in sync and dead, or alive and behind, which are different problems. `state` reads `N errors` when the machine reported policies it could not apply. JSON `{machines, deployments}` with raw epoch-ms timestamps plus the computed `drifted`; a machine carries `policyErrors`/`policyErrorsAt` and `jevMode` when the server has them.
238
+ - `fleet show <machine>` — the set the machine is told to run, **and whether it has collected it**. Reads both the deployment and the machine record, so it reports `not yet collected` / `machine is on #N` / `collected` alongside who deployed it, when, and last-seen. A machine can be told to run a policy it has never picked up; the policy list alone cannot tell you which. Also the **Jev mode** FailproofAI Cloud sets on it (`local` when none) and the **policy errors** the machine last reported — `[{id, version, kind: regex|jev|both|daemon, message}]`: a policy that did not load, `jev_unconfigured` (a Jev mode, and the machine's key does not carry Jev), `transcripts_disabled` (a Jev mode on a machine connected with `--no-transcripts`, which never asks), `jev_budget: dropped <name>` (an installed pack's check FailproofAI Cloud left out of a request for the question budget). Whatever is listed there is not enforcing. JSON `{machine, deployment}` with raw timestamps and both label fields; `deployment: null` when nothing is deployed.
239
+ - `fleet deploy <machine> [--add REF]… [--remove ID]… [--set REF]… [--jev-mode off|observe|enforce|local] [--create] [-y]`
235
240
 
236
241
  **A deploy REPLACES the whole set.** The endpoint takes the full list and does not merge. `--add`/`--remove` are a read-modify-write: the CLI reads the current set, applies the delta, prints the complete result, writes that. `--set` replaces everything and is refused alongside `--add`/`--remove`.
237
242
 
238
- REF is `id`, `id@version`, `id:effect`, or `id@version:effect`. Effect is `enforce` (default) or `observe`. A bare `--add` of an already-deployed policy **keeps its pinned version** — pass `id@version` to move it.
243
+ REF is `id`, `id@version`, `id:effect`, or `id@version:effect`. Effect is `enforce` (default) or `observe`. A bare `--add` of an already-deployed policy **keeps its pinned version** — pass `id@version` to move it. Every kind deploys the same way (a `both` policy's JavaScript and its checks go together); the effect applies to JavaScript only, so `observe` on a `jev` policy is refused (**exit 2**) — watch Jev through the mode instead — and an `observe` `both` policy sends only its (observed) JavaScript: its Jev checks are not asked while it is observed.
244
+
245
+ `--jev-mode` sets the machine's Jev mode from FailproofAI Cloud and **overrides the machine's own, a local `off` included**: `observe` asks Jev and logs it while the regex policies decide; `enforce` lets Jev's checks **block** calls (every `jev` policy's deny checks) as well as clear the regex verdicts they review; `off` stops Jev there; `local` hands the choice back to the machine. The mode is the **machine's**: it also governs the checks of every pack installed there. Omitted, it is left as it is (the request carries no `jevMode`). `--jev-mode` with no change to the set changes **only the mode**, through its own route (like `fleet jev-mode`), so disabled policies' assignments and any concurrent edit are untouched; the plan shows `jev mode <from> → <to>` and the JSON adds `jevModeOnly: true`. On a machine with no deployment yet, it deploys the (empty) set with the mode. Jev checks run on FailproofAI Cloud; nothing is installed on the machine. Under `observe`/`enforce` the machine sends each checked tool call to FailproofAI Cloud on its Cloud Jev credential (its `jev.json` is not used); without one it reports `jev_unconfigured`, and connected with `--no-transcripts` it never asks and reports `transcripts_disabled`.
239
246
 
240
247
  Deploying to an id that has never checked in is refused (a typo would mint a machine); `--create` allows it for pre-staging.
241
248
 
@@ -245,9 +252,10 @@ Cloud-managed enforcement, split the way the dashboard splits it: `policies` wri
245
252
 
246
253
  **Exit codes.** A malformed ref (`bad ref!!`, `id:banana`, empty), or `--set` combined with `--add`/`--remove`, is a usage error → **exit 2**, like every other bad flag value. A ref that parses but names something that does not exist (`--add ghost-policy`, an unpublished `@version`) is **exit 1**; an unknown machine is **exit 6**. Branch on these rather than on the message.
247
254
 
248
- JSON `{plan:{result,added,removed,changed,unchanged,noop}, deployment, applied}` — the plan is included so a harness does not recompute the diff.
255
+ JSON `{plan:{result,added,removed,changed,unchanged,noop[,jevMode:{from,to,changed}]}, deployment, applied}` — the plan is included so a harness does not recompute the diff.
256
+ - `fleet jev-mode <machine>… <mode>` · `fleet jev-mode --all <mode> [-y]` — set the Jev mode FailproofAI Cloud gives machines (`off|observe|enforce|local`) and **nothing else**: the policy set is never read back or rewritten. A machine already on the mode is skipped; `--all` covers every machine with a deployment and skips the rest; a named machine with no deployment is refused before anything is written (`fleet deploy <machine> --jev-mode <mode>` gives it its first). Each change is a new generation in `fleet history`. One machine's failure does not stop the others; the exit code is 1 and the JSON row carries `error`. A bad mode, or `--all` with machine ids, is **exit 2**. JSON `{mode, applied, machines:[{machineId, from, to, changed, deployment?, error?}]}`.
249
257
  - `fleet diff [machine]` — intent vs delivery per machine, with a `drifted` flag. A machine id nobody has reported under is refused (exit 6), not rendered as an empty fleet.
250
- - `fleet history <machine>` · `fleet rollback <machine> <generation> [-y]` — rollback mints a NEW generation carrying the old set; history stays append-only. The `change` column uses the deploy plan's vocabulary: `+` added, `-` removed, `~` same policy at a different version or effect (an enforce → observe flip is a policy that stopped blocking, so it is never "no change").
258
+ - `fleet history <machine>` · `fleet rollback <machine> <generation> [-y]` — rollback mints a NEW generation carrying the old set; history stays append-only. The `change` column uses the deploy plan's vocabulary: `+` added, `-` removed, `~` same policy at a different version or effect (an enforce → observe flip is a policy that stopped blocking, so it is never "no change"), and `~ jev <from>→<to>` for a Jev mode change. The `jev` column is the mode FailproofAI Cloud set with each generation (`local` = none). **A rollback restores that generation's Jev mode too**, `local` included: the prompt and the result say `jev mode <now> → <then>`, and the JSON adds `jevModeBefore`.
251
259
  - `fleet rename <machine> "<label>"` — a human label; the id never changes. The server stores it as an override beside the machine's self-asserted label. An empty label **clears** the override (the machine falls back to its own label, else its id) and the CLI says so rather than reporting a rename to nothing. A machine that has never checked in cannot be renamed → exit 6.
252
260
 
253
261
  ### guardrails