failproofai 1.0.1 → 1.0.2-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 (390) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +6 -6
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  24. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  29. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
  31. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
  33. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
  35. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  39. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/index.html +1 -1
  42. package/.next/standalone/.next/server/app/index.rsc +15 -15
  43. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  44. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  45. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/page.js +2 -2
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +66 -22
  51. package/.next/standalone/.next/server/app/policies/page.js +5 -4
  52. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/projects/page.js +2 -2
  65. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  68. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  69. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/chunks/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
  72. package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
  84. package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
  89. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  93. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0l4748x._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  102. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/_0bwu160._.js +23 -0
  104. package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _0ns6k3a._.js} +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
  108. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  114. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  115. package/.next/standalone/.next/server/middleware.js +1 -1
  116. package/.next/standalone/.next/server/pages/404.html +1 -1
  117. package/.next/standalone/.next/server/pages/500.html +1 -1
  118. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
  120. package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 01u1cf8js2yd6.js} +1 -1
  121. package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 0izgu7nqh2zp-.js} +1 -1
  122. package/.next/standalone/.next/static/chunks/0u4-org_e7zde.js +1 -0
  123. package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
  124. package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 1tynnrrh711lr.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 22rqvlr7p_1mv.js} +13 -13
  126. package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2whxqyskpirbk.js} +1 -1
  127. package/.next/standalone/.next/static/chunks/31mwk8k0q3_3g.js +6 -0
  128. package/.next/standalone/.next/static/chunks/3ms6gxfm82t6s.js +1 -0
  129. package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 3tbbs_d8xrvyp.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/43nlc236oo898.js +1 -0
  131. package/.next/standalone/SECURITY.md +5 -4
  132. package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
  133. package/.next/standalone/app/actions/pack-actions.ts +163 -0
  134. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  135. package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
  136. package/.next/standalone/app/policies/hooks-client.tsx +320 -15
  137. package/.next/standalone/components/navbar.tsx +2 -28
  138. package/.next/standalone/components/reach-developers.tsx +8 -1
  139. package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
  140. package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
  141. package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
  142. package/.next/standalone/docs-old/i18n/README.de.md +1 -0
  143. package/.next/standalone/docs-old/i18n/README.es.md +1 -0
  144. package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
  145. package/.next/standalone/docs-old/i18n/README.he.md +1 -0
  146. package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
  147. package/.next/standalone/docs-old/i18n/README.it.md +1 -0
  148. package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
  149. package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
  150. package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
  151. package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
  152. package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
  153. package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
  154. package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
  155. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
  156. package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
  157. package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
  158. package/.next/standalone/fp-cloud-cli/README.md +259 -0
  159. package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
  160. package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
  161. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
  162. package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
  163. package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
  164. package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
  165. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
  166. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
  167. package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
  168. package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
  169. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
  170. package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
  171. package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
  172. package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
  173. package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
  174. package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
  175. package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
  176. package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
  177. package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
  178. package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
  179. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
  180. package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
  181. package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
  182. package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
  183. package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
  184. package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
  185. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
  186. package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
  187. package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
  188. package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
  189. package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
  190. package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
  191. package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
  192. package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
  193. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
  194. package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
  195. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
  196. package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
  197. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
  198. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
  199. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
  200. package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
  201. package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
  202. package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
  203. package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
  204. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
  205. package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
  206. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
  207. package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
  208. package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
  209. package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
  210. package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
  211. package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
  212. package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
  213. package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
  214. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
  215. package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
  216. package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
  217. package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
  218. package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
  219. package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
  220. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
  221. package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
  222. package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
  223. package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
  224. package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
  225. package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
  226. package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
  227. package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
  228. package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
  229. package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
  230. package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
  231. package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
  232. package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
  233. package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
  234. package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
  235. package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
  236. package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
  237. package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
  238. package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
  239. package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
  240. package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
  241. package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
  242. package/.next/standalone/package.json +27 -21
  243. package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
  244. package/.next/standalone/sdk/python/LICENSE +42 -0
  245. package/.next/standalone/sdk/python/MANIFEST.in +21 -0
  246. package/.next/standalone/sdk/python/README.md +466 -0
  247. package/.next/standalone/sdk/python/docs/README.md +249 -0
  248. package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
  249. package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
  250. package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
  251. package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
  252. package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
  253. package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
  254. package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
  255. package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
  256. package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
  257. package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
  258. package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
  259. package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
  260. package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
  261. package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
  262. package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
  263. package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
  264. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
  265. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
  266. package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
  267. package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
  268. package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
  269. package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
  270. package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
  271. package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
  272. package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
  273. package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
  274. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
  275. package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
  276. package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
  277. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
  278. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
  279. package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
  280. package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
  281. package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
  282. package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
  283. package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
  284. package/.next/standalone/sdk/python/pyproject.toml +92 -0
  285. package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
  286. package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
  287. package/.next/standalone/sdk/python/skill/references/events.md +234 -0
  288. package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
  289. package/.next/standalone/sdk/python/skill/references/install.md +100 -0
  290. package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
  291. package/.next/standalone/sdk/python/tests/__init__.py +0 -0
  292. package/.next/standalone/sdk/python/tests/conftest.py +169 -0
  293. package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
  294. package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
  295. package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
  296. package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
  297. package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
  298. package/.next/standalone/sdk/python/tests/test_context.py +410 -0
  299. package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
  300. package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
  301. package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
  302. package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
  303. package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
  304. package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
  305. package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
  306. package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
  307. package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
  308. package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
  309. package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
  310. package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
  311. package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
  312. package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
  313. package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
  314. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
  315. package/.next/standalone/sdk/python/uv.lock +6005 -0
  316. package/.next/standalone/server.js +1 -1
  317. package/README.md +77 -21
  318. package/bin/failproofai.mjs +579 -306
  319. package/dist/cli.mjs +6499 -3662
  320. package/dist/index.js +5 -5
  321. package/dist/worker.mjs +2390 -1978
  322. package/package.json +27 -21
  323. package/scripts/build-policy-pack.mjs +121 -0
  324. package/scripts/changelog-section.py +77 -0
  325. package/scripts/prune-standalone.mjs +3 -0
  326. package/scripts/python-version.py +232 -0
  327. package/scripts/translate-docs/cli.ts +21 -0
  328. package/scripts/translate-docs/mintlify-nav.ts +85 -13
  329. package/scripts/translate-docs/translator.ts +66 -11
  330. package/src/audit/cache.ts +40 -3
  331. package/src/audit/cli.ts +1 -1
  332. package/src/audit/index.ts +1 -1
  333. package/src/audit/replay.ts +89 -3
  334. package/src/audit/report.ts +5 -5
  335. package/src/audit/schedule-cli.ts +49 -53
  336. package/src/hooks/builtin-policies.ts +217 -579
  337. package/src/hooks/cloud-enrollment-cli.ts +66 -27
  338. package/src/hooks/cloud-managed-policies.ts +6 -1
  339. package/src/hooks/configure-wizard.ts +56 -369
  340. package/src/hooks/custom-hooks-loader.ts +89 -6
  341. package/src/hooks/fp-home.ts +92 -4
  342. package/src/hooks/fp-reset.ts +34 -2
  343. package/src/hooks/handler.ts +181 -11
  344. package/src/hooks/harness-cli.ts +79 -25
  345. package/src/hooks/hook-activity-store.ts +13 -3
  346. package/src/hooks/install-prompt.ts +25 -3
  347. package/src/hooks/manager.ts +424 -172
  348. package/src/hooks/migrations.ts +2 -2
  349. package/src/hooks/pack-cli.ts +885 -0
  350. package/src/hooks/pack-failclosed.ts +189 -0
  351. package/src/hooks/pack-manifest.ts +352 -0
  352. package/src/hooks/pack-store.ts +891 -0
  353. package/src/hooks/policy-catalog.ts +535 -0
  354. package/src/hooks/policy-evaluator.ts +24 -16
  355. package/src/hooks/policy-registry.ts +8 -2
  356. package/src/hooks/policy-types.ts +26 -0
  357. package/src/hooks/session-pause-cli.ts +33 -9
  358. package/src/hooks/tui.ts +695 -39
  359. package/src/hooks/types.ts +15 -1
  360. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
  361. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
  362. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
  363. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
  364. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
  365. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
  366. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
  367. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
  368. package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
  369. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
  370. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
  371. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07tbbf3._.js +0 -3
  372. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
  373. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
  374. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
  375. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
  376. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
  377. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
  378. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
  379. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
  380. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
  381. package/.next/standalone/.next/server/chunks/ssr/_0lvyuv4._.js +0 -23
  382. package/.next/standalone/.next/static/chunks/2_2lhuojq2xop.js +0 -1
  383. package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
  384. package/.next/standalone/.next/static/chunks/3k7mxbp9hiom0.js +0 -1
  385. package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
  386. package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
  387. package/src/hooks/policy-presets.ts +0 -135
  388. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_buildManifest.js +0 -0
  389. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_clientMiddlewareManifest.js +0 -0
  390. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_ssgManifest.js +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Install/remove/list failproofai hooks for one or more agent CLIs.
