failproofai 1.0.4-beta.1 → 1.0.4-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 (348) 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]__0v1hz15._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/{[externals]__0_gzk8q._.js → [externals]__1nb206a._.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  90. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/_0otft92._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +14 -21
  95. package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  99. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  100. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +1 -1
  102. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00-s7h8._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  109. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wvusu0._.js → [root-of-the-server]__0n_lxhg._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__16gamwq._.js → [root-of-the-server]__0yl9wb5._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__190won8._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1sc84bb._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zl_r0t._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  122. package/.next/standalone/.next/server/chunks/ssr/{_1byck5v._.js → _0guxnh-._.js} +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +61 -168
  128. package/.next/standalone/.next/server/chunks/ssr/{_08f0jd2._.js → _1if9ixi._.js} +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
  132. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  141. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  142. package/.next/standalone/.next/server/pages/404.html +1 -1
  143. package/.next/standalone/.next/server/pages/500.html +1 -1
  144. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  145. package/.next/standalone/.next/server/server-reference-manifest.json +54 -21
  146. package/.next/standalone/.next/static/chunks/04o5zfl7p5lhx.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0jpd8dv2vk930.css} +1 -1
  148. package/.next/standalone/.next/static/chunks/{1po3lirgk2riw.js → 0po8n4tyotjf2.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +2 -0
  150. package/.next/standalone/.next/static/chunks/0qx7gav1c2_vu.js +1 -0
  151. package/.next/standalone/.next/static/chunks/17tfki9x5rd_7.js +1 -0
  152. package/.next/standalone/.next/static/chunks/{1_bxotmf9qngs.js → 1i-i0pxg9q_8y.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{1_2jsatfuthrw.js → 2bxr2_2h4kaqq.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{01hvwwrhvbzyh.js → 2zafgs90r_leq.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{00gv0v91mycm-.js → 323cyb3a2s06y.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{0qn9khv0oo9ex.js → 364cqbpeg1y5i.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{2bvwsyu6x__ia.js → 38dqur43qthrl.js} +1 -1
  158. package/.next/standalone/.next/static/chunks/{3_--3ibia6ba1.js → 3l36d73kj18zw.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
  160. package/.next/standalone/.next/static/chunks/{2bnf_4pgyk1rq.js → 40lpt2o2b283e.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/42gwu2bsz4ske.js +6 -0
  162. package/.next/standalone/.next/static/chunks/{turbopack-18qf4jodtft6b.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
  163. package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
  164. package/.next/standalone/app/actions/get-leaks.ts +109 -0
  165. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +140 -34
  166. package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
  167. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
  168. package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
  169. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
  170. package/.next/standalone/app/audit/_components/leak-section.tsx +192 -0
  171. package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
  172. package/.next/standalone/app/audit/audit-styles.css +132 -1
  173. package/.next/standalone/app/project/[name]/page.tsx +4 -24
  174. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
  175. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
  176. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
  177. package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
  178. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
  179. package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
  180. package/.next/standalone/lib/auth/api-server-client.ts +28 -0
  181. package/.next/standalone/lib/claude-sessions.ts +101 -27
  182. package/.next/standalone/lib/cli-registry.ts +1 -33
  183. package/.next/standalone/lib/download-session.ts +0 -37
  184. package/.next/standalone/lib/projects.ts +3 -27
  185. package/.next/standalone/lib/sqlite-reader.ts +78 -1
  186. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  187. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  189. package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
  190. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  191. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
  192. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
  193. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  195. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  196. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +2 -2
  197. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  198. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  199. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  201. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  202. package/.next/standalone/node_modules/next/package.json +11 -11
  203. package/.next/standalone/package.json +15 -14
  204. package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
  205. package/.next/standalone/sdk/python/README.md +8 -0
  206. package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
  207. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  208. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
  209. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
  210. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
  211. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
  212. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
  213. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
  214. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
  215. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
  216. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
  217. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
  218. package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
  219. package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
  220. package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
  221. package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
  222. package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
  223. package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
  224. package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
  225. package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
  226. package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
  227. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
  228. package/.next/standalone/sdk/python/uv.lock +18 -18
  229. package/.next/standalone/server.js +1 -1
  230. package/README.md +19 -95
  231. package/bin/failproofai.mjs +8 -25
  232. package/dist/cli.mjs +20091 -25864
  233. package/dist/worker.mjs +4881 -6361
  234. package/lib/auth/api-server-client.ts +28 -0
  235. package/lib/claude-sessions.ts +101 -27
  236. package/lib/cli-registry.ts +1 -33
  237. package/lib/download-session.ts +0 -37
  238. package/lib/projects.ts +3 -27
  239. package/lib/sqlite-reader.ts +78 -1
  240. package/package.json +15 -14
  241. package/pi-extension/index.ts +61 -8
  242. package/scripts/changelog-open.py +115 -0
  243. package/src/audit/cache.ts +6 -0
  244. package/src/audit/cli-adapters/claude.ts +13 -0
  245. package/src/audit/cli-adapters/index.ts +0 -24
  246. package/src/audit/cli-login.ts +4 -1
  247. package/src/audit/cli.ts +164 -5
  248. package/src/audit/desktop-notify.ts +420 -0
  249. package/src/audit/harm-report.ts +101 -0
  250. package/src/audit/index.ts +284 -15
  251. package/src/audit/leak-fingerprint.ts +200 -0
  252. package/src/audit/leak-notice.ts +259 -0
  253. package/src/audit/leak-record.ts +232 -0
  254. package/src/audit/leak-scan.ts +268 -0
  255. package/src/audit/leak-store.ts +265 -0
  256. package/src/audit/macos-notifier.ts +310 -0
  257. package/src/audit/redact-example.ts +322 -15
  258. package/src/audit/report-harm.ts +12 -1
  259. package/src/audit/report.ts +6 -3
  260. package/src/audit/schedule-cli.ts +37 -0
  261. package/src/audit/scoring.ts +49 -0
  262. package/src/audit/types.ts +47 -0
  263. package/src/hooks/builtin-policies.ts +71 -8
  264. package/src/hooks/configure-wizard.ts +20 -0
  265. package/src/hooks/enforcement-capability.ts +0 -97
  266. package/src/hooks/fp-config.ts +71 -11
  267. package/src/hooks/fp-home.ts +27 -0
  268. package/src/hooks/handler.ts +84 -62
  269. package/src/hooks/harness-cli.ts +1 -3
  270. package/src/hooks/integrations.ts +63 -769
  271. package/src/hooks/normalize-cli-payload.ts +0 -126
  272. package/src/hooks/notice.ts +191 -0
  273. package/src/hooks/policy-evaluator.ts +6 -297
  274. package/src/hooks/resolve-transcript-path.ts +0 -10
  275. package/src/hooks/tool-name-canonicalize.ts +0 -64
  276. package/src/hooks/types.ts +2 -533
  277. package/src/hooks/uninstall-cli.ts +15 -0
  278. package/.next/standalone/.grok/hooks/failproofai.json +0 -172
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07hor7h._.js +0 -5
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0elsuli._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gnj_qa._.js +0 -3
  282. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljuzm_._.js +0 -3
  283. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w23bpg._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0x5c7sh._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yik9_w._.js +0 -3
  286. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1jg6s9m._.js +0 -3
  287. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1s6qci9._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xfm05z._.js +0 -3
  289. package/.next/standalone/.next/server/chunks/_0tdy_6l._.js +0 -3
  290. package/.next/standalone/.next/server/chunks/lib_grok-projects_ts_1i2epl_._.js +0 -3
  291. package/.next/standalone/.next/server/chunks/lib_qwen-projects_ts_0wu_a8e._.js +0 -3
  292. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_4gcz8._.js +0 -3
  293. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d--wmy._.js +0 -3
  294. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qqrlf_._.js +0 -3
  295. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s11fx1._.js +0 -3
  296. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bbk-si._.js +0 -3
  297. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1c3ybxr._.js +0 -3
  298. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1izi7zn._.js +0 -3
  299. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__21bj7uw._.js +0 -3
  300. package/.next/standalone/.next/server/chunks/ssr/_00z5uuq._.js +0 -3
  301. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
  302. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
  303. package/.next/standalone/.next/server/chunks/ssr/lib_cline-projects_ts_13_hbdd._.js +0 -3
  304. package/.next/standalone/.next/server/chunks/ssr/lib_grok-projects_ts_0rc-d0o._.js +0 -3
  305. package/.next/standalone/.next/server/chunks/ssr/lib_ori-projects_ts_04tofqo._.js +0 -3
  306. package/.next/standalone/.next/server/chunks/ssr/lib_qwen-projects_ts_0w3i9o7._.js +0 -3
  307. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
  308. package/.next/standalone/.next/static/chunks/0hzmxwb2pvk5p.js +0 -6
  309. package/.next/standalone/.next/static/chunks/0j884dwnwtyrt.js +0 -1
  310. package/.next/standalone/.next/static/chunks/0mr9dg3bnloa1.js +0 -1
  311. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
  312. package/.next/standalone/.next/static/chunks/3gf1jg-5m-u1t.css +0 -2
  313. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
  314. package/.next/standalone/.next/static/chunks/3ws7eljkq25wg.js +0 -69
  315. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
  316. package/.next/standalone/.qwen/settings.json +0 -251
  317. package/.next/standalone/assets/logos/cline.png +0 -0
  318. package/.next/standalone/assets/logos/grok-dark.svg +0 -1
  319. package/.next/standalone/assets/logos/grok-light.svg +0 -1
  320. package/.next/standalone/assets/logos/ori-dark.svg +0 -1
  321. package/.next/standalone/assets/logos/ori-light.svg +0 -1
  322. package/.next/standalone/assets/logos/qwen.svg +0 -1
  323. package/.next/standalone/lib/cline-projects.ts +0 -155
  324. package/.next/standalone/lib/cline-sessions.ts +0 -268
  325. package/.next/standalone/lib/grok-projects.ts +0 -123
  326. package/.next/standalone/lib/grok-sessions.ts +0 -355
  327. package/.next/standalone/lib/ori-projects.ts +0 -141
  328. package/.next/standalone/lib/ori-sessions.ts +0 -221
  329. package/.next/standalone/lib/qwen-projects.ts +0 -106
  330. package/.next/standalone/lib/qwen-sessions.ts +0 -313
  331. package/lib/cline-projects.ts +0 -155
  332. package/lib/cline-sessions.ts +0 -268
  333. package/lib/grok-projects.ts +0 -123
  334. package/lib/grok-sessions.ts +0 -355
  335. package/lib/ori-projects.ts +0 -141
  336. package/lib/ori-sessions.ts +0 -221
  337. package/lib/qwen-projects.ts +0 -106
  338. package/lib/qwen-sessions.ts +0 -313
  339. package/src/audit/cli-adapters/cline.ts +0 -50
  340. package/src/audit/cli-adapters/grok.ts +0 -70
  341. package/src/audit/cli-adapters/ori.ts +0 -55
  342. package/src/audit/cli-adapters/qwen.ts +0 -71
  343. package/src/hooks/batch-expand.ts +0 -380
  344. package/src/hooks/batch-fanout.ts +0 -142
  345. package/src/hooks/risk-patterns.ts +0 -26
  346. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_buildManifest.js +0 -0
  347. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_clientMiddlewareManifest.js +0 -0
  348. /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_ssgManifest.js +0 -0
@@ -10,56 +10,6 @@
10
10
  */
11
11
  import type { IntegrationType } from "./types";
12
12
 
13
- /**
14
- * True when `parsed` carries grok's camelCase envelope.
15
- *
16
- * This exists because grok EXECUTES OTHER CLIS' HOOK CONFIGS. Its discovery
17
- * scans `~/.claude/settings.json`, `~/.claude/settings.local.json`,
18
- * `<cwd>/.claude/settings.json` and the `.cursor` equivalents by default
19
- * (`[compat.claude] hooks = true`) — and `<cwd>/.claude/settings.json` is
20
- * exactly the file `policies --install --cli claude --scope project` writes. So
21
- * on any machine with both tools, grok runs OUR hooks and hands them `--cli
22
- * claude` on the command line while piping ITS OWN camelCase payload.
23
- *
24
- * Verified live against grok 1.0.3: the hook fired, `tool_name` and
25
- * `tool_input` were both undefined, and every builtin that reads either —
26
- * block-sudo, block-env-files, block-secrets-write, block-force-push — allowed.
27
- * Installed, running, costing latency, enforcing nothing; strictly worse than
28
- * no coverage, because the install reports success. Detecting the envelope lets
29
- * handler.ts re-route the event onto grok's path (tool maps AND response
30
- * shapes — grok ignores Claude's `hookSpecificOutput.permissionDecision`, also
31
- * verified by A/B).
32
- *
33
- * The test is on the PAYLOAD, never on an env var: `GROK_HOOK_EVENT` is set by
34
- * grok's runner but is still just an env var, and mistaking a real Claude event
35
- * for a grok one would break Claude's own enforcement. `workspaceRoot` +
36
- * `hookEventName` with no `hook_event_name` is a shape Claude never sends.
37
- */
38
- export function isGrokEnvelope(parsed: Record<string, unknown>): boolean {
39
- return (
40
- typeof parsed.hookEventName === "string" &&
41
- typeof parsed.workspaceRoot === "string" &&
42
- parsed.hook_event_name === undefined
43
- );
44
- }
45
-
46
- /**
47
- * The CLI whose contract actually governs this event.
48
- *
49
- * Identical to the declared `--cli` for every integration except the grok
50
- * cross-execution case above, where the flag says `claude` and the wire says
51
- * grok. Returning "grok" there routes the rest of the pipeline — tool-name and
52
- * tool-input canonicalization, and the response shape policy-evaluator emits —
53
- * onto the contract that will actually be honored.
54
- */
55
- export function resolveEffectiveCli(
56
- cli: IntegrationType,
57
- parsed: Record<string, unknown>,
58
- ): IntegrationType {
59
- if (cli === "claude" && isGrokEnvelope(parsed)) return "grok";
60
- return cli;
61
- }
62
-
63
13
  export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string, unknown>): void {
64
14
  // Antigravity (agy) pipes a camelCase protojson payload; normalize the fields
65
15
  // the handler downstream reads to canonical snake_case BEFORE any
@@ -97,33 +47,6 @@ export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string,
97
47
  }
98
48
  }
99
49
 
100
- // grok pipes a camelCase envelope (verified live against grok 1.0.3 with a
101
- // recorder hook on all 14 events). Normalize the fields the handler reads to
102
- // snake_case BEFORE any canonicalization runs. Note `workspaceRoot` → `cwd`:
103
- // grok sends BOTH, they were identical in every capture, and `cwd` is what
104
- // resolveCwd + block-read-outside-cwd need. `toolResult` is grok's PostToolUse
105
- // output key — it is NOT Claude's `tool_response`, so a sanitize policy
106
- // reading the documented Claude key would see nothing without this line.
107
- // `hookEventName` is deliberately NOT mapped: its value is snake_case
108
- // ("pre_tool_use") whereas the canonical set is PascalCase, and the `--hook`
109
- // arg already carries the PascalCase name.
110
- if (cli === "grok") {
111
- if (typeof parsed.toolName === "string") parsed.tool_name = parsed.toolName;
112
- if (parsed.toolInput !== undefined) parsed.tool_input = parsed.toolInput;
113
- if (parsed.toolResult !== undefined && parsed.tool_response === undefined) {
114
- parsed.tool_response = parsed.toolResult;
115
- }
116
- if (typeof parsed.sessionId === "string") parsed.session_id = parsed.sessionId;
117
- if (typeof parsed.transcriptPath === "string") parsed.transcript_path = parsed.transcriptPath;
118
- if (typeof parsed.workspaceRoot === "string" && parsed.cwd === undefined) {
119
- parsed.cwd = parsed.workspaceRoot;
120
- }
121
- if (typeof parsed.permissionMode === "string") parsed.permission_mode = parsed.permissionMode;
122
- if (typeof parsed.stopHookActive === "boolean") {
123
- parsed.stop_hook_active = parsed.stopHookActive;
124
- }
125
- }
126
-
127
50
  // Goose pipes `event` / `working_dir` instead of Claude's `hook_event_name` /
128
51
  // `cwd` (its `tool_name` / `tool_input` are already canonical field names).
129
52
  // Normalize both so resolveCwd keeps its cwd (block-read-outside-cwd) and the
@@ -135,53 +58,4 @@ export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string,
135
58
  parsed.hook_event_name = parsed.event;
136
59
  }
137
60
  }
138
-
139
- // Cline pipes its own payload verbatim — its hooks are event-NAMED scripts,
140
- // not a generated shim that could pre-shape stdin — so without this every
141
- // field the handler reads (tool_name, tool_input, session_id, cwd) is
142
- // undefined and resolveCwd / block-read-outside-cwd see nothing. Verified live
143
- // against cline v3.0.60 with a recorder hook on PreToolUse.
144
- //
145
- // `tool_call.input` is the source, NOT `preToolUse.parameters`: the latter
146
- // JSON-STRINGIFIES every array value (`commands` arrives as the literal string
147
- // `["echo hi"]`), so reading it would hand the batch expander a string and
148
- // silently drop the entire fan-out. `preToolUse` is used only as a fallback
149
- // for a payload carrying no `tool_call` at all.
150
- //
151
- // `hookName` is deliberately NOT mapped onto hook_event_name: its value is
152
- // cline's INTERNAL name ("tool_call"), not the filename event, and the --hook
153
- // argument already carries the canonical one. Same reasoning as grok's
154
- // hookEventName.
155
- if (cli === "cline") {
156
- const tc = parsed.tool_call;
157
- if (tc && typeof tc === "object" && !Array.isArray(tc)) {
158
- const t = tc as Record<string, unknown>;
159
- if (typeof t.name === "string") parsed.tool_name = t.name;
160
- if (t.input !== undefined) parsed.tool_input = t.input;
161
- }
162
- const pre = parsed.preToolUse;
163
- if (pre && typeof pre === "object" && !Array.isArray(pre)) {
164
- const pp = pre as Record<string, unknown>;
165
- if (parsed.tool_name === undefined && typeof pp.toolName === "string") {
166
- parsed.tool_name = pp.toolName;
167
- }
168
- if (parsed.tool_input === undefined && pp.parameters !== undefined) {
169
- parsed.tool_input = pp.parameters;
170
- }
171
- }
172
- // taskId is the conversation ("conv_…"). sessionContext.rootSessionId and
173
- // agent_id / parent_agent_id stay raw for custom policies correlating a
174
- // subagent back to its root.
175
- if (typeof parsed.taskId === "string") parsed.session_id = parsed.taskId;
176
- const roots = parsed.workspaceRoots;
177
- if (Array.isArray(roots) && typeof roots[0] === "string" && roots[0].length > 0) {
178
- parsed.cwd = roots[0];
179
- } else {
180
- const wi = parsed.workspaceInfo;
181
- if (wi && typeof wi === "object" && !Array.isArray(wi)) {
182
- const rp = (wi as Record<string, unknown>).rootPath;
183
- if (typeof rp === "string" && rp.length > 0) parsed.cwd = rp;
184
- }
185
- }
186
- }
187
61
  }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Putting a short message in front of a HUMAN, inside the agent they are using.
3
+ *
4
+ * A notice is not a verdict. `allow` / `deny` / `instruct` all answer "what
5
+ * happens to this tool call"; a notice answers nothing — it is one line of text
6
+ * for the person, delivered alongside whatever the verdict turned out to be.
7
+ * That is why it is a separate field rather than a fourth decision: shaping it
8
+ * into stdout beside a verdict ad hoc is how the copilot nested-vs-flat
9
+ * `additionalContext` bug happened.
10
+ *
11
+ * ## Why the channel differs per CLI, and why it is not `additionalContext`
12
+ *
13
+ * Measured across all 12 CLIs by live probe. The obvious channel —
14
+ * `hookSpecificOutput.additionalContext` — reaches the MODEL, not the user, and
15
+ * a model is a paraphraser: over eight real Claude turns it mentioned an
16
+ * informational notice 8/8, but an earlier notice that asked the agent to *act*
17
+ * was disclaimed as a suspected prompt injection 6/6 ("instructions arriving
18
+ * that way aren't something I'll act on unasked"). A security notice routed
19
+ * through something that may decline to repeat it is not a delivered notice.
20
+ *
21
+ * So each CLI gets the channel that was proven to paint text on the user's
22
+ * terminal, and the ones with no such channel get nothing rather than a
23
+ * pretend delivery:
24
+ *
25
+ * claude `{"systemMessage":…}` renders `Stop says: …`; model-invisible
26
+ * (a neutral probe answered `NONE`)
27
+ * codex `{"systemMessage":…}` renders `• Stop (completed) says: …`
28
+ * copilot exit 0 + stderr renders as a `!` session warning
29
+ * factory plain stdout renders under a `Hooks Stop` block
30
+ *
31
+ * opencode and pi have real channels too, but they live inside the plugin shims
32
+ * we ship rather than in a hook's stdout, so a private JSON field is consumed
33
+ * there and handed to `showToast` / `ui.notify`. OpenClaw, cursor, devin, goose,
34
+ * antigravity and hermes have no verified channel that reaches a human without
35
+ * an unsupported escape hatch — they get nothing here, and their users get the
36
+ * finding from `failproofai audit` and the scheduled desktop notification.
37
+ */
38
+ import type { IntegrationType } from "./types";
39
+
40
+ /** How a notice is delivered on one CLI. */
41
+ type NoticeChannel = "systemMessage" | "stderr" | "stdout" | "shim" | "none";
42
+
43
+ /**
44
+ * Per-CLI channel, from live probes rather than documentation.
45
+ *
46
+ * A CLI absent from this map delivers nothing — deliberately. Guessing a
47
+ * channel produces the worst outcome available: output that looks delivered
48
+ * from our side and reaches nobody, so we would report a leak as "notified"
49
+ * that the user never saw.
50
+ */
51
+ const NOTICE_CHANNEL: Partial<Record<IntegrationType, NoticeChannel>> = {
52
+ claude: "systemMessage",
53
+ codex: "systemMessage",
54
+ copilot: "stderr",
55
+ factory: "stdout",
56
+ opencode: "shim",
57
+ pi: "shim",
58
+ };
59
+
60
+ /**
61
+ * Claude truncates `systemMessage` at 4000 characters on the cloud-forwarding
62
+ * path, and a notice that gets cut mid-sentence reads as a broken tool. Ours is
63
+ * two lines by design; this is a backstop, not a budget.
64
+ */
65
+ const MAX_NOTICE_CHARS = 900;
66
+
67
+ export interface ShapedNotice {
68
+ /** Merged into the outcome's stdout, or empty. */
69
+ stdout: string;
70
+ /** Merged into the outcome's stderr, or empty. */
71
+ stderr: string;
72
+ }
73
+
74
+ /**
75
+ * Render a notice for one CLI, merging it into any verdict JSON already bound
76
+ * for stdout.
77
+ *
78
+ * `existingStdout` matters: a Stop-gate deny already emits a JSON object on
79
+ * several CLIs, and emitting a second object would produce two JSON documents
80
+ * on one stream — which every host parses as a syntax error and drops, taking
81
+ * the deny with it. So the notice is merged INTO that object when there is one.
82
+ */
83
+ export function shapeNotice(
84
+ cli: IntegrationType | undefined,
85
+ notice: string,
86
+ existingStdout = "",
87
+ ): ShapedNotice {
88
+ const empty: ShapedNotice = { stdout: existingStdout, stderr: "" };
89
+ if (!notice.trim() || !cli) return empty;
90
+
91
+ const channel = NOTICE_CHANNEL[cli] ?? "none";
92
+ const text = notice.length > MAX_NOTICE_CHARS
93
+ ? notice.slice(0, MAX_NOTICE_CHARS - 1) + "…"
94
+ : notice;
95
+
96
+ switch (channel) {
97
+ case "systemMessage": {
98
+ // Merge into the verdict object when one exists, so the stream stays a
99
+ // single JSON document.
100
+ if (existingStdout.trim().startsWith("{")) {
101
+ try {
102
+ const parsed = JSON.parse(existingStdout) as Record<string, unknown>;
103
+ parsed.systemMessage = text;
104
+ return { stdout: JSON.stringify(parsed), stderr: "" };
105
+ } catch {
106
+ // Not the JSON we thought it was — leave the verdict untouched
107
+ // rather than risk destroying it to add a courtesy message.
108
+ return empty;
109
+ }
110
+ }
111
+ // Anything already on stdout that we could not merge into is left
112
+ // exactly as it is, and the notice is dropped instead. The alternative —
113
+ // emitting our own object over the top — DESTROYS it: measured with a
114
+ // JSON array on stdout, which does not start with `{`, so it fell past
115
+ // the merge and was replaced wholesale. A courtesy message is never worth
116
+ // more than whatever the host was already being told.
117
+ if (existingStdout.trim()) return empty;
118
+ return { stdout: JSON.stringify({ systemMessage: text }), stderr: "" };
119
+ }
120
+ case "stderr":
121
+ // Exit code is NOT touched: on copilot a non-zero exit from a tool event
122
+ // is a block, and a notice must never block anything.
123
+ return { stdout: existingStdout, stderr: text + "\n" };
124
+ case "stdout":
125
+ // factory renders plain text under its Hooks block. Only safe when no
126
+ // verdict JSON is already on the stream.
127
+ if (existingStdout.trim()) return empty;
128
+ return { stdout: text + "\n", stderr: "" };
129
+ case "shim": {
130
+ // OpenCode and Pi are in-process plugin integrations. Their generated
131
+ // shims consume this private field and call the host's real user-facing
132
+ // notification API; putting the text in additionalContext would send it
133
+ // to the model instead.
134
+ if (existingStdout.trim().startsWith("{")) {
135
+ try {
136
+ const parsed = JSON.parse(existingStdout) as Record<string, unknown>;
137
+ parsed.failproofaiNotice = text;
138
+ return { stdout: JSON.stringify(parsed), stderr: "" };
139
+ } catch {
140
+ return empty;
141
+ }
142
+ }
143
+ if (existingStdout.trim()) return empty;
144
+ return { stdout: JSON.stringify({ failproofaiNotice: text }), stderr: "" };
145
+ }
146
+ default:
147
+ return empty;
148
+ }
149
+ }
150
+
151
+ /** True when this CLI has a channel that was proven to reach a human. */
152
+ export function canDeliverNotice(cli: IntegrationType | undefined): boolean {
153
+ return !!cli && (NOTICE_CHANNEL[cli] ?? "none") !== "none";
154
+ }
155
+
156
+ /**
157
+ * The notice itself. A FIXED template with no interpolation of scanned content.
158
+ *
159
+ * The count is a number we computed; everything else is a constant. That is not
160
+ * stylistic: the alternative is interpolating a finding's `example`, which is
161
+ * the verbatim text of a command from a repository — attacker-controlled
162
+ * through a README, a Makefile target or an npm script. Tested against the
163
+ * shipped redactor, `echo "IGNORE ALL PRIOR INSTRUCTIONS…"` passes through
164
+ * byte-identical, because that redactor masks secrets, not instructions. On the
165
+ * CLIs where the only channel is the model's context, that string would land
166
+ * directly in the prompt. So nothing scanned ever reaches this template.
167
+ */
168
+ export function leakNoticeText(newFindings: number): string {
169
+ const n = newFindings === 1
170
+ ? "a possible credential exposure"
171
+ : `${newFindings} possible credential exposures`;
172
+ return (
173
+ `failproofai found ${n} in this project's agent transcripts.\n` +
174
+ "Run `failproofai audit` to review the matches and rotate anything real."
175
+ );
176
+ }
177
+
178
+ /** Copy for the OS notification raised by an unattended scheduled scan.
179
+ *
180
+ * Deliberately omits the count. A burst of generated fixtures can hit the
181
+ * record cap, and a desktop banner saying "500 leaked credentials" states a
182
+ * conclusion the detector cannot support. The dashboard carries the evidence;
183
+ * the banner's job is only to ask for review. */
184
+ export function desktopLeakNotice(): { title: string; body: string } {
185
+ return {
186
+ title: "failproofai audit needs review",
187
+ body:
188
+ "Possible credential exposure was found in your agent transcripts. " +
189
+ "Run failproofai audit to verify the matches and rotate anything real.",
190
+ };
191
+ }