failproofai 1.0.4-beta.1 → 1.0.4-beta.2

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 (340) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +5 -5
  3. package/.next/standalone/.next/prerender-manifest.json +5 -5
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  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 +2 -2
  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 +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -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/run/route_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/status/route_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-request/route_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-verify/route_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  42. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +37 -4
  43. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  44. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/index.html +1 -1
  47. package/.next/standalone/.next/server/app/index.rsc +15 -15
  48. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  49. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  50. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  52. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  53. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  56. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  57. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  58. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
  63. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  73. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  74. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
  77. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  78. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/chunks/{[externals]__1zndo1i._.js → [externals]__1lh7m5d._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/{[externals]__0_gzk8q._.js → [externals]__1rqkg_y._.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  88. package/.next/standalone/.next/server/chunks/_08w6xzm._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +14 -21
  93. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  95. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  96. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00-s7h8._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wvusu0._.js → [root-of-the-server]__0n_lxhg._.js} +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__190won8._.js +3 -0
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1sc84bb._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zl_r0t._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +23 -0
  117. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  119. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +48 -168
  123. package/.next/standalone/.next/server/chunks/ssr/{_08f0jd2._.js → _1if9ixi._.js} +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  126. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
  127. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  133. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  136. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  137. package/.next/standalone/.next/server/pages/404.html +1 -1
  138. package/.next/standalone/.next/server/pages/500.html +1 -1
  139. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/server-reference-manifest.json +54 -21
  141. package/.next/standalone/.next/static/chunks/{1_bxotmf9qngs.js → 010bv1w6j171t.js} +1 -1
  142. package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +1 -0
  143. package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0o-hh5_turzlz.css} +1 -1
  144. package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +2 -0
  145. package/.next/standalone/.next/static/chunks/{00gv0v91mycm-.js → 0wz8yftk18ts2.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{0qn9khv0oo9ex.js → 1pb1oztsbwcss.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/2aquitk72k2op.js +1 -0
  149. package/.next/standalone/.next/static/chunks/2bi_1y0a_smt7.js +6 -0
  150. package/.next/standalone/.next/static/chunks/{1po3lirgk2riw.js → 2ej3b8gk5ittu.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/{01hvwwrhvbzyh.js → 2zafgs90r_leq.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/{1_2jsatfuthrw.js → 32spub4wqjem-.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{3_--3ibia6ba1.js → 3l36d73kj18zw.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{2bvwsyu6x__ia.js → 3m4upvybtrexd.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
  156. package/.next/standalone/.next/static/chunks/{2bnf_4pgyk1rq.js → 40lpt2o2b283e.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{turbopack-18qf4jodtft6b.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
  158. package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
  159. package/.next/standalone/app/actions/get-leaks.ts +100 -0
  160. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +112 -33
  161. package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
  162. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
  163. package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
  164. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
  165. package/.next/standalone/app/audit/_components/leak-section.tsx +179 -0
  166. package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
  167. package/.next/standalone/app/audit/audit-styles.css +114 -1
  168. package/.next/standalone/app/project/[name]/page.tsx +4 -24
  169. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
  170. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
  171. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
  172. package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
  173. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
  174. package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
  175. package/.next/standalone/lib/auth/api-server-client.ts +28 -0
  176. package/.next/standalone/lib/claude-sessions.ts +101 -27
  177. package/.next/standalone/lib/cli-registry.ts +1 -33
  178. package/.next/standalone/lib/download-session.ts +0 -37
  179. package/.next/standalone/lib/projects.ts +3 -27
  180. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  181. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  182. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  183. package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
  184. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  185. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
  186. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
  187. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  189. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  190. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +2 -2
  191. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  192. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  193. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  195. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  196. package/.next/standalone/node_modules/next/package.json +11 -11
  197. package/.next/standalone/package.json +15 -14
  198. package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
  199. package/.next/standalone/sdk/python/README.md +8 -0
  200. package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
  201. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  202. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
  203. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
  204. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
  205. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
  206. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
  207. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
  208. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
  209. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
  210. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
  211. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
  212. package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
  213. package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
  214. package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
  215. package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
  216. package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
  217. package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
  218. package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
  219. package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
  220. package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
  221. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
  222. package/.next/standalone/sdk/python/uv.lock +18 -18
  223. package/.next/standalone/server.js +1 -1
  224. package/README.md +19 -95
  225. package/bin/failproofai.mjs +7 -24
  226. package/dist/cli.mjs +19910 -25855
  227. package/dist/worker.mjs +4818 -6344
  228. package/lib/auth/api-server-client.ts +28 -0
  229. package/lib/claude-sessions.ts +101 -27
  230. package/lib/cli-registry.ts +1 -33
  231. package/lib/download-session.ts +0 -37
  232. package/lib/projects.ts +3 -27
  233. package/package.json +15 -14
  234. package/pi-extension/index.ts +42 -4
  235. package/scripts/changelog-open.py +115 -0
  236. package/src/audit/cli-adapters/index.ts +0 -24
  237. package/src/audit/cli.ts +134 -0
  238. package/src/audit/desktop-notify.ts +420 -0
  239. package/src/audit/harm-report.ts +101 -0
  240. package/src/audit/index.ts +141 -13
  241. package/src/audit/leak-fingerprint.ts +200 -0
  242. package/src/audit/leak-notice.ts +161 -0
  243. package/src/audit/leak-record.ts +232 -0
  244. package/src/audit/leak-scan.ts +292 -0
  245. package/src/audit/leak-store.ts +217 -0
  246. package/src/audit/macos-notifier.ts +310 -0
  247. package/src/audit/redact-example.ts +266 -14
  248. package/src/audit/report-harm.ts +12 -1
  249. package/src/audit/report.ts +6 -3
  250. package/src/audit/schedule-cli.ts +37 -0
  251. package/src/audit/scoring.ts +49 -0
  252. package/src/audit/types.ts +41 -0
  253. package/src/hooks/builtin-policies.ts +71 -8
  254. package/src/hooks/configure-wizard.ts +20 -0
  255. package/src/hooks/enforcement-capability.ts +0 -97
  256. package/src/hooks/fp-config.ts +71 -11
  257. package/src/hooks/fp-home.ts +27 -0
  258. package/src/hooks/handler.ts +64 -62
  259. package/src/hooks/harness-cli.ts +1 -3
  260. package/src/hooks/integrations.ts +49 -769
  261. package/src/hooks/normalize-cli-payload.ts +0 -126
  262. package/src/hooks/notice.ts +155 -0
  263. package/src/hooks/policy-evaluator.ts +6 -297
  264. package/src/hooks/resolve-transcript-path.ts +0 -10
  265. package/src/hooks/tool-name-canonicalize.ts +0 -64
  266. package/src/hooks/types.ts +2 -533
  267. package/src/hooks/uninstall-cli.ts +15 -0
  268. package/.next/standalone/.grok/hooks/failproofai.json +0 -172
  269. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07hor7h._.js +0 -5
  270. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0elsuli._.js +0 -3
  271. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gnj_qa._.js +0 -3
  272. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljuzm_._.js +0 -3
  273. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w23bpg._.js +0 -3
  274. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0x5c7sh._.js +0 -3
  275. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yik9_w._.js +0 -3
  276. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1jg6s9m._.js +0 -3
  277. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1s6qci9._.js +0 -3
  278. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xfm05z._.js +0 -3
  279. package/.next/standalone/.next/server/chunks/_0tdy_6l._.js +0 -3
  280. package/.next/standalone/.next/server/chunks/lib_grok-projects_ts_1i2epl_._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/lib_qwen-projects_ts_0wu_a8e._.js +0 -3
  282. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_4gcz8._.js +0 -3
  283. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d--wmy._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qqrlf_._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s11fx1._.js +0 -3
  286. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gamwq._.js +0 -3
  287. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bbk-si._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1c3ybxr._.js +0 -3
  289. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1izi7zn._.js +0 -3
  290. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__21bj7uw._.js +0 -3
  291. package/.next/standalone/.next/server/chunks/ssr/_00z5uuq._.js +0 -3
  292. package/.next/standalone/.next/server/chunks/ssr/_1byck5v._.js +0 -23
  293. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
  294. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
  295. package/.next/standalone/.next/server/chunks/ssr/lib_cline-projects_ts_13_hbdd._.js +0 -3
  296. package/.next/standalone/.next/server/chunks/ssr/lib_grok-projects_ts_0rc-d0o._.js +0 -3
  297. package/.next/standalone/.next/server/chunks/ssr/lib_ori-projects_ts_04tofqo._.js +0 -3
  298. package/.next/standalone/.next/server/chunks/ssr/lib_qwen-projects_ts_0w3i9o7._.js +0 -3
  299. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
  300. package/.next/standalone/.next/static/chunks/0hzmxwb2pvk5p.js +0 -6
  301. package/.next/standalone/.next/static/chunks/0j884dwnwtyrt.js +0 -1
  302. package/.next/standalone/.next/static/chunks/0mr9dg3bnloa1.js +0 -1
  303. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
  304. package/.next/standalone/.next/static/chunks/3gf1jg-5m-u1t.css +0 -2
  305. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
  306. package/.next/standalone/.next/static/chunks/3ws7eljkq25wg.js +0 -69
  307. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
  308. package/.next/standalone/.qwen/settings.json +0 -251
  309. package/.next/standalone/assets/logos/cline.png +0 -0
  310. package/.next/standalone/assets/logos/grok-dark.svg +0 -1
  311. package/.next/standalone/assets/logos/grok-light.svg +0 -1
  312. package/.next/standalone/assets/logos/ori-dark.svg +0 -1
  313. package/.next/standalone/assets/logos/ori-light.svg +0 -1
  314. package/.next/standalone/assets/logos/qwen.svg +0 -1
  315. package/.next/standalone/lib/cline-projects.ts +0 -155
  316. package/.next/standalone/lib/cline-sessions.ts +0 -268
  317. package/.next/standalone/lib/grok-projects.ts +0 -123
  318. package/.next/standalone/lib/grok-sessions.ts +0 -355
  319. package/.next/standalone/lib/ori-projects.ts +0 -141
  320. package/.next/standalone/lib/ori-sessions.ts +0 -221
  321. package/.next/standalone/lib/qwen-projects.ts +0 -106
  322. package/.next/standalone/lib/qwen-sessions.ts +0 -313
  323. package/lib/cline-projects.ts +0 -155
  324. package/lib/cline-sessions.ts +0 -268
  325. package/lib/grok-projects.ts +0 -123
  326. package/lib/grok-sessions.ts +0 -355
  327. package/lib/ori-projects.ts +0 -141
  328. package/lib/ori-sessions.ts +0 -221
  329. package/lib/qwen-projects.ts +0 -106
  330. package/lib/qwen-sessions.ts +0 -313
  331. package/src/audit/cli-adapters/cline.ts +0 -50
  332. package/src/audit/cli-adapters/grok.ts +0 -70
  333. package/src/audit/cli-adapters/ori.ts +0 -55
  334. package/src/audit/cli-adapters/qwen.ts +0 -71
  335. package/src/hooks/batch-expand.ts +0 -380
  336. package/src/hooks/batch-fanout.ts +0 -142
  337. package/src/hooks/risk-patterns.ts +0 -26
  338. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → aKNv4Kmv98Xpwns0Xa-C-}/_buildManifest.js +0 -0
  339. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → aKNv4Kmv98Xpwns0Xa-C-}/_clientMiddlewareManifest.js +0 -0
  340. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → aKNv4Kmv98Xpwns0Xa-C-}/_ssgManifest.js +0 -0
@@ -253,6 +253,34 @@ export interface AuditReportBody {
253
253
  last_seen?: string;
254
254
  examples: string[];
255
255
  }[];
256
+ /**
257
+ * Credentials this machine found in its own transcripts.
258
+ *
259
+ * Optional on the wire, so a CLI newer than the api-server sends a field the
260
+ * server ignores rather than failing the whole digest — the harmful counts
261
+ * are the older half of the contract and must keep arriving either way.
262
+ *
263
+ * Every field here is a mask, a label, a length or a location. There is no
264
+ * shape of this object that carries a secret, because the record it is built
265
+ * from never stores one: `recordLeaks` fingerprints at detection time and
266
+ * keeps only the masked rendering.
267
+ */
268
+ leaks?: {
269
+ id: string;
270
+ display: string;
271
+ label: string;
272
+ length: number;
273
+ attributed: boolean;
274
+ name: string | null;
275
+ confidence: string;
276
+ first_seen: string;
277
+ last_seen: string;
278
+ occurrences: number;
279
+ cli: string;
280
+ project: string;
281
+ mechanism: string;
282
+ direction: "input" | "result";
283
+ }[];
256
284
  }
257
285
 
258
286
  export interface AuditReportResult {
@@ -4,8 +4,14 @@
4
4
  * Claude stores transcripts at:
5
5
  * <CLAUDE_PROJECTS_PATH>/<encoded-cwd>/<sessionId>.jsonl
6
6
  *
7
- * Subagent transcripts (when a session spawned subagents) live alongside:
7
+ * Subagent transcripts (when a session spawned subagents) live alongside, in two
8
+ * shapes — a direct child, and one nested per workflow run:
8
9
  * <CLAUDE_PROJECTS_PATH>/<encoded-cwd>/<sessionId>/subagents/<agentId>.jsonl
10
+ * <CLAUDE_PROJECTS_PATH>/<encoded-cwd>/<sessionId>/subagents/workflows/<runId>/<agentId>.jsonl
11
+ *
12
+ * Everything below `subagents/` is walked to a bounded depth rather than one
13
+ * level, because the second shape arrived after this module was written and the
14
+ * miss was most of the corpus. See `collectSubagentTranscripts`.
9
15
  *
10
16
  * The parser for these files lives in `lib/log-entries.ts` (`parseLogContent`,
11
17
  * `parseSessionLog`). This module exposes discovery only — the audit pipeline
@@ -70,6 +76,99 @@ export function listClaudeProjects(): ClaudeProjectFolder[] {
70
76
  /** Lists every JSONL transcript under one Claude project folder, including
71
77
  * subagent transcripts under `<sessionId>/subagents/`. Returns [] on missing
72
78
  * or unreadable paths. */
79
+ /**
80
+ * Depth of nesting allowed below `subagents/`.
81
+ *
82
+ * The two shapes that exist today need 0 and 2 (`<agent>.jsonl` and
83
+ * `workflows/<runId>/<agent>.jsonl`). The cap is here so an unexpected layout —
84
+ * or a symlink cycle a future Claude build introduces — costs a bounded walk
85
+ * rather than an audit that never returns, and it is deliberately loose enough
86
+ * that a third shape lands inside it without another release.
87
+ */
88
+ const MAX_SUBAGENT_DEPTH = 5;
89
+
90
+ /**
91
+ * Collect every `.jsonl` beneath a session's `subagents/` directory.
92
+ *
93
+ * This used to read only the DIRECT children of `subagents/`, which was correct
94
+ * for the layout Claude shipped when it was written and silently wrong the day
95
+ * workflow runs started nesting their agents one level further down. On a
96
+ * machine that uses them the miss is most of the corpus: 1,839 transcripts on
97
+ * disk, 1,741 under `subagents/`, and 1,679 of those inside
98
+ * `subagents/workflows/<runId>/` — so the audit was walking 160 files, 8.7% of
99
+ * the evidence, and reporting the result as though it had read everything.
100
+ *
101
+ * ## Session ids come from the relative path, not the basename
102
+ *
103
+ * Every workflow run writes a `journal.jsonl` beside its agents, so basenames
104
+ * are NOT unique below this directory — 1,741 files share 1,613 distinct names
105
+ * on the machine this was found on. A basename id would collide those 128 files
106
+ * onto ~one row each, and `sessionId` is what the cache, the per-session
107
+ * detector state and the example attribution are keyed by, so the collision
108
+ * would silently merge unrelated sessions rather than fail.
109
+ *
110
+ * Nor is the path below `subagents/` unique on its own. A workflow run id is
111
+ * reused when its session is resumed, so `wf_3d609e92-a38/journal.jsonl` exists
112
+ * under two different parent sessions in the same project — found by asserting
113
+ * uniqueness over the real corpus, not by reasoning about it.
114
+ *
115
+ * The id is therefore the PARENT SESSION id followed by the path relative to
116
+ * `subagents/`, minus the extension, joined with `__`:
117
+ * `<parentUuid>__workflows__wf_123__agent-abc`. That is a filesystem path
118
+ * within the project, so it is unique by construction rather than by argument,
119
+ * and it names the session the subagent belongs to. `__` is safe as the joiner
120
+ * because no id Claude generates contains one — verified against 1,741 real
121
+ * files, zero hits. Top-level session ids are untouched.
122
+ *
123
+ * Symlinks are not followed. Nothing in the layout uses them, and following one
124
+ * is how a directory walk turns into an infinite loop.
125
+ */
126
+ function collectSubagentTranscripts(
127
+ dir: string,
128
+ relPrefix: string,
129
+ project: ClaudeProjectFolder,
130
+ out: ClaudeTranscriptFile[],
131
+ depth: number,
132
+ ): void {
133
+ if (depth > MAX_SUBAGENT_DEPTH) return;
134
+ let entries: import("node:fs").Dirent[];
135
+ try {
136
+ entries = readdirSync(dir, { withFileTypes: true });
137
+ } catch {
138
+ return;
139
+ }
140
+ for (const entry of entries) {
141
+ if (entry.isSymbolicLink()) continue;
142
+ if (entry.isDirectory()) {
143
+ collectSubagentTranscripts(
144
+ join(dir, entry.name),
145
+ relPrefix ? `${relPrefix}__${entry.name}` : entry.name,
146
+ project,
147
+ out,
148
+ depth + 1,
149
+ );
150
+ continue;
151
+ }
152
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
153
+ const stem = entry.name.slice(0, -".jsonl".length);
154
+ const transcriptPath = join(dir, entry.name);
155
+ try {
156
+ const s = statSync(transcriptPath);
157
+ out.push({
158
+ projectName: project.name,
159
+ cwd: project.cwd,
160
+ sessionId: relPrefix ? `${relPrefix}__${stem}` : stem,
161
+ transcriptPath,
162
+ mtimeMs: s.mtimeMs,
163
+ sizeBytes: s.size,
164
+ isSubagent: true,
165
+ });
166
+ } catch {
167
+ // unreadable — skip
168
+ }
169
+ }
170
+ }
171
+
73
172
  export function listClaudeTranscripts(project: ClaudeProjectFolder): ClaudeTranscriptFile[] {
74
173
  const out: ClaudeTranscriptFile[] = [];
75
174
  let entries: import("node:fs").Dirent[];
@@ -99,34 +198,9 @@ export function listClaudeTranscripts(project: ClaudeProjectFolder): ClaudeTrans
99
198
  // unreadable — skip
100
199
  }
101
200
  } else if (entry.isDirectory() && UUID_RE.test(entry.name)) {
102
- // Subagent transcripts at <sessionId>/subagents/<agentId>.jsonl
103
201
  const subDir = join(project.path, entry.name, "subagents");
104
202
  if (!existsSync(subDir)) continue;
105
- let subEntries: import("node:fs").Dirent[];
106
- try {
107
- subEntries = readdirSync(subDir, { withFileTypes: true });
108
- } catch {
109
- continue;
110
- }
111
- for (const sub of subEntries) {
112
- if (!sub.isFile() || !sub.name.endsWith(".jsonl")) continue;
113
- const agentId = sub.name.slice(0, -".jsonl".length);
114
- const transcriptPath = join(subDir, sub.name);
115
- try {
116
- const s = statSync(transcriptPath);
117
- out.push({
118
- projectName: project.name,
119
- cwd: project.cwd,
120
- sessionId: agentId,
121
- transcriptPath,
122
- mtimeMs: s.mtimeMs,
123
- sizeBytes: s.size,
124
- isSubagent: true,
125
- });
126
- } catch {
127
- // unreadable — skip
128
- }
129
- }
203
+ collectSubagentTranscripts(subDir, entry.name, project, out, 0);
130
204
  }
131
205
  }
132
206
 
@@ -27,7 +27,7 @@
27
27
  import type { IntegrationType } from "@/src/hooks/types";
28
28
 
29
29
  /** Canonical CLI ids the registry knows about. Mirrors `INTEGRATION_TYPES`. */
30
- export const KNOWN_CLI_IDS = ["claude", "codex", "copilot", "cursor", "opencode", "pi", "hermes", "openclaw", "factory", "devin", "antigravity", "goose", "grok", "qwen", "ori", "cline"] as const satisfies readonly IntegrationType[];
30
+ export const KNOWN_CLI_IDS = ["claude", "codex", "copilot", "cursor", "opencode", "pi", "hermes", "openclaw", "factory", "devin", "antigravity", "goose"] as const satisfies readonly IntegrationType[];
31
31
  export type CliId = (typeof KNOWN_CLI_IDS)[number];
32
32
 
33
33
  /** Per-CLI metadata consumed by the dashboard. */
@@ -99,38 +99,6 @@ const CLI_ENTRIES: Record<CliId, CliEntry> = {
99
99
  label: "Goose",
100
100
  badgeClasses: "bg-lime-500/10 text-lime-400 border-lime-500/20",
101
101
  },
102
- grok: {
103
- id: "grok",
104
- label: "grok CLI",
105
- // Neutral zinc, matching the design system's `--color-default`. Deliberately
106
- // NOT a status hue: green/amber/red carry health meaning in this dashboard
107
- // and must never be spent on identity. Neutral also happens to suit xAI's
108
- // monochrome brand, and it is maximally far from qwen's magenta — the two
109
- // were previously indistinguishable.
110
- badgeClasses: "bg-zinc-500/10 text-zinc-200 border-zinc-500/20",
111
- },
112
- qwen: {
113
- id: "qwen",
114
- label: "Qwen Code",
115
- // Magenta — the opposite pole from grok's neutral, and outside the
116
- // status set (success/amber/orange/error) so it reads as identity.
117
- badgeClasses: "bg-fuchsia-500/10 text-fuchsia-400 border-fuchsia-500/20",
118
- },
119
- ori: {
120
- id: "ori",
121
- label: "Ori",
122
- // Sky — the badge set must be UNIQUE per CLI (a test asserts it), and
123
- // violet/teal were already taken. Still outside the status set
124
- // (success/amber/orange/error) so it reads as identity, not state.
125
- badgeClasses: "bg-sky-500/10 text-sky-400 border-sky-500/20",
126
- },
127
- cline: {
128
- id: "cline",
129
- label: "Cline",
130
- // Stone — the remaining neutral that is not zinc (grok's) and not a status
131
- // colour.
132
- badgeClasses: "bg-stone-500/10 text-stone-300 border-stone-500/20",
133
- },
134
102
  };
135
103
 
136
104
  export function getCliEntry(id: string): CliEntry | undefined {
@@ -125,21 +125,6 @@ export async function resolveDownloadSource(
125
125
  return path ? { kind: "file", path } : null;
126
126
  }
127
127
 
128
- if (cli === "grok") {
129
- // grok writes a real chat_history.jsonl per session directory — stream it
130
- // verbatim (no synthesis, like Factory).
131
- const { findGrokTranscript } = await import("./grok-sessions");
132
- const path = findGrokTranscript(sessionId);
133
- return path ? { kind: "file", path } : null;
134
- }
135
-
136
- if (cli === "qwen") {
137
- // qwen writes a real JSONL transcript per session — stream it verbatim.
138
- const { findQwenTranscript } = await import("./qwen-sessions");
139
- const path = findQwenTranscript(sessionId);
140
- return path ? { kind: "file", path } : null;
141
- }
142
-
143
128
  if (cli === "devin") {
144
129
  // Devin keeps sessions in SQLite (~/.local/share/devin/cli/sessions.db).
145
130
  // Synthesize a JSONL export of the session's raw chat_message rows.
@@ -167,28 +152,6 @@ export async function resolveDownloadSource(
167
152
  return { kind: "synthesized", body, contentType: "application/x-ndjson", extension: "jsonl" };
168
153
  }
169
154
 
170
- if (cli === "ori") {
171
- // Ori keeps the whole conversation in one SQLite column
172
- // (ori_agent_loop_history.prompt). Synthesize a JSONL export from the
173
- // parsed entries — there is no per-session file to stream.
174
- const { getOriSessionLog } = await import("./ori-sessions");
175
- const result = await getOriSessionLog(sessionId);
176
- if (!result) return null;
177
- const body = result.entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
178
- return { kind: "synthesized", body, contentType: "application/x-ndjson", extension: "jsonl" };
179
- }
180
-
181
- if (cli === "cline") {
182
- // Cline stores a real per-session messages file, but it is a single JSON
183
- // document rather than JSONL, so stream the parsed entries as JSONL for
184
- // consistency with every other synthesized export.
185
- const { getClineSessionLog } = await import("./cline-sessions");
186
- const result = await getClineSessionLog(sessionId);
187
- if (!result) return null;
188
- const body = result.entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
189
- return { kind: "synthesized", body, contentType: "application/x-ndjson", extension: "jsonl" };
190
- }
191
-
192
155
  // Exhaustive — but TypeScript can't always see CliId is exhausted across the
193
156
  // if-chain above, so guard with a runtime fallback.
194
157
  const _exhaustive: never = cli;
package/lib/projects.ts CHANGED
@@ -16,7 +16,7 @@ import { formatDate } from "./format-date";
16
16
  export const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
17
17
  export const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
18
18
 
19
- export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "hermes" | "openclaw" | "factory" | "devin" | "antigravity" | "goose" | "grok" | "qwen" | "ori" | "cline";
19
+ export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "hermes" | "openclaw" | "factory" | "devin" | "antigravity" | "goose";
20
20
 
21
21
  export interface ProjectFolder {
22
22
  name: string;
@@ -171,10 +171,6 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
171
171
  { getDevinProjects },
172
172
  { getAntigravityProjects },
173
173
  { getGooseProjects },
174
- { getGrokProjects },
175
- { getQwenProjects },
176
- { getOriProjects },
177
- { getClineProjects },
178
174
  ] = await Promise.all([
179
175
  import("./codex-projects"),
180
176
  import("./copilot-projects"),
@@ -187,12 +183,8 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
187
183
  import("./devin-projects"),
188
184
  import("./antigravity-projects"),
189
185
  import("./goose-projects"),
190
- import("./grok-projects"),
191
- import("./qwen-projects"),
192
- import("./ori-projects"),
193
- import("./cline-projects"),
194
186
  ]);
195
- const [claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose, grok, qwen, ori, cline] = await Promise.all([
187
+ const [claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose] = await Promise.all([
196
188
  getClaudeProjectFolders(),
197
189
  getCodexProjects().catch((error) => {
198
190
  logError("Error reading Codex projects:", error);
@@ -238,24 +230,8 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
238
230
  logError("Error reading Goose projects:", error);
239
231
  return [] as ProjectFolder[];
240
232
  }),
241
- getGrokProjects().catch((error) => {
242
- logError("Error reading grok projects:", error);
243
- return [] as ProjectFolder[];
244
- }),
245
- getQwenProjects().catch((error) => {
246
- logError("Error reading Qwen projects:", error);
247
- return [] as ProjectFolder[];
248
- }),
249
- getOriProjects().catch((error) => {
250
- logError("Error reading Ori projects:", error);
251
- return [] as ProjectFolder[];
252
- }),
253
- getClineProjects().catch((error) => {
254
- logError("Error reading Cline projects:", error);
255
- return [] as ProjectFolder[];
256
- }),
257
233
  ]);
258
- return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose, grok, qwen, ori, cline);
234
+ return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose);
259
235
  }
260
236
 
261
237
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.4-beta.1",
4
- "description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 16 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
3
+ "version": "1.0.4-beta.2",
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",
7
7
  "failproofaid": "./bin/failproofaid-shim.mjs"
@@ -80,14 +80,14 @@
80
80
  "access": "public"
81
81
  },
82
82
  "devDependencies": {
83
- "@anthropic-ai/sdk": "^0.120.0",
83
+ "@anthropic-ai/sdk": "^0.123.0",
84
84
  "@mdx-js/mdx": "^3.1.1",
85
85
  "@tailwindcss/postcss": "^4.3.1",
86
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
89
  "@testing-library/user-event": "^14.6.5",
90
- "@types/node": "26.4.0",
90
+ "@types/node": "26.4.1",
91
91
  "@types/react": "19.2.18",
92
92
  "@types/react-dom": "^19.2.3",
93
93
  "@vitejs/plugin-react": "^6.1.0",
@@ -102,7 +102,7 @@
102
102
  "tailwind-merge": "^3.4.0",
103
103
  "tailwindcss": "^4.3.1",
104
104
  "typescript": "6.0.3",
105
- "vitest": "^4.1.11"
105
+ "vitest": "^5.0.0"
106
106
  },
107
107
  "dependencies": {
108
108
  "html-to-image": "^1.11.13",
@@ -117,18 +117,19 @@
117
117
  "vite": "8.0.16",
118
118
  "undici": "7.29.0",
119
119
  "brace-expansion": "5.0.9",
120
- "sharp": "0.35.0"
120
+ "sharp": "0.35.0",
121
+ "browserslist": "4.28.8"
121
122
  },
122
123
  "optionalDependencies": {
123
- "@failproofai/failproofaid-linux-x64": "1.0.4-beta.1",
124
- "@failproofai/failproofaid-linux-arm64": "1.0.4-beta.1",
125
- "@failproofai/failproofaid-darwin-x64": "1.0.4-beta.1",
126
- "@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.1"
124
+ "@failproofai/failproofaid-linux-x64": "1.0.4-beta.2",
125
+ "@failproofai/failproofaid-linux-arm64": "1.0.4-beta.2",
126
+ "@failproofai/failproofaid-darwin-x64": "1.0.4-beta.2",
127
+ "@failproofai/failproofaid-darwin-arm64": "1.0.4-beta.2"
127
128
  },
128
129
  "failproofaidBinaries": {
129
- "linux-x64": "bda0aa2ff38779c247161c8928a6b5ca7dd846ae3ce5c6a6efd8b9509f67fca7",
130
- "linux-arm64": "557699a24bec7a970839b952ee139b679e2feb0a505f5652a0e770e295ae148a",
131
- "darwin-x64": "f23ff6e57f45ac51c0f3c7f5b876041100c119d7a3b81105a0df00018a2e2cc1",
132
- "darwin-arm64": "7bc433a08d26fc148c21bc5471679440bf50300e47f1a2af505465476ca3352e"
130
+ "linux-x64": "3039575242ff240c68a79c4ebd4e0e35e865da99c6d8a46d1a17b2d7962cb47b",
131
+ "linux-arm64": "02fe993dca0faccb528c48472ca1d637274c562992ba7da1bc8d439103dbcc13",
132
+ "darwin-x64": "2be5a422007a773253611f24f2ad9e2a301f075392ed7bc2dc0f6354d3186696",
133
+ "darwin-arm64": "5882a4acd7317289346934290ac8277ca79edadb009e284c9fc0b23b74d321d7"
133
134
  }
134
135
  }
@@ -25,7 +25,7 @@
25
25
  * spawns extensions with an undefined cwd contract, so paths are resolved
26
26
  * relative to this file via `import.meta.url`, NOT process.cwd().
27
27
  */
28
- import { spawnSync } from "node:child_process";
28
+ import { spawn, spawnSync } from "node:child_process";
29
29
  import { resolve, dirname, join } from "node:path";
30
30
  import { fileURLToPath } from "node:url";
31
31
  import { existsSync, readdirSync, statSync } from "node:fs";
@@ -64,6 +64,44 @@ function debug(msg: string): void {
64
64
  }
65
65
  }
66
66
 
67
+ /**
68
+ * Forward an event whose VERDICT NOBODY READS, without blocking Pi.
69
+ *
70
+ * `callPolicy` is `spawnSync`, and Pi awaits its handlers serially on the
71
+ * startup path — so a `session_start` forward froze the whole TUI for as long
72
+ * as the subprocess took (measured floor 1.0-1.6s just to boot the binary,
73
+ * ceiling the full 60s timeout) and then threw the answer away. Three call
74
+ * sites are like that by design: `session_start`, `tool_result` and
75
+ * `session_shutdown` forward for the activity feed and for session-id
76
+ * continuity, and Pi cannot act on their return in any case
77
+ * (`ToolResultEventResult` has no `block`; the other two have no Result type
78
+ * at all).
79
+ *
80
+ * So they get a detached `spawn` instead. The event still reaches failproofai,
81
+ * the policies still run and still log, and Pi keeps rendering. Deliberately
82
+ * NOT used for `tool_call`, `user_bash`, `input` or `agent_end`: those four
83
+ * consume the decision, and blocking is what enforcement means.
84
+ */
85
+ function forwardPolicy(eventName: string, payload: unknown): void {
86
+ const { cmd, args } = resolveSpawn();
87
+ debug(`forwardPolicy (detached) event=${eventName} cmd=${cmd}`);
88
+ try {
89
+ const child = spawn(cmd, [...args, "--hook", eventName, "--cli", "pi"], {
90
+ stdio: ["pipe", "ignore", "ignore"],
91
+ detached: false,
92
+ });
93
+ // A child that outlives its parent's interest must not keep the event loop
94
+ // alive, or Pi cannot exit until the subprocess does.
95
+ child.unref();
96
+ // EPIPE if the child died before stdin was consumed — fail-open, silently.
97
+ child.on("error", (err) => debug(`forward error ${err.message}`));
98
+ child.stdin.on("error", () => {});
99
+ child.stdin.end(JSON.stringify(payload));
100
+ } catch (err) {
101
+ debug(`FORWARD EXCEPTION ${err instanceof Error ? err.message : String(err)}`);
102
+ }
103
+ }
104
+
67
105
  function callPolicy(eventName: string, payload: unknown): { block: boolean; reason: string } {
68
106
  const { cmd, args } = resolveSpawn();
69
107
  debug(`callPolicy event=${eventName} cmd=${cmd}`);
@@ -417,7 +455,7 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
417
455
  // need session_id continuity see the metadata.
418
456
  pi.on("session_start", (event: unknown): unknown => {
419
457
  const e = event as PiSessionStartEvent;
420
- callPolicy("session_start", {
458
+ forwardPolicy("session_start", {
421
459
  session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
422
460
  cwd: resolveCwd(e.cwd),
423
461
  reason: e.reason,
@@ -435,7 +473,7 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
435
473
  pi.on("tool_result", (event: unknown): unknown => {
436
474
  const e = event as PiToolResultEvent;
437
475
  const canonicalTool = canonicalizeToolName(e.toolName);
438
- callPolicy("tool_result", {
476
+ forwardPolicy("tool_result", {
439
477
  tool_name: canonicalTool,
440
478
  tool_input: canonicalizeToolInput(canonicalTool, e.input) ?? {},
441
479
  tool_response: { content: e.content, isError: e.isError },
@@ -500,7 +538,7 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
500
538
  pi.on("session_shutdown", (event: unknown): unknown => {
501
539
  const e = event as PiSessionShutdownEvent;
502
540
  const cwd = resolveCwd(e.cwd);
503
- callPolicy("session_shutdown", {
541
+ forwardPolicy("session_shutdown", {
504
542
  session_id: resolveSessionId(e.sessionId, cwd),
505
543
  cwd,
506
544
  reason: e.reason,
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ """Open a stub CHANGELOG section for the version a release just moved to.
3
+
4
+ changelog-open.py <changelog> <version> <published> [<date>]
5
+
6
+ The companion to `changelog-section.py`, which REFUSES a release whose version has
7
+ no section. Both PyPI publish workflows call this from `bump`, in the same commit
8
+ that moves `_version.py`, so the two files can never disagree about which version
9
+ `main` is open on.
10
+
11
+ WHY THIS EXISTS AS AUTOMATION RATHER THAN A RULE. `bump` used to move the version
12
+ alone, which left `main` on a version with no section — the exact state
13
+ `changelog-section.py` refuses at release time and that
14
+ `__tests__/ci/python-version-pipeline.test.ts` asserts against. Bump commits carry
15
+ a skip-ci marker, so nothing went red until the next unrelated PR ran CI and came
16
+ back failing for a reason that had nothing to do with it. That happened at the
17
+ first publish (repaired by hand in #755, which named the recurrence and left it),
18
+ and again at the second. It is not a rule a maintainer can be asked to remember:
19
+ the commit that creates the state is written by a workflow, not a person.
20
+
21
+ IDEMPOTENT ON PURPOSE. A re-run of `bump` against a `main` that already carries
22
+ the section must be a no-op, not a second heading — two `## <version>` headings
23
+ would put only the first one's body in the GitHub Release, silently. The version
24
+ is matched with the same trailing word boundary the extractor uses, so `0.0.1b1`
25
+ is not answered by `0.0.1b10`.
26
+
27
+ The stub is deliberately non-empty prose, not a bare heading: an empty section
28
+ fails `changelog-section.py` just as a missing one does, so a stub that could not
29
+ release would only move the failure.
30
+
31
+ Stdlib only, same reason as its two siblings: it runs in a job that installs
32
+ nothing precisely because it is handed a token.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import re
38
+ import sys
39
+ from datetime import datetime, timezone
40
+
41
+ # The heading grammar CLAUDE.md fixes for the root changelog and both per-package
42
+ # files follow — an EM DASH, and a date. Matched with `\b` after the version so a
43
+ # request to open `0.0.1b1` is not satisfied by an existing `0.0.1b10`.
44
+ _HEADING = "## {version} — {date}"
45
+
46
+ #: What lands under the new heading. Non-empty by construction (see the module
47
+ #: docstring), and it says who wrote it so the next reader does not go looking for
48
+ #: the human who did not.
49
+ _STUB = """Open for the next release. `{published}` published on {date} and the `bump` job
50
+ moved the version here automatically; nothing has landed against `{version}` yet.
51
+ Add entries as changes merge — this section becomes the GitHub Release body when
52
+ it ships.
53
+ """
54
+
55
+
56
+ class ChangelogError(Exception):
57
+ """A message a maintainer can act on, printed without a traceback."""
58
+
59
+
60
+ def has_section(text: str, version: str) -> bool:
61
+ return re.search(rf"^##[ \t]+{re.escape(version)}\b", text, re.MULTILINE) is not None
62
+
63
+
64
+ def open_section(text: str, version: str, published: str, date: str) -> str:
65
+ """`text` with a stub section for `version` above the topmost existing one."""
66
+ if has_section(text, version):
67
+ return text
68
+
69
+ block = _HEADING.format(version=version, date=date) + "\n\n" + _STUB.format(
70
+ published=published, version=version, date=date
71
+ )
72
+
73
+ # Above the newest existing section, never at the end: the file is
74
+ # reverse-chronological, and its preamble (title, and the grammar note the
75
+ # extractor depends on) sits above the first heading and must stay there.
76
+ first = re.search(r"^##[ \t]", text, re.MULTILINE)
77
+ if first:
78
+ return text[: first.start()] + block + "\n" + text[first.start() :]
79
+
80
+ # No sections yet — the first release off a fresh file. Keep the preamble and
81
+ # append, with exactly one blank line between.
82
+ return text.rstrip("\n") + "\n\n" + block
83
+
84
+
85
+ def main(argv: list[str]) -> int:
86
+ if len(argv) < 4:
87
+ print(__doc__ or "", file=sys.stderr)
88
+ return 2
89
+ path, version, published = argv[1], argv[2], argv[3]
90
+ date = argv[4] if len(argv) >= 5 else datetime.now(timezone.utc).strftime("%Y-%m-%d")
91
+ if not re.fullmatch(r"\d{4}-\d{2}-\d{2}", date):
92
+ raise ChangelogError(f"'{date}' is not a YYYY-MM-DD date")
93
+
94
+ try:
95
+ with open(path, encoding="utf-8") as handle:
96
+ text = handle.read()
97
+ except OSError as exc:
98
+ raise ChangelogError(f"cannot read {path}: {exc}") from exc
99
+
100
+ if has_section(text, version):
101
+ print(f"{path} already has a '## {version}' section — nothing to open.")
102
+ return 0
103
+
104
+ with open(path, "w", encoding="utf-8") as handle:
105
+ handle.write(open_section(text, version, published, date))
106
+ print(f"opened '## {version} — {date}' in {path}")
107
+ return 0
108
+
109
+
110
+ if __name__ == "__main__":
111
+ try:
112
+ raise SystemExit(main(sys.argv))
113
+ except ChangelogError as error:
114
+ print(f"error: {error}", file=sys.stderr)
115
+ raise SystemExit(1) from None
@@ -23,10 +23,6 @@ import { listFactoryTranscriptMetadata, streamFactoryEvents } from "./factory";
23
23
  import { listAntigravityTranscriptMetadata, streamAntigravityEvents } from "./antigravity";
24
24
  import { listDevinTranscriptMetadata, streamDevinEvents } from "./devin";
25
25
  import { listGooseTranscriptMetadata, streamGooseEvents } from "./goose";
26
- import { listGrokTranscriptMetadata, streamGrokEvents } from "./grok";
27
- import { listQwenTranscriptMetadata, streamQwenEvents } from "./qwen";
28
- import { listOriTranscriptMetadata, streamOriEvents } from "./ori";
29
- import { listClineTranscriptMetadata, streamClineEvents } from "./cline";
30
26
 
31
27
  export type { ListOpts };
32
28
 
@@ -127,26 +123,6 @@ export const ADAPTERS: Record<IntegrationType, CliAdapter> = {
127
123
  listTranscripts: listGooseTranscriptMetadata,
128
124
  streamEvents: streamGooseEvents,
129
125
  },
130
- grok: {
131
- cli: "grok",
132
- listTranscripts: listGrokTranscriptMetadata,
133
- streamEvents: streamGrokEvents,
134
- },
135
- qwen: {
136
- cli: "qwen",
137
- listTranscripts: listQwenTranscriptMetadata,
138
- streamEvents: streamQwenEvents,
139
- },
140
- ori: {
141
- cli: "ori",
142
- listTranscripts: listOriTranscriptMetadata,
143
- streamEvents: streamOriEvents,
144
- },
145
- cline: {
146
- cli: "cline",
147
- listTranscripts: listClineTranscriptMetadata,
148
- streamEvents: streamClineEvents,
149
- },
150
126
  };
151
127
 
152
128
  export function getAdapter(cli: IntegrationType): CliAdapter {