2
+ * Install/remove/list failproofai hooks for one or more agent harnesses.
3
3
  *
4
4
  * Per-CLI path resolution and settings I/O live in `./integrations` (one
5
5
  * `Integration` impl per CLI). This module orchestrates: validation, policy
@@ -26,6 +26,23 @@ import { CliError } from "../cli-error";
26
26
  import { hookLogWarn } from "./hook-logger";
27
27
  import { customPoliciesDir, globalPolicyConfigFile } from "./fp-home";
28
28
  import { readActiveCloudManagedPolicies } from "./cloud-managed-policies";
29
+ import { CORE_SOURCE, addPack, setPackPolicyEnabled } from "./pack-store";
30
+ import type { ResolvedPack } from "./pack-manifest";
31
+ import { hasInstalledPacks, readInstalledPacks } from "./pack-manifest";
32
+ import {
33
+ chip,
34
+ note,
35
+ nextStep,
36
+ optsFor,
37
+ printBlock,
38
+ rule,
39
+ stack,
40
+ table,
41
+ title,
42
+ warning,
43
+ type ChipState,
44
+ type TableRow,
45
+ } from "./tui";
29
46
 
30
47
  const VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
31
48
 
@@ -63,13 +80,167 @@ function resolveFailproofaiBinary(): string {
63
80
  }
64
81
  }
65
82
 
