failproofai 1.0.2-beta.2 → 1.0.2-beta.4

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 (183) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +6 -6
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/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.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/build-manifest.json +3 -3
  15. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  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 +14 -14
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js +2 -3
  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 +4 -5
  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 +3 -4
  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/build-manifest.json +3 -3
  38. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +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 +14 -14
  43. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  44. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  45. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  46. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  47. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  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/build-manifest.json +3 -3
  51. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  52. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  53. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  56. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  65. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +3 -3
  69. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  70. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/chunks/{[externals]__0e32-7c._.js → [externals]__1qzqcio._.js} +1 -1
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +3 -0
  75. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0clqfab._.js +3 -0
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0igxt32._.js → [root-of-the-server]__1p8poxd._.js} +2 -2
  80. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +6 -5
  82. package/.next/standalone/.next/server/chunks/_1lbh81u._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  85. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  87. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js} +1 -1
  88. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  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 +2 -2
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07gm3zl._.js +4 -4
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14muoc4._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/ssr/{src_hooks_08o7bqo._.js → [root-of-the-server]__15r6r7w._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tag8_-._.js +5 -0
  103. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/{_1oggjel._.js → _0ovx4lz._.js} +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/_1dkh76z._.js +23 -0
  106. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  114. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  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 +19 -19
  120. package/.next/standalone/.next/static/chunks/{1yw71gn8vqco1.js → 00_t-xvc59tkb.js} +2 -2
  121. package/.next/standalone/.next/static/chunks/{3jjmdqqzem21x.js → 07j2u70_3qsx8.js} +1 -1
  122. package/.next/standalone/.next/static/chunks/{2fzxl7n717pbc.js → 0d_re32wscs0b.js} +1 -1
  123. package/.next/standalone/.next/static/chunks/{0ykd_go4oahan.js → 0ds64uculrze8.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/{0nnbtp0w4sxrr.js → 0k_yvjc1yz0pq.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/0lshnlpy1m6bc.js +1 -0
  126. package/.next/standalone/.next/static/chunks/{1da5a_a-3btas.js → 1o0ahew9568ls.js} +1 -1
  127. package/.next/standalone/.next/static/chunks/{1za08zjcctyil.js → 1oz0svxwt9574.js} +2 -2
  128. package/.next/standalone/.next/static/chunks/{29eh4k77otcw6.js → 1q76jk_1_wgjq.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/{2axi5ehjbld9h.js → 2817_8e94ah9z.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/{1nzy5jc_vf9e0.js → 2dbi_x0c8n8fi.js} +1 -1
  131. package/.next/standalone/.next/static/chunks/{1fac8krph-zgp.js → 354-n7p7labpn.js} +1 -1
  132. package/.next/standalone/.next/static/chunks/turbopack-17utl_jw0wj11.js +1 -0
  133. package/.next/standalone/SECURITY.md +18 -3
  134. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +7 -0
  135. package/.next/standalone/fp-cloud-cli/uv.lock +6 -6
  136. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  137. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  138. package/.next/standalone/node_modules/next/dist/client/components/segment-cache/cache.js +23 -34
  139. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
  140. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
  141. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  142. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  143. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  144. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  145. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  146. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  147. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  148. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  149. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  150. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  151. package/.next/standalone/node_modules/next/package.json +10 -10
  152. package/.next/standalone/package.json +19 -19
  153. package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
  154. package/.next/standalone/sdk/python/uv.lock +23 -23
  155. package/.next/standalone/server.js +1 -1
  156. package/bin/failproofai.mjs +131 -55
  157. package/dist/cli.mjs +12940 -12627
  158. package/dist/worker.mjs +11 -1
  159. package/package.json +19 -19
  160. package/src/audit/cache.ts +34 -29
  161. package/src/audit/replay.ts +15 -91
  162. package/src/hooks/configure-wizard.ts +16 -48
  163. package/src/hooks/handler.ts +8 -1
  164. package/src/hooks/manager.ts +24 -2
  165. package/src/hooks/pack-cli.ts +572 -43
  166. package/src/hooks/pack-manifest.ts +28 -0
  167. package/src/hooks/pack-store.ts +20 -28
  168. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +0 -3
  169. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +0 -3
  170. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +0 -3
  171. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +0 -3
  172. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +0 -3
  173. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +0 -3
  174. package/.next/standalone/.next/server/chunks/_185gyk9._.js +0 -3
  175. package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +0 -12
  176. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__090sxxw._.js +0 -3
  177. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +0 -5
  178. package/.next/standalone/.next/server/chunks/ssr/_0wb8osu._.js +0 -23
  179. package/.next/standalone/.next/static/chunks/140ip21rqmelt.js +0 -1
  180. package/.next/standalone/.next/static/chunks/turbopack-2vqpa-dts1gy4.js +0 -1
  181. /package/.next/standalone/.next/static/{A7czO1PZdTcNzLGa_H1sm → 0iNzkTsP6rh14Rj5WL17o}/_buildManifest.js +0 -0
  182. /package/.next/standalone/.next/static/{A7czO1PZdTcNzLGa_H1sm → 0iNzkTsP6rh14Rj5WL17o}/_clientMiddlewareManifest.js +0 -0
  183. /package/.next/standalone/.next/static/{A7czO1PZdTcNzLGa_H1sm → 0iNzkTsP6rh14Rj5WL17o}/_ssgManifest.js +0 -0
package/dist/worker.mjs CHANGED
@@ -2559,7 +2559,7 @@ var init_builtin_policies = __esm(() => {
2559
2559
  });
2560
2560
 
2561
2561
  // package.json
2562
- var version = "1.0.2-beta.2";
2562
+ var version = "1.0.2-beta.4";
2563
2563
  var init_package = () => {};
2564
2564
 
2565
2565
  // src/posthog-key.ts
@@ -8972,7 +8972,15 @@ function parsePack(root, value) {
8972
8972
  }
8973
8973
  enabled = raw.enabled.filter((n) => names.has(n));
8974
8974
  }
8975
+ let clis = null;
8976
+ if (raw.clis !== undefined) {
8977
+ if (!Array.isArray(raw.clis) || raw.clis.some((c) => typeof c !== "string")) {
8978
+ throw new Error(`pack ${raw.id} clis is not an array of agent names`);
8979
+ }
8980
+ clis = raw.clis;
8981
+ }
8975
8982
  return {
8983
+ clis,
8976
8984
  id: identity.id,
8977
8985
  version: identity.version,
8978
8986
  source: raw.source,
@@ -9435,6 +9443,8 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
9435
9443
  continue;
9436
9444
  if (!cloudManaged && activePause)
9437
9445
  continue;
9446
+ if (pack?.clis && !pack.clis.includes(cli))
9447
+ continue;
9438
9448
  if (pack?.enabled && !pack.enabled.includes(hook.name))
9439
9449
  continue;
9440
9450
  if (pack && enabledBuiltinNames.has(hook.name))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.2-beta.2",
3
+ "version": "1.0.2-beta.4",
4
4
  "description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs",
@@ -80,33 +80,33 @@
80
80
  "access": "public"
81
81
  },
82
82
  "devDependencies": {
83
- "@anthropic-ai/sdk": "^0.117.1",
83
+ "@anthropic-ai/sdk": "^0.120.0",
84
84
  "@mdx-js/mdx": "^3.1.1",
85
85
  "@tailwindcss/postcss": "^4.3.1",
86
- "@tanstack/react-virtual": "^3.14.9",
86
+ "@tanstack/react-virtual": "^3.14.10",
87
87
  "@testing-library/jest-dom": "^7.0.0",
88
88
  "@testing-library/react": "^16.3.2",
89
- "@testing-library/user-event": "^14.6.1",
89
+ "@testing-library/user-event": "^14.6.5",
90
90
  "@types/node": "26.2.0",
91
91
  "@types/react": "19.2.18",
92
92
  "@types/react-dom": "^19.2.3",
93
- "@vitejs/plugin-react": "^6.0.5",
93
+ "@vitejs/plugin-react": "^6.1.0",
94
94
  "clsx": "^2.1.1",
95
- "eslint": "^10.5.0",
96
- "eslint-config-next": "^16.2.9",
95
+ "eslint": "^10.9.0",
96
+ "eslint-config-next": "^16.3.2",
97
97
  "jsdom": "^30.0.1",
98
- "lucide-react": "^1.28.0",
99
- "next": "^16.2.11",
98
+ "lucide-react": "^1.33.0",
99
+ "next": "^16.3.2",
100
100
  "react": "^19.2.4",
101
101
  "react-dom": "^19.2.4",
102
102
  "tailwind-merge": "^3.4.0",
103
103
  "tailwindcss": "^4.3.1",
104
104
  "typescript": "6.0.3",
105
- "vitest": "^4.1.9"
105
+ "vitest": "^4.1.11"
106
106
  },
107
107
  "dependencies": {
108
108
  "html-to-image": "^1.11.13",
109
- "posthog-node": "^5.47.7",
109
+ "posthog-node": "^5.50.0",
110
110
  "sql.js": "^1.14.1",
111
111
  "yaml": "^2.9.0"
112
112
  },
@@ -120,15 +120,15 @@
120
120
  "sharp": "0.35.0"
121
121
  },
122
122
  "optionalDependencies": {
123
- "@failproofai/failproofaid-linux-x64": "1.0.2-beta.2",
124
- "@failproofai/failproofaid-linux-arm64": "1.0.2-beta.2",
125
- "@failproofai/failproofaid-darwin-x64": "1.0.2-beta.2",
126
- "@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.2"
123
+ "@failproofai/failproofaid-linux-x64": "1.0.2-beta.4",
124
+ "@failproofai/failproofaid-linux-arm64": "1.0.2-beta.4",
125
+ "@failproofai/failproofaid-darwin-x64": "1.0.2-beta.4",
126
+ "@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.4"
127
127
  },
128
128
  "failproofaidBinaries": {
129
- "linux-x64": "fcfa5ae234e66eb6b57ec28445900c5f5a231f5f3a1fb0aeaca281d81f74c81e",
130
- "linux-arm64": "a2bb9894d311a86740170128a706a9503fecc15ac5cbbc4587243151175a671c",
131
- "darwin-x64": "97c1054b6229824ff7a9cbee67f3c1e2608db9e0b38a0dfc1adb21bf772a9666",
132
- "darwin-arm64": "eb60b5fd3c1727f0d5406961367e2ea9b6c7fbc0b975247e7db664d502863f13"
129
+ "linux-x64": "08bc00a7f8d7c80bf898471ce59c57f4e96c1420080009cc933b2ddeb4f23750",
130
+ "linux-arm64": "1167d809628cff04c38efbc0eaa83ca26ab8de2878bed0ef59ca43054173eea7",
131
+ "darwin-x64": "6ed32db21af6869c4475e753e524ad8e128436674209ad778421b240a4bd5c53",
132
+ "darwin-arm64": "3843de034c9d6b11f77c1f423e6201ad6a77b7eff3b3d4460ba324c5aeb62021"
133
133
  }
134
134
  }
@@ -13,7 +13,6 @@ import { createHash } from "node:crypto";
13
13
  import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
14
14
  import { join } from "node:path";
15
15
  import { BUILTIN_POLICIES } from "../hooks/builtin-policies";
16
- import { readInstalledPacks } from "../hooks/pack-manifest";
17
16
  import { AUDIT_DETECTORS } from "./detectors";
18
17
  import type { TranscriptAuditResult } from "./types";
19
18
  import { auditCacheDir } from "../hooks/fp-home";
@@ -22,23 +21,27 @@ let cachedEngineVersion: string | null = null;
22
21
  let cachedDetectorVersion: string | null = null;
23
22
 
24
23
  /**
25
- * Hash of every builtin policy's name + function body, PLUS the identity of every
26
- * installed pack. Changes when policy code changes, invalidating downstream
27
- * caches.
24
+ * Hash of every builtin policy's name and function body. Changes when policy
25
+ * code changes, invalidating downstream caches.
28
26
  *
29
- * Packs belong in this key because they change what a machine would have caught,
30
- * which is exactly what a cached audit result claims to know. They are folded in
31
- * by `id|version|sha256` rather than by source text: the pack's bytes are already
32
- * digest-pinned, and hashing the loaded source instead would drag in the
33
- * per-load temporary filename the loader rewrites into every import specifier
34
- * which changes on every single run and would cold-rescan the whole history each
35
- * time.
27
+ * Nothing else is in it, and specifically not installed packs. The audit
28
+ * evaluates the implementations compiled into this build and only those
29
+ * `replay.ts` iterates `BUILTIN_POLICIES` and never reads the installed packs
30
+ * so a pack cannot move an audit result, and keying on one meant every install
31
+ * or removal cold-rescanned the entire history to reproduce the answers it
32
+ * already had. The key it replaced folded packs in by `id|version|sha256`; the
33
+ * rationale was that packs "change what a machine would have caught", which is
34
+ * true of enforcement and was never true of this replay.
36
35
  *
37
- * **A machine with no packs must hash byte-identically to a build with no pack
38
- * support at all.** Otherwise merely shipping this feature invalidates every
39
- * user's audit cache and forces a ~104-second cold rescan on upgrade, for a
40
- * capability they are not using. Hence the empty pack set contributes nothing —
41
- * not an empty line, not a separator.
36
+ * **A machine with no packs hashes byte-identically to the old key**, because
37
+ * that one contributed nothing for an empty pack set — not an empty line, not a
38
+ * separator. So this change costs a rescan only to machines that had a pack
39
+ * installed, and costs it exactly once.
40
+ *
41
+ * It is deliberately NOT the source text of anything loaded at runtime: the
42
+ * loader rewrites a per-load temporary filename into every import specifier, so
43
+ * hashing loaded source would change on every single run and cold-rescan the
44
+ * whole history each time.
42
45
  */
43
46
  function getEngineVersion(): string {
44
47
  if (cachedEngineVersion) return cachedEngineVersion;
@@ -46,19 +49,21 @@ function getEngineVersion(): string {
46
49
  .map((p) => `${p.name}|${p.fn.toString()}`)
47
50
  .sort()
48
51
  .join("\n");
49
- // Never throws by contract, but this runs inside the audit's own try/catch-free
50
- // path and a cache key is not worth taking the run down for.
51
- let packBlob = "";
52
- try {
53
- packBlob = readInstalledPacks()
54
- .packs.map((pack) => `${pack.id}|${pack.version}|${pack.sha256}`)
55
- .sort()
56
- .join("\n");
57
- } catch {
58
- packBlob = "";
59
- }
60
- const blob = packBlob ? `${builtinBlob}\n${packBlob}` : builtinBlob;
61
- cachedEngineVersion = createHash("sha1").update(blob).digest("hex").slice(0, 16);
52
+ // Packs are NOT in this key, and that is the whole point of the audit's design
53
+ // rather than an omission.
54
+ //
55
+ // `replay.ts` evaluates `BUILTIN_POLICIES` and nothing else — it never reads
56
+ // `readInstalledPacks()`, and third-party pack policies are never replayed. So
57
+ // an installed pack cannot change an audit result, and folding its identity in
58
+ // here invalidated every cached transcript on a change that provably produces
59
+ // the same answers: a full rescan of thousands of transcripts to arrive back
60
+ // where it started. That was survivable while packs were rare; policies ARE
61
+ // packs now, so installing or removing one is routine.
62
+ //
63
+ // If audit is ever extended to evaluate what a pack would actually have
64
+ // caught, this key has to carry pack identity again — and that change belongs
65
+ // in the same commit as the one that makes it true.
66
+ cachedEngineVersion = createHash("sha1").update(builtinBlob).digest("hex").slice(0, 16);
62
67
  return cachedEngineVersion;
63
68
  }
64
69
 
@@ -13,19 +13,13 @@
13
13
  * execSync against live git, so they never fire on PreToolUse/PostToolUse
14
14
  * replay — no explicit skip needed.
15
15
  */
16
- import { resolve } from "node:path";
17
- import { existsSync } from "node:fs";
18
16
  import type { EvaluationResult } from "../hooks/policy-evaluator";
19
17
  import { evaluatePolicies } from "../hooks/policy-evaluator";
20
18
  import { BUILTIN_POLICIES, registerBuiltinPolicies } from "../hooks/builtin-policies";
21
- import { PACK_ENTRY_ASSET, bundledPackDir } from "../hooks/pack-store";
22
- import { loadCustomHooks } from "../hooks/custom-hooks-loader";
23
- import { clearCustomHooks } from "../hooks/custom-hooks-registry";
24
19
  import {
25
20
  clearPolicies,
26
21
  getAllPolicies,
27
22
  normalizePolicyName,
28
- registerPolicy,
29
23
  setAllPolicies,
30
24
  } from "../hooks/policy-registry";
31
25
  import type { RegisteredPolicy } from "../hooks/policy-types";
@@ -46,10 +40,20 @@ let initialized = false;
46
40
  * (e.g. the Next.js dashboard) doesn't wipe any prior policy registrations. */
47
41
  let savedSnapshot: RegisteredPolicy[] | null = null;
48
42
 
49
- /** Register every builtin policy (regardless of user config) so the replay
50
- * shows what *could* be caught, not just what's currently enabled. Called
51
- * once per `runAudit` invocation. Snapshots the existing registry so it can
52
- * be restored by `restoreReplay()` once the audit is done. */
43
+ /**
44
+ * Register every builtin policy (regardless of user config) so the replay shows
45
+ * what *could* be caught, not just what is currently enabled. Called once per
46
+ * `runAudit` invocation. Snapshots the existing registry so `restoreReplay()`
47
+ * can put it back.
48
+ *
49
+ * The implementations compiled into this build, and ONLY those. The audit used
50
+ * to prefer a vendored `policy-pack/` copy where one existed, which was
51
+ * meaningful while the package shipped that directory; it no longer ships it, so
52
+ * that branch could not fire in any published build and existed only to be
53
+ * misread as "the audit scores against installed packs". It does not, and must
54
+ * not: an audit is a fixed yardstick, and one that changed shape with whatever
55
+ * pack a machine happened to have could not be compared against its own history.
56
+ */
53
57
  export async function initReplay(): Promise<void> {
54
58
  if (initialized) return;
55
59
  savedSnapshot = getAllPolicies();
@@ -57,90 +61,10 @@ export async function initReplay(): Promise<void> {
57
61
  const enabled = BUILTIN_POLICIES
58
62
  .map((p) => p.name)
59
63
  .filter((n) => !SKIP_POLICIES.has(normalizePolicyName(n)));
60
- if (!(await registerFromVendoredPack(enabled))) registerBuiltinPolicies(enabled);
64
+ registerBuiltinPolicies(enabled);
61
65
  initialized = true;
62
66
  }
63
67
 
64
- /**
65
- * Register the audit's policies from the pack that ships inside the package,
66
- * rather than from implementations compiled into this build.
67
- *
68
- * The audit scores by RUNNING the policies — three of its four penalty buckets
69
- * are replay hits — so the day the builtins stop being compiled in, this is
70
- * what keeps `failproofai audit` reporting the same findings and the same score
71
- * for the same transcripts. The pack carries the identical functions:
72
- * `builtin-pack-conformance.test.ts` replays a corpus through both and asserts
73
- * the verdicts do not diverge.
74
- *
75
- * Two things it must get right, or the audit quietly changes:
76
- *
77
- * - The pack cannot carry `block-failproofai-commands`, because `alwaysOn` is
78
- * refused by the pack loader by design — a downloaded file that no local
79
- * command can switch off. It is registered from the compiled side, so the
80
- * replayed set is the same 39 either way.
81
- * - The skip list still applies. `warn-repeated-tool-calls` writes a per-session
82
- * sidecar into the user's real transcript directory on every evaluation.
83
- *
84
- * Returns false when there is no vendored pack to read — a source checkout that
85
- * has not run `build:pack`, or a tarball packed without it — and the caller
86
- * falls back to the compiled implementations. An audit that silently scored on
87
- * fewer policies would be worse than either.
88
- */
89
- async function registerFromVendoredPack(enabled: string[]): Promise<boolean> {
90
- const dir = bundledPackDir();
91
- if (!dir) return false;
92
- const entry = resolve(dir, PACK_ENTRY_ASSET);
93
- if (!existsSync(entry)) return false;
94
-
95
- const wanted = new Set(enabled);
96
- let hooks;
97
- try {
98
- clearCustomHooks();
99
- hooks = await loadCustomHooks(entry, { strict: true });
100
- } catch {
101
- return false;
102
- } finally {
103
- clearCustomHooks();
104
- }
105
- if (hooks.length === 0) return false;
106
- const fromPack = new Map(hooks.map((hook) => [hook.name, hook]));
107
-
108
- // Registered in CATALOG ORDER, taking the pack's function where it has one and
109
- // the compiled one for the guard the pack may not carry.
110
- //
111
- // Order is not cosmetic here. `getPoliciesForEvent` sorts by priority only and
112
- // V8's sort is stable, so registration order decides EVALUATION order, and
113
- // `evaluatePolicies` stops at the first deny — which means the order decides
114
- // which policy is CREDITED for a hit. Appending the always-on guard after the
115
- // pack instead of at its catalog position moved it from index 11 to last, and
116
- // re-attributed 3 events in a 23,477-event corpus: 456 -> 453 for the guard,
117
- // with the denies it used to shadow surfacing as block-force-push and
118
- // block-gh-pipeline. The audit's per-policy counts are what a user reads, so
119
- // that is a changed audit, not a changed internal.
120
- let registered = 0;
121
- for (const policy of BUILTIN_POLICIES) {
122
- if (!wanted.has(policy.name)) continue;
123
- const packHook = fromPack.get(policy.name);
124
- if (policy.alwaysOn || !packHook) {
125
- registerPolicy(policy.name, policy.description, policy.fn, policy.match, 0, policy.params);
126
- continue;
127
- }
128
- registerPolicy(
129
- packHook.name,
130
- packHook.description ?? policy.description,
131
- packHook.fn,
132
- packHook.match ?? policy.match,
133
- 0,
134
- policy.params,
135
- );
136
- registered += 1;
137
- }
138
- // Nothing came from the pack: it loaded but carried none of the names the
139
- // audit replays. Falling back is more honest than scoring on the compiled set
140
- // while claiming the pack lane ran.
141
- return registered > 0;
142
- }
143
-
144
68
  /** Restore the registry to whatever was there before `initReplay()`. Safe to
145
69
  * call when not initialized (no-op). Always paired with `initReplay()` in a
146
70
  * try/finally inside `runAudit()`. */
@@ -32,7 +32,6 @@ import { dirname, resolve, sep } from "node:path";
32
32
 
33
33
  import {
34
34
  selectOne,
35
- multiSelect,
36
35
  promptText,
37
36
  intro,
38
37
  outro,
@@ -223,7 +222,6 @@ export function buildAgentChoices(scope: HookScope, cwd: string): MultiChoice<In
223
222
  });
224
223
  }
225
224
 
226
- const ALL_CLIS = "__all_clis__";
227
225
 
228
226
  const DIM_NOTE = "(auto-loaded)";
229
227
 
@@ -938,54 +936,24 @@ export async function runConfigureWizard(io: WizardIO = {}): Promise<WizardResul
938
936
  // An "Everything available" row protects every supported CLI (detected +
939
937
  // set-up-ahead); when ticked it wins over the individual boxes.
940
938
  //
941
- // Always asked, never inferred. This is the one decision setup genuinely
942
- // cannot make for you: which agents on this machine should be guarded. The
943
- // detected ones are pre-ticked, so the common answer is a single keypress.
939
+ // Every supported agent, detected or not setup asks nothing about this.
944
940
  //
945
- // No `allowBack`: with the policy step and the scope fork both gone, there is
946
- // nothing inside setup to return to.
947
- let clisSel: string[];
948
- {
949
- const picked = await multiSelect<string>({
950
- message: "Which harnesses should it protect?",
951
- choices: [
952
- {
953
- label: "Everything available",
954
- value: ALL_CLIS,
955
- // Counts only what this scope can actually take expanding to all 12
956
- // under project scope is what crashed the apply on Hermes.
957
- hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
958
- // A selector, not a harness. Counting it gave "13 harnesses" for
959
- // the 12 supported CLIs, and listed "Everything available" among them.
960
- summaryExclude: true,
961
- },
962
- ...buildAgentChoices(primaryScope, cwd),
963
- ],
964
- minSelected: 1,
965
- summaryNoun: "harnesses",
966
- hint: "detected CLIs are pre-selected · space toggles · ctrl+a all · ↵ confirm",
967
- stdin,
968
- stdout,
969
- });
970
- if (picked === null) return cancel();
971
- clisSel = picked;
972
- }
973
- // Filter to what the chosen scopes support in BOTH branches: "Everything
974
- // available" must not expand to CLIs that cannot take any selected scope,
975
- // and a locked row can't be ticked but belt-and-braces keeps the invariant
976
- // local to the one place `clis` is built.
941
+ // It used to be the one question left, on the reasoning that which agents to
942
+ // guard is a real choice. It stopped being one when this package stopped
943
+ // shipping policies: hooks alone enforce nothing, so wiring them everywhere
944
+ // costs a config entry and changes no behaviour until a pack arrives. What it
945
+ // buys is that an agent installed NEXT WEEK is guarded from its first tool
946
+ // call, instead of running unguarded until somebody remembers to re-run setup
947
+ // — and nobody remembers, because nothing tells them to.
948
+ //
949
+ // ALL twelve, not the detected ones, for exactly that reason. The cost is
950
+ // honest and worth naming: hook config appears under `~/.cursor/`,
951
+ // `~/.factory/` and the rest for agents that may never be installed.
977
952
  //
978
- // The union across scopes, not the intersection: under "Both", a user-scope-
979
- // only gateway like Hermes is still installable via the user half, and
980
- // dropping it because project scope cannot take it would silently protect
981
- // less than the user asked for. `installHooks` is called per scope below and
982
- // skips what a given scope cannot take.
983
- const supported = new Set(scopes.flatMap((s) => clisSupportingScope(s)));
984
- const clis: IntegrationType[] = (
985
- clisSel.includes(ALL_CLIS)
986
- ? [...INTEGRATION_TYPES]
987
- : (clisSel.filter((v) => v !== ALL_CLIS) as IntegrationType[])
988
- ).filter((id) => supported.has(id));
953
+ // Which policies run, and on which of these agents, is chosen at
954
+ // `failproofai policies add` where the user is looking at a real list
955
+ // instead of answering in the abstract.
956
+ const clis: IntegrationType[] = [...clisSupportingScope(primaryScope)];
989
957
 
990
958
  // 4 — Connect this machine? Last, because by this point the user has decided
991
959
  // what to protect, so "would you like to see it in a dashboard?" follows
@@ -425,9 +425,16 @@ export async function evaluateHookEvent(
425
425
  if (!cloudManaged && policyId && disabledCustomPolicies.has(policyId)) continue;
426
426
  // Same rule for a session pause — it suspends local policy, never cloud.
427
427
  if (!cloudManaged && activePause) continue;
428
+ // A pack narrowed to particular agents does not fire on the others.
429
+ // Setup wires hooks into every supported CLI — hooks alone enforce
430
+ // nothing — so the choice of which agents a pack guards is made when the
431
+ // pack is installed, and this is where it takes effect. `clis: null`
432
+ // means every agent, which is what an install with no narrowing writes
433
+ // and what every pack installed before this field existed reads as.
434
+ if (pack?.clis && !pack.clis.includes(cli)) continue;
428
435
  // A pack's artifact registers everything it contains; the user may have
429
436
  // taken only some of it. `enabled: null` means the whole pack, which is
430
- // what `pack add` writes when no selection was made.
437
+ // what `policies add` writes when no selection was made.
431
438
  if (pack?.enabled && !pack.enabled.includes(hook.name)) continue;
432
439
  // A pack policy whose name is an ENABLED BUILTIN is the same guard
433
440
  // twice. The two register under different keys — `failproofai/block-sudo`
@@ -9,7 +9,7 @@ import { execSync } from "node:child_process";
9
9
  import { existsSync } from "node:fs";
10
10
  import { resolve, basename } from "node:path";
11
11
  import { platform, arch, release, hostname } from "node:os";
12
- import {
12
+ import { INTEGRATION_TYPES,
13
13
  HOOK_SCOPES,
14
14
  type HookScope,
15
15
  type IntegrationType,
@@ -256,8 +256,30 @@ function deduplicateScopes(scopes: readonly HookScope[], cwd?: string): HookScop
256
256
  });
257
257
  }
258
258
 
259
+ /**
260
+ * Is ANY agent CLI wired to call failproofai at this scope?
261
+ *
262
+ * Asked Claude Code and only Claude Code, which made it wrong for every machine
263
+ * guarded through one of the other eleven. A user with hooks in `~/.codex/` was
264
+ * told nothing was installed — quietly, while this only tinted a subtitle, and
265
+ * loudly once the listing started warning that every policy shown was inert.
266
+ * Reported from a real machine set up for codex.
267
+ *
268
+ * Any one integration answering yes is enough: the question is whether
269
+ * enforcement can reach this machine at all, not whether a particular agent is
270
+ * covered. Which agents specifically is a different question, and the per-CLI
271
+ * rows of the listing already answer it.
272
+ */
259
273
  export function hooksInstalledInSettings(scope: HookScope, cwd?: string): boolean {
260
- return claudeCode.hooksInstalledInSettings(scope, cwd);
274
+ return INTEGRATION_TYPES.some((id) => {
275
+ try {
276
+ return getIntegration(id).hooksInstalledInSettings(scope, cwd);
277
+ } catch {
278
+ // An integration whose settings file is unreadable is not evidence that
279
+ // nothing is installed — keep asking the rest.
280
+ return false;
281
+ }
282
+ });
261
283
  }
262
284
 
263
285
  export interface InstallHooksOptions {