66
- function validatePolicyNames(names: string[]): void {
67
- const invalid = names.filter((n) => !VALID_POLICY_NAMES.has(n));
68
- if (invalid.length > 0) {
69
- const validList = [...VALID_POLICY_NAMES].join(", ");
83
+ /** One policy of one installed pack, resolved from a name the user typed. */
84
+ interface PackPolicyRef {
85
+ packId: string;
86
+ packVersion: string;
87
+ name: string;
88
+ /** The `disabledCustomPolicies` entry the dashboard writes for it. */
89
+ disabledKey: string;
90
+ }
91
+
92
+ /**
93
+ * Split names the user typed into builtins and installed-pack policies.
94
+ *
95
+ * Without this, every name went through `validatePolicyNames`, whose set is the
96
+ * compiled builtins — so `policies --disable block-big-refund` on a pack the
97
+ * user had just installed answered "Unknown policy name" and listed 39 names
98
+ * that were not the one they meant. A pack could be installed and then not
99
+ * managed at all.
100
+ *
101
+ * A builtin wins a bare name, because that is the name people have typed for a
102
+ * year and a third-party pack must not be able to capture it. Two packs
103
+ * declaring the same name is the one case that cannot be guessed, so it is
104
+ * refused with the qualified `<pack-id>:<name>` form spelled out.
105
+ */
106
+ function resolvePolicyNames(names: string[]): { builtins: string[]; packs: PackPolicyRef[] } {
107
+ const builtins: string[] = [];
108
+ const packs: PackPolicyRef[] = [];
109
+ const unknown: string[] = [];
110
+
111
+ let installed: ResolvedPack[] = [];
112
+ try {
113
+ installed = readInstalledPacks().packs;
114
+ } catch {
115
+ // No packs, or an unreadable manifest: names simply resolve as builtins and
116
+ // an unknown one gets the ordinary error. A listing-adjacent command must
117
+ // not fail because a pack manifest is corrupt.
118
+ }
119
+
120
+ const refsFor = (packId: string | null, policyName: string): PackPolicyRef[] =>
121
+ installed
122
+ .filter((pack) => (packId === null || pack.id === packId))
123
+ .filter((pack) => pack.policies.some((p) => p.name === policyName))
124
+ .map((pack) => ({
125
+ packId: pack.id,
126
+ packVersion: pack.version,
127
+ name: policyName,
128
+ disabledKey: `pack:${pack.id}@${pack.version}:${policyName}`,
129
+ }));
130
+
131
+ for (const raw of names) {
132
+ // A PACK first, then the compiled set. This order is the whole fix for
133
+ // `policy remove block-sudo` printing "Disabled 0" while `block-sudo` kept
134
+ // denying: the name resolved to a builtin, the command edited
135
+ // `enabledPolicies`, and `enabledPolicies` stopped deciding anything when
136
+ // this build stopped registering builtins. The pack is where the switch is.
137
+ const direct = refsFor(null, raw);
138
+ if (direct.length === 1) {
139
+ packs.push(direct[0]);
140
+ continue;
141
+ }
142
+ if (direct.length > 1) {
143
+ throw new CliError(
144
+ `"${raw}" is declared by ${direct.length} installed packs.\n` +
145
+ `Name the one you mean:\n` +
146
+ direct.map((m) => ` ${m.packId}:${m.name}`).join("\n"),
147
+ );
148
+ }
149
+ // No pack carries it. Falls back to the compiled name set, which is what a
150
+ // machine still running on the migration shim has.
151
+ if (VALID_POLICY_NAMES.has(raw)) {
152
+ builtins.push(raw);
153
+ continue;
154
+ }
155
+ // `acme/finance:block-big-refund` — a pack id holds a slash, never a colon,
156
+ // so the last colon separates them unambiguously.
157
+ const colon = raw.lastIndexOf(":");
158
+ const qualified = colon > 0
159
+ ? { packId: raw.slice(0, colon), name: raw.slice(colon + 1) }
160
+ : null;
161
+ const matches = qualified
162
+ ? refsFor(qualified.packId, qualified.name)
163
+ : refsFor(null, raw);
164
+
165
+ if (matches.length === 1) {
166
+ packs.push(matches[0]);
167
+ continue;
168
+ }
169
+ if (matches.length > 1) {
170
+ throw new CliError(
171
+ `"${raw}" is declared by ${matches.length} installed packs.\n` +
172
+ `Name the one you mean:\n` +
173
+ matches.map((m) => ` ${m.packId}:${m.name}`).join("\n"),
174
+ );
175
+ }
176
+ unknown.push(raw);
177
+ }
178
+
179
+ if (unknown.length > 0) {
180
+ const packNames = installed.flatMap((pack) =>
181
+ pack.policies.map((p) => `${pack.id}:${p.name}`),
182
+ );
70
183
  throw new CliError(
71
- `Unknown policy name(s): ${invalid.join(", ")}\n` +
72
- `Valid policies: ${validList}`
184
+ `Unknown policy name(s): ${unknown.join(", ")}\n` +
185
+ `Valid policies: ${[...VALID_POLICY_NAMES].join(", ")}` +
186
+ (packNames.length > 0 ? `\nFrom installed packs: ${packNames.join(", ")}` : ""),
187
+ );
188
+ }
189
+ return { builtins, packs };
190
+ }
191
+
192
+ /** Turn pack policies on or off, and say what happened. */
193
+ function applyPackPolicies(
194
+ refs: PackPolicyRef[],
195
+ on: boolean,
196
+ scope: HookScope,
197
+ cwd?: string,
198
+ ): void {
199
+ if (refs.length === 0) return;
200
+ for (const ref of refs) {
201
+ const result = setPackPolicyEnabled(ref.packId, ref.name, on);
202
+ if (!result.ok) {
203
+ throw new CliError(`Could not ${on ? "enable" : "disable"} ${ref.name}: ${result.reason}`);
204
+ }
205
+ }
206
+ if (on) {
207
+ // Clearing the dashboard's key too. The selection and the disabled key are
208
+ // two different switches for one policy, and leaving the second one set
209
+ // would report the policy enabled while it stayed off.
210
+ const config = readScopedHooksConfig(scope, cwd);
211
+ const keys = new Set(refs.map((r) => r.disabledKey));
212
+ const remaining = (config.disabledCustomPolicies ?? []).filter((k) => !keys.has(k));
213
+ if (remaining.length !== (config.disabledCustomPolicies ?? []).length) {
214
+ const next: HooksConfig = { ...config, disabledCustomPolicies: remaining };
215
+ if (remaining.length === 0) delete next.disabledCustomPolicies;
216
+ writeScopedHooksConfig(next, scope, cwd);
217
+ }
218
+ }
219
+ for (const ref of refs) {
220
+ console.log(
221
+ `${on ? "Enabled" : "Disabled"} ${ref.name} from pack ${ref.packId}@${ref.packVersion}.`,
222
+ );
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Refuse to "disable" a policy that will register anyway.
228
+ *
229
+ * Removing an `alwaysOn` name from `enabledPolicies` succeeds at the file level
230
+ * and changes nothing at the enforcement level, so without this the CLI reports
231
+ * a policy disabled while it keeps denying — the operator's mental model and the
232
+ * machine's behaviour diverge silently, which is the failure this policy exists
233
+ * to prevent in the first place.
234
+ */
235
+ function rejectAlwaysOnPolicies(names: string[]): void {
236
+ const alwaysOn = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => p.name));
237
+ const refused = names.filter((n) => alwaysOn.has(n));
238
+ if (refused.length > 0) {
239
+ throw new CliError(
240
+ `Cannot disable: ${refused.join(", ")}\n` +
241
+ `This policy stops an agent from switching off failproofai itself, so it ` +
242
+ `is always on and ships with the package. A guard the agent can disable ` +
243
+ `by the means it is meant to prevent is not a guard.`
73
244
  );
74
245
  }
75
246
  }
@@ -151,8 +322,21 @@ async function installHooksImpl(
151
322
  // Validate user input first before any system checks
152
323
  if (policyNames !== undefined && policyNames.length > 0) {
153
324
  const nonAllNames = policyNames.filter((n) => n !== "all");
154
- // Check unknown names first (most actionable error for the user)
155
- if (nonAllNames.length > 0) validatePolicyNames(nonAllNames);
325
+ // Check unknown names first (most actionable error for the user). Pack
326
+ // policies are applied here and taken out of the list: the rest of this
327
+ // function writes `enabledPolicies`, which is a builtin-only set.
328
+ if (nonAllNames.length > 0) {
329
+ const resolved = resolvePolicyNames(nonAllNames);
330
+ applyPackPolicies(resolved.packs, true, scope, cwd);
331
+ if (resolved.packs.length > 0) {
332
+ policyNames = policyNames.filter((n) => n === "all" || resolved.builtins.includes(n));
333
+ // Named ONLY pack policies: the work is done. Carrying on would resolve
334
+ // the failproofai binary and rewrite every CLI's settings to enable a
335
+ // set of builtins nobody asked about — and would fail outright on a
336
+ // machine where the binary is not on PATH, AFTER the pack change landed.
337
+ if (policyNames.length === 0) return;
338
+ }
339
+ }
156
340
  // Then check if "all" is mixed with valid specific names
157
341
  if (policyNames.includes("all") && nonAllNames.length > 0) {
158
342
  throw new CliError(
@@ -290,6 +474,55 @@ async function installHooksImpl(
290
474
  }
291
475
  }
292
476
  writeScopedHooksConfig(configToWrite, scope, cwd);
477
+
478
+ // Choosing policies IS selecting from the pack now. Nothing registers these
479
+ // names from this build any more, so writing `enabledPolicies` and stopping
480
+ // would leave a freshly set-up machine enforcing nothing at all — which is
481
+ // the failure mode this whole product exists to prevent.
482
+ //
483
+ // Fetched from the pack's GitHub release: there is no copy in this package,
484
+ // cannot fail behind a proxy, and the machine is guarded the moment it is
485
+ // configured rather than the moment it next reaches github.com.
486
+ // The always-on guard is excluded: a pack may not declare `alwaysOn`, so the
487
+ // pack does not carry it and asking for it by name is a selection the pack
488
+ // cannot satisfy. It ships compiled in and registers regardless.
489
+ const alwaysOnNames = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => p.name));
490
+ const fromPack = selectedPolicies.filter((name) => !alwaysOnNames.has(name));
491
+ if (fromPack.length > 0) {
492
+ // ONLY when there is no pack yet. Fetching the core pack with
493
+ // `only: <selection>` REPLACES whatever the machine had chosen, and
494
+ // `selectedPolicies` is derived from `enabledPolicies` — which is empty on a
495
+ // machine whose pack came from `pack add core`. `policy add block-rm-rf`
496
+ // therefore took a machine from ten guards to one, silently, and nine
497
+ // policies went from denying to allowing.
498
+ //
499
+ // With a pack already installed the names are switched on individually
500
+ // instead, which is additive and touches nothing else.
501
+ if (!hasInstalledPacks()) {
502
+ // Fetched, not unpacked from this package: there is no copy in here any
503
+ // more. That makes this the one path in `policies --install` that needs
504
+ // the network, so its failure is reported rather than thrown — the names
505
+ // are already written to config, and the no-pack fallback enforces them
506
+ // until a pack arrives.
507
+ try {
508
+ await addPack(CORE_SOURCE, { only: fromPack });
509
+ } catch (err) {
510
+ console.log(
511
+ `\nWarning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).\n` +
512
+ `Run \`failproofai policies add core\` once you are online.`,
513
+ );
514
+ }
515
+ } else {
516
+ for (const name of fromPack) {
517
+ for (const pack of readInstalledPacks().packs) {
518
+ if (pack.policies.some((p) => p.name === name)) {
519
+ setPackPolicyEnabled(pack.id, name, true);
520
+ break;
521
+ }
522
+ }
523
+ }
524
+ }
525
+ }
293
526
  console.log(`\nEnabled ${selectedPolicies.length} policy(ies): ${selectedPolicies.join(", ")}\n`);
294
527
  if (removeCustomHooks) {
295
528
  console.log("Custom hooks path cleared.");
@@ -434,7 +667,18 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
434
667
 
435
668
  // Remove specific policies from config (keep hooks installed)
436
669
  if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
437
- validatePolicyNames(policyNames);
670
+ const resolved = resolvePolicyNames(policyNames);
671
+ applyPackPolicies(resolved.packs, false, configScope, cwd);
672
+ policyNames = resolved.builtins;
673
+ rejectAlwaysOnPolicies(policyNames);
674
+ // Named ONLY pack policies: they are off now and there is nothing else to
675
+ // do. Falling through would reach the hook-removal path below with an empty
676
+ // name list, which is the "remove failproofai from every CLI" branch — so
677
+ // `--uninstall <a-pack-policy>` would have torn out every hook on the
678
+ // machine.
679
+ if (resolved.packs.length > 0 && policyNames.length === 0) return;
680
+ }
681
+ if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
438
682
  const config = readScopedHooksConfig(configScope, cwd);
439
683
  const removeSet = new Set(policyNames);
440
684
  const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
@@ -593,135 +837,88 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
593
837
  */
594
838
  export async function listHooks(cwd?: string): Promise<void> {
595
839
  const config = readMergedHooksConfig(cwd);
596
- const enabledSet = new Set(config.enabledPolicies);
597
840
  const disabledCustomSet = new Set(config.disabledCustomPolicies ?? []);
841
+ const opts = optsFor(process.stdout);
598
842
 
599
843
  // Determine which scopes have hooks installed (deduplicate when paths overlap, e.g. cwd === home)
600
844
  const uniqueScopes = deduplicateScopes(HOOK_SCOPES, cwd);
601
845
  const installedScopes = uniqueScopes.filter((s) => hooksInstalledInSettings(s, cwd));
602
846
 
603
- // Separate beta from regular policies
604
- const regularPolicies = BUILTIN_POLICIES.filter((p) => !p.beta);
605
- const betaPolicies = BUILTIN_POLICIES.filter((p) => p.beta);
606
-
607
- // Dynamic name column width based on longest policy name
608
- const nameColWidth = Math.max(...BUILTIN_POLICIES.map((p) => p.name.length)) + 2;
609
-
610
- // All known builtin policy names (for unknown policyParams key detection)
611
- const builtinPolicyNames = new Set(BUILTIN_POLICIES.map((p) => p.name));
612
-
613
- // Helper: print params summary lines beneath a policy row
614
- const printParamsSummary = (policyName: string, indent: string) => {
615
- const params = config.policyParams?.[policyName];
616
- if (!params) return;
617
- for (const [key, val] of Object.entries(params)) {
618
- console.log(`${indent} ${key}: ${JSON.stringify(val)}`);
847
+ // Names a `policyParams` key may legitimately use: every policy an installed
848
+ // pack carries. Previously the compiled catalog, which no longer describes
849
+ // what runs.
850
+ const knownPolicyNames = new Set<string>();
851
+ try {
852
+ for (const pack of readInstalledPacks().packs) {
853
+ for (const policy of pack.policies) knownPolicyNames.add(policy.name);
619
854
  }
620
- };
855
+ } catch {
856
+ // Unreadable manifest: skip the typo warning rather than invent one.
857
+ }
621
858
 
622
- const statusCol = 8;
623
- const printSimpleRow = (policy: { name: string; description: string }) => {
624
- const mark = enabledSet.has(policy.name) ? `\x1B[32m\u2713\x1B[0m` : " ";
625
- console.log(` ${mark}${" ".repeat(statusCol - 1)}${policy.name.padEnd(nameColWidth)}${policy.description}`);
626
- printParamsSummary(policy.name, ` ${" ".repeat(statusCol)}`);
627
- };
628
- const printBetaSection = (printRow: (p: { name: string; description: string }) => void) => {
629
- if (betaPolicies.length > 0) {
630
- console.log(`\n \x1B[2m\u2500\u2500 Beta \u2500\u2500\x1B[0m`);
631
- for (const policy of betaPolicies) printRow(policy);
859
+ const groups: Array<string[] | null> = [];
860
+ const packCount = (() => {
861
+ try {
862
+ return readInstalledPacks().packs.reduce(
863
+ (n, pack) => n + (pack.enabled ?? pack.policies.map((p) => p.name)).length,
864
+ 0,
865
+ );
866
+ } catch {
867
+ return 0;
632
868
  }
633
- };
869
+ })();
870
+ groups.push(
871
+ title(
872
+ "failproofai policies",
873
+ installedScopes.length === 0
874
+ ? "not installed"
875
+ : `${installedScopes.join(" + ")} · ${packCount} on`,
876
+ opts,
877
+ ),
878
+ );
634
879
 
635
880
  if (installedScopes.length === 0) {
636
- // State A: No hooks installed — show table with configured state + descriptions
637
- console.log("\nFailproof AI Policies \u2014 not installed\n");
638
-
639
- console.log(` ${"Status".padEnd(statusCol)}${"Name".padEnd(nameColWidth)}Description`);
640
- console.log(` ${"\u2500".repeat(6)} ${"\u2500".repeat(nameColWidth - 2)} ${"\u2500".repeat(38)}`);
641
-
642
- for (const policy of regularPolicies) printSimpleRow(policy);
643
- printBetaSection(printSimpleRow);
644
-
645
- if (config.enabledPolicies.length > 0) {
646
- console.log("\n Policies not installed. Run `failproofai policies --install` to activate.");
647
- } else {
648
- console.log("\n Run `failproofai policies --install` to get started.");
649
- }
650
- console.log(` Config: ${globalPolicyConfigFile()}\n`);
651
- } else if (installedScopes.length === 1) {
652
- // State B: Single scope — table with header row
653
- const scope = installedScopes[0];
654
- console.log(`\nFailproof AI Hook Policies (${scope})\n`);
655
-
656
- console.log(` ${"Status".padEnd(statusCol)}${"Name".padEnd(nameColWidth)}Description`);
657
- console.log(` ${"\u2500".repeat(6)} ${"\u2500".repeat(nameColWidth - 2)} ${"\u2500".repeat(38)}`);
658
-
659
- for (const policy of regularPolicies) printSimpleRow(policy);
660
- printBetaSection(printSimpleRow);
661
-
662
- console.log(`\n Config: ${globalPolicyConfigFile()}\n`);
663
- } else {
664
- // State C: Multiple scopes — column table
665
- const COL = 9;
666
- const scopeLabelMap: Record<HookScope, string> = {
667
- user: "User",
668
- project: "Project",
669
- local: "Local",
670
- };
671
-
672
- console.log("\nFailproof AI Hook Policies\n");
673
-
674
- // Header with only installed scope columns + separator
675
- const buildScopePrefix = () => {
676
- let s = " ";
677
- for (const sc of installedScopes) s += scopeLabelMap[sc].padEnd(COL);
678
- return s;
679
- };
680
- const scopeHeaderWidth = installedScopes.length * COL;
681
- console.log(`${buildScopePrefix()}${"Name".padEnd(nameColWidth)}Description`);
682
- console.log(` ${"\u2500".repeat(scopeHeaderWidth)}${"\u2500".repeat(nameColWidth)}${"\u2500".repeat(38)}`);
683
-
684
- const printMultiScopeRow = (policy: { name: string; description: string }) => {
685
- const enabled = enabledSet.has(policy.name);
686
- let row = " ";
687
- for (const _scope of installedScopes) {
688
- if (enabled) {
689
- row += `\x1B[32m\u2713 ON\x1B[0m` + " ".repeat(COL - 4);
690
- } else {
691
- row += " OFF" + " ".repeat(COL - 5);
692
- }
693
- }
694
- row += policy.name.padEnd(nameColWidth) + policy.description;
695
- console.log(row);
696
- printParamsSummary(policy.name, ` ${" ".repeat(scopeHeaderWidth)}`);
697
- };
698
-
699
- for (const policy of regularPolicies) printMultiScopeRow(policy);
700
-
701
- if (betaPolicies.length > 0) {
702
- console.log(`\n \x1B[2m\u2500\u2500 Beta \u2500\u2500\x1B[0m`);
703
- for (const policy of betaPolicies) printMultiScopeRow(policy);
704
- }
705
-
706
- console.log(`\n Config: ${globalPolicyConfigFile()}`);
707
-
708
- // Multi-scope warning
709
- const scopeNames = installedScopes.join(", ");
710
- console.log();
711
- console.log(`\x1B[33m\u26A0 Hooks in multiple scopes (${scopeNames}).\x1B[0m`);
712
- console.log(" Consider keeping one. Remove with: failproofai policies --uninstall --scope <scope>\n");
881
+ groups.push(
882
+ nextStep(
883
+ "failproofai policies --install",
884
+ config.enabledPolicies.length > 0
885
+ ? "These are configured but NOT installed — no hook is running them:"
886
+ : "Nothing is installed yet. Get started with:",
887
+ opts,
888
+ ),
889
+ );
890
+ }
891
+ // Held back to the end rather than printed here. Everything below this point
892
+ // is another section of the same listing, and a footer in the middle of it
893
+ // reads as the end of the output — while a warning at the very end is the one
894
+ // a reader scrolling back from their prompt actually sees.
895
+ const footer: Array<string[] | null> = [note(`Config: ${globalPolicyConfigFile()}`, opts)];
896
+
897
+ if (installedScopes.length > 1) {
898
+ footer.push(
899
+ warning(
900
+ [
901
+ `Hooks in multiple scopes (${installedScopes.join(", ")}).`,
902
+ "Consider keeping one. Remove with: failproofai policies --uninstall --scope <scope>",
903
+ ],
904
+ opts,
905
+ ),
906
+ );
713
907
  }
714
908
 
715
909
  // Warn about unknown policyParams keys
716
910
  if (config.policyParams) {
717
911
  const unknownKeys: string[] = [];
718
912
  for (const key of Object.keys(config.policyParams)) {
719
- if (!builtinPolicyNames.has(key)) {
720
- console.log(` \x1B[33mWarning: unknown policyParams key "${key}" — possible typo\x1B[0m`);
721
- unknownKeys.push(key);
722
- }
913
+ if (knownPolicyNames.size > 0 && !knownPolicyNames.has(key)) unknownKeys.push(key);
723
914
  }
724
915
  if (unknownKeys.length > 0) {
916
+ footer.push(
917
+ warning(
918
+ unknownKeys.map((key) => `unknown policyParams key "${key}" — possible typo`),
919
+ opts,
920
+ ),
921
+ );
725
922
  try {
726
923
  await trackHookEvent(getInstanceId(), "policy_params_validation_warning", {
727
924
  unknown_keys_count: unknownKeys.length,
@@ -734,30 +931,38 @@ export async function listHooks(cwd?: string): Promise<void> {
734
931
  // Explicit Custom Policies section
735
932
  const explicitPaths = configuredCustomPolicyPaths(config);
736
933
  if (explicitPaths.length > 0) {
737
- console.log(`\n \u2500\u2500 Custom Policies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
934
+ groups.push(rule("Custom Policies", opts));
738
935
  for (const path of explicitPaths) {
739
936
  // Enforcement resolves configured paths from the project config root.
740
937
  // Use the same canonical path here so the ID checked by the CLI exactly
741
938
  // matches the ID written by the dashboard.
742
939
  const absPath = resolve(findProjectConfigDir(cwd ?? process.cwd()), path);
743
- console.log(` ${absPath}`);
940
+ groups.push(note(absPath, opts));
744
941
  if (!existsSync(absPath)) {
745
- console.log(` \x1B[31m\u2717 File not found: ${absPath}\x1B[0m`);
942
+ groups.push(warning([`file not found: ${absPath}`], opts));
746
943
  continue;
747
944
  }
748
945
  const hooks = await loadCustomHooks(absPath);
749
946
  if (hooks.length === 0) {
750
- console.log(` \x1B[31m\u2717 ERR failed to load (check ~/.failproofai/logs/hooks.log)\x1B[0m`);
947
+ groups.push(
948
+ warning(["failed to load (check ~/.failproofai/logs/hooks.log)"], opts),
949
+ );
751
950
  } else {
752
- const descColWidth = nameColWidth;
753
- for (const hook of hooks) {
754
- const disabled = disabledCustomSet.has(`custom:${absPath}:${hook.name}`);
755
- const status = disabled ? "\x1B[2m OFF\x1B[0m" : "\x1B[32m\u2713 ON\x1B[0m";
756
- console.log(` ${status} ${hook.name.padEnd(descColWidth)}${hook.description ?? ""}`);
757
- }
951
+ groups.push(
952
+ table(
953
+ {
954
+ head: ["", "Name", "Description"],
955
+ rows: hooks.map((hook) => [
956
+ chip(disabledCustomSet.has(`custom:${absPath}:${hook.name}`) ? "off" : "on", opts),
957
+ hook.name,
958
+ hook.description ?? "",
959
+ ]),
960
+ },
961
+ opts,
962
+ ),
963
+ );
758
964
  }
759
965
  }
760
- console.log();
761
966
  }
762
967
 
763
968
  // Convention Policies section (.failproofai/policies/*policies.{js,mjs,ts})
@@ -801,43 +1006,82 @@ export async function listHooks(cwd?: string): Promise<void> {
801
1006
  for (const t of targets) discovered[t].push({ file, hooks });
802
1007
  };
803
1008
 
804
- console.log(`\n \u2500\u2500 Convention Policies \u2014 ${label} (${dir}) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
805
- // `nameColWidth` is sized to the longest BUILTIN name, but a convention
806
- // filename can be longer \u2014 `padEnd` is then a no-op and the hook count runs
807
- // straight into the filename with no space
808
- // (`enforce-bengaluru-event-links-policies.mjs1 hook(s)`). Widen to fit the
809
- // files actually being printed, keeping a two-space gutter.
810
- const colWidth = Math.max(nameColWidth, ...files.map((f) => basename(f).length + 2));
1009
+ groups.push(rule(`Convention Policies ${label}`, opts));
1010
+ groups.push(note(dir, opts));
1011
+ const rows: TableRow[] = [];
811
1012
  for (const file of files) {
1013
+ const filename = basename(file);
812
1014
  try {
813
1015
  const hooks = await loadCustomHooks(file);
814
- const filename = basename(file);
815
1016
  record(filename, hooks.map((h) => h.name));
816
1017
  if (hooks.length === 0) {
817
- console.log(` \x1B[31m\u2717\x1B[0m ${filename.padEnd(colWidth)}\x1B[31mfailed to load\x1B[0m`);
818
- } else {
819
- const hookStates = hooks.map((hook) => ({
820
- hook,
821
- disabled: disabledCustomSet.has(`convention:${policyScope}:${filename}:${hook.name}`),
822
- }));
823
- const disabledCount = hookStates.filter((entry) => entry.disabled).length;
824
- const status = disabledCount === 0
825
- ? "\x1B[32m\u2713 ON\x1B[0m"
826
- : disabledCount === hooks.length
827
- ? "\x1B[2m OFF\x1B[0m"
828
- : "\x1B[33m\u25D0 MIXED\x1B[0m";
829
- const hookSummary = hookStates
830
- .map(({ hook, disabled }) => `${hook.name}${disabled ? " (OFF)" : ""}`)
831
- .join(", ");
832
- console.log(` ${status} ${filename.padEnd(colWidth)}${hooks.length} hook(s): ${hookSummary}`);
1018
+ rows.push({ cells: [chip("failed", opts), filename, "failed to load"] });
1019
+ continue;
833
1020
  }
1021
+ const hookStates = hooks.map((hook) => ({
1022
+ hook,
1023
+ disabled: disabledCustomSet.has(`convention:${policyScope}:${filename}:${hook.name}`),
1024
+ }));
1025
+ const disabledCount = hookStates.filter((entry) => entry.disabled).length;
1026
+ const state: ChipState =
1027
+ disabledCount === 0 ? "on" : disabledCount === hooks.length ? "off" : "mixed";
1028
+ const hookSummary = hookStates
1029
+ .map(({ hook, disabled }) => `${hook.name}${disabled ? " (OFF)" : ""}`)
1030
+ .join(", ");
1031
+ rows.push({
1032
+ cells: [chip(state, opts), filename, `${hooks.length} hook(s): ${hookSummary}`],
1033
+ });
834
1034
  } catch {
835
- const filename = basename(file);
836
1035
  record(filename, []);
837
- console.log(` \x1B[31m\u2717\x1B[0m ${filename.padEnd(colWidth)}\x1B[31merror\x1B[0m`);
1036
+ rows.push({ cells: [chip("failed", opts), filename, "error"] });
838
1037
  }
839
1038
  }
840
- console.log();
1039
+ groups.push(table({ head: ["", "File", "Hooks"], rows }, opts));
1040
+ }
1041
+
1042
+ // Installed packs. They enforce on this machine exactly like every section
1043
+ // above, and until now the only way to see one was `failproofai policies` —
1044
+ // so the command that answers "what is enforcing here?" answered it with a
1045
+ // subset, for the one source a person had to go out of their way to install.
1046
+ try {
1047
+ const { packs, errors } = readInstalledPacks();
1048
+ for (const pack of packs) {
1049
+ const taken = pack.enabled ?? pack.policies.map((p) => p.name);
1050
+ groups.push(rule(`Pack — ${pack.id}@${pack.version}`, opts));
1051
+ groups.push(
1052
+ table(
1053
+ {
1054
+ head: ["", "Name", "Description"],
1055
+ rows: pack.policies.map((policy) => {
1056
+ const disabled = disabledCustomSet.has(
1057
+ `pack:${pack.id}@${pack.version}:${policy.name}`,
1058
+ );
1059
+ // `observe` evaluates and discards its verdict, so a row reading
1060
+ // ON would claim enforcement the pack deliberately is not doing.
1061
+ const state: ChipState =
1062
+ pack.effect === "observe"
1063
+ ? "observe"
1064
+ : !taken.includes(policy.name) || disabled
1065
+ ? "off"
1066
+ : "pack";
1067
+ return [chip(state, opts), policy.name, policy.description];
1068
+ }),
1069
+ },
1070
+ opts,
1071
+ ),
1072
+ );
1073
+ }
1074
+ if (errors.length > 0) {
1075
+ groups.push(
1076
+ warning(
1077
+ errors.map((err) => `pack ${err.id ?? "(unnamed)"} will not load: ${err.reason}`),
1078
+ opts,
1079
+ ),
1080
+ );
1081
+ }
1082
+ } catch {
1083
+ // Same rule as the cloud section below: a listing must not be the thing
1084
+ // that turns an unreadable manifest into a broken command.
841
1085
  }
842
1086
 
843
1087
  // Cloud-managed policies. These enforce on this machine exactly like the two
@@ -852,21 +1096,27 @@ export async function listHooks(cwd?: string): Promise<void> {
852
1096
  try {
853
1097
  const cloud = readActiveCloudManagedPolicies();
854
1098
  if (cloud.length > 0) {
855
- const gen = cloud[0].deployment;
856
- console.log(
857
- `\n \u2500\u2500 Cloud-managed \u2014 deployment ${gen} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
1099
+ groups.push(rule(`Cloud-managed deployment ${cloud[0].deployment}`, opts));
1100
+ groups.push(
1101
+ table(
1102
+ {
1103
+ head: ["", "Policy", "Version"],
1104
+ rows: cloud.map((artifact) => [
1105
+ // `observe` is evaluated and then has its verdict discarded, so a
1106
+ // row that read "ON" would claim enforcement this policy
1107
+ // deliberately is not doing.
1108
+ chip(artifact.effect === "observe" ? "observe" : "cloud", opts),
1109
+ artifact.id,
1110
+ `v${artifact.version}`,
1111
+ ]),
1112
+ flex: 1,
1113
+ },
1114
+ opts,
1115
+ ),
1116
+ );
1117
+ groups.push(
1118
+ note("Managed from the dashboard — not switchable with `failproofai policies`.", opts),
858
1119
  );
859
- const colWidth = Math.max(nameColWidth, ...cloud.map((c) => c.id.length + 2));
860
- for (const artifact of cloud) {
861
- // `observe` is evaluated and then has its verdict discarded, so a row
862
- // that read "ON" would claim enforcement this policy deliberately is
863
- // not doing.
864
- const status =
865
- artifact.effect === "observe" ? "\x1B[33m\u25D0 OBS\x1B[0m" : "\x1B[32m\u2713 ON\x1B[0m";
866
- console.log(` ${status} ${artifact.id.padEnd(colWidth)}v${artifact.version}`);
867
- }
868
- console.log("\n Managed from the dashboard \u2014 not switchable with `failproofai policies`.");
869
- console.log();
870
1120
  }
871
1121
  } catch {
872
1122
  // A machine with no deployment, or an unreadable manifest, simply has no
@@ -874,6 +1124,8 @@ export async function listHooks(cwd?: string): Promise<void> {
874
1124
  // the thing that turns a bad manifest into a broken command.
875
1125
  }
876
1126
 
1127
+ printBlock(process.stdout, stack(...groups, ...footer));
1128
+
877
1129
  // Mirror what was just listed into the USER config. Safe here because
878
1130
  // `failproofai policies` is a one-shot command — never do this on the hook
879
1131
  // path (see the HooksConfig.conventionPolicies doc comment).