failproofai 1.0.3 → 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 (251) 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.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  41. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +37 -4
  42. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  43. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/index.html +1 -1
  46. package/.next/standalone/.next/server/app/index.rsc +15 -15
  47. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  48. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  49. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  50. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  55. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  56. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  69. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  73. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/chunks/{[externals]__0-9ti7z._.js → [externals]__1lh7m5d._.js} +1 -1
  76. package/.next/standalone/.next/server/chunks/{[externals]__0u3wrvq._.js → [externals]__1rqkg_y._.js} +1 -1
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/{_1-akg6w._.js → _08w6xzm._.js} +2 -2
  80. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +4 -4
  83. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
  85. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
  86. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  98. package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +23 -0
  99. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +47 -13
  103. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  105. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
  106. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  115. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  116. package/.next/standalone/.next/server/pages/404.html +1 -1
  117. package/.next/standalone/.next/server/pages/500.html +1 -1
  118. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.json +54 -21
  120. package/.next/standalone/.next/static/chunks/010bv1w6j171t.js +1 -0
  121. package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +1 -0
  122. package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0o-hh5_turzlz.css} +1 -1
  123. package/.next/standalone/.next/static/chunks/{40nr6ehj4p3cw.css → 0qmsunv04x4na.css} +1 -1
  124. package/.next/standalone/.next/static/chunks/{0ollg-nnu3t3e.js → 0wz8yftk18ts2.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +1 -0
  126. package/.next/standalone/.next/static/chunks/1eyl7fzkmnkf_.js +1 -0
  127. package/.next/standalone/.next/static/chunks/1pb1oztsbwcss.js +1 -0
  128. package/.next/standalone/.next/static/chunks/{1-_zkmnm-jwei.js → 2aquitk72k2op.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/2bi_1y0a_smt7.js +6 -0
  130. package/.next/standalone/.next/static/chunks/{42yfkhekx440s.js → 2ej3b8gk5ittu.js} +1 -1
  131. package/.next/standalone/.next/static/chunks/{354-n7p7labpn.js → 2zafgs90r_leq.js} +1 -1
  132. package/.next/standalone/.next/static/chunks/{2xm5mj1d21b4n.js → 32spub4wqjem-.js} +1 -1
  133. package/.next/standalone/.next/static/chunks/{1a6u7l3giejch.js → 3m4upvybtrexd.js} +1 -1
  134. package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
  135. package/.next/standalone/.next/static/chunks/{turbopack-2x-o099q64i8h.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
  136. package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
  137. package/.next/standalone/app/actions/get-leaks.ts +100 -0
  138. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +112 -33
  139. package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
  140. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
  141. package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
  142. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
  143. package/.next/standalone/app/audit/_components/leak-section.tsx +179 -0
  144. package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
  145. package/.next/standalone/app/audit/audit-styles.css +114 -1
  146. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
  147. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
  148. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
  149. package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
  150. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
  151. package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
  152. package/.next/standalone/lib/auth/api-server-client.ts +28 -0
  153. package/.next/standalone/lib/claude-sessions.ts +101 -27
  154. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  155. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  156. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
  157. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
  158. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +1 -1
  159. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +5 -5
  160. package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
  161. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  162. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
  163. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
  164. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  165. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  166. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  167. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  168. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +17 -4
  169. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  170. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/escape-path-delimiters.js +1 -1
  171. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  172. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  173. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  174. package/.next/standalone/node_modules/next/package.json +11 -11
  175. package/.next/standalone/package.json +14 -13
  176. package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
  177. package/.next/standalone/sdk/python/README.md +8 -0
  178. package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
  179. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
  180. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
  181. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
  182. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
  183. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
  184. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
  185. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
  186. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
  187. package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
  188. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
  189. package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
  190. package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
  191. package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
  192. package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
  193. package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
  194. package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
  195. package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
  196. package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
  197. package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
  198. package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
  199. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
  200. package/.next/standalone/sdk/python/uv.lock +29 -29
  201. package/.next/standalone/server.js +1 -1
  202. package/README.md +13 -58
  203. package/dist/cli.mjs +5368 -8756
  204. package/dist/worker.mjs +539 -312
  205. package/lib/auth/api-server-client.ts +28 -0
  206. package/lib/claude-sessions.ts +101 -27
  207. package/package.json +14 -13
  208. package/pi-extension/index.ts +42 -4
  209. package/scripts/changelog-open.py +115 -0
  210. package/src/audit/cli.ts +134 -0
  211. package/src/audit/desktop-notify.ts +420 -0
  212. package/src/audit/harm-report.ts +101 -0
  213. package/src/audit/index.ts +141 -13
  214. package/src/audit/leak-fingerprint.ts +200 -0
  215. package/src/audit/leak-notice.ts +161 -0
  216. package/src/audit/leak-record.ts +232 -0
  217. package/src/audit/leak-scan.ts +292 -0
  218. package/src/audit/leak-store.ts +217 -0
  219. package/src/audit/macos-notifier.ts +310 -0
  220. package/src/audit/redact-example.ts +266 -14
  221. package/src/audit/report-harm.ts +12 -1
  222. package/src/audit/report.ts +6 -3
  223. package/src/audit/schedule-cli.ts +37 -0
  224. package/src/audit/scoring.ts +49 -0
  225. package/src/audit/types.ts +41 -0
  226. package/src/hooks/builtin-policies.ts +67 -2
  227. package/src/hooks/configure-wizard.ts +20 -0
  228. package/src/hooks/fp-config.ts +71 -11
  229. package/src/hooks/fp-home.ts +27 -0
  230. package/src/hooks/handler.ts +59 -1
  231. package/src/hooks/integrations.ts +47 -13
  232. package/src/hooks/notice.ts +155 -0
  233. package/src/hooks/uninstall-cli.ts +15 -0
  234. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1b5jzxr._.js +0 -3
  235. package/.next/standalone/.next/server/chunks/ssr/_0j6gd_k._.js +0 -23
  236. package/.next/standalone/.next/server/chunks/ssr/_0v8eebd._.js +0 -3
  237. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
  238. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
  239. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
  240. package/.next/standalone/.next/static/chunks/03slu0c8_71y7.js +0 -1
  241. package/.next/standalone/.next/static/chunks/0nm11ysn0153h.js +0 -1
  242. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
  243. package/.next/standalone/.next/static/chunks/1r9676mkvihto.js +0 -1
  244. package/.next/standalone/.next/static/chunks/30pct5s734jua.js +0 -6
  245. package/.next/standalone/.next/static/chunks/3pibxnbhjza7n.js +0 -69
  246. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
  247. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
  248. package/.next/standalone/.next/static/chunks/41b5ptaocvi_l.js +0 -1
  249. /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_buildManifest.js +0 -0
  250. /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_clientMiddlewareManifest.js +0 -0
  251. /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_ssgManifest.js +0 -0
@@ -3,8 +3,8 @@
3
3
  /**
4
4
  * Top-level client wrapper for /audit.
5
5
  *
6
- * Composes the calm personality report: classify the agent into one of
7
- * 8 archetypes, derive a score, and render the 5-section flow:
6
+ * The old five-section personality report is SWITCHED OFF and being rebuilt
7
+ * bottom-up around leak detection. What it used to compose:
8
8
  *
9
9
  * 01 AuditPoster — single-screen shareable poster
10
10
  * 02 StrengthsSection — what it's great at
@@ -12,23 +12,36 @@
12
12
  * 04 HowToImproveSection — install / configure
13
13
  * 05 ComeBackBetterSection — spread the audit (invite)
14
14
  *
15
+ * Every one of those components, and the score / persona / strengths / findings
16
+ * modules behind them, is intact and still unit-tested — only the renders and
17
+ * the imports here are commented out, so restoring any of it is deleting a pair
18
+ * of comment markers. See the header of `src/audit/scoring.ts` for why.
19
+ *
20
+ * What this page renders now is `AuditReportPlaceholder`: the scan's own
21
+ * numbers. The scan, the 12-CLI transcript reader and the emailed digest all
22
+ * still run.
23
+ *
15
24
  * Empty / running states fall back to EmptyState and RunProgress.
16
25
  */
17
26
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
27
  import { getAuditResultAction } from "@/app/actions/get-audit-result";
19
28
  import type { AuditResult, RunAuditOptions } from "@/src/audit/types";
20
- import { classifyAgent } from "@/src/audit/archetypes";
21
- import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
22
- import { deriveStrengths } from "@/src/audit/strengths";
23
- import { deriveFindings } from "@/src/audit/findings";
29
+ // import { classifyAgent } from "@/src/audit/archetypes";
30
+ // Score switched off see the header of `src/audit/scoring.ts`.
31
+ // import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
32
+ // import { deriveStrengths } from "@/src/audit/strengths";
33
+ // import { deriveFindings } from "@/src/audit/findings";
24
34
  import { usePostHog } from "@/contexts/PostHogContext";
25
35
 
26
- import { AuditPoster } from "./audit-poster";
27
- import { StrengthsSection } from "./strengths-section";
28
- import { QuirksSection } from "./quirks-section";
29
- import { HowToImproveSection } from "./how-to-improve-section";
30
- import { ComeBackBetterSection } from "./come-back-better-section";
36
+ // The old report's five sections — switched off, not deleted. Each component
37
+ // file and its tests are untouched; only this render is commented out.
38
+ // import { AuditPoster } from "./audit-poster";
39
+ // import { StrengthsSection } from "./strengths-section";
40
+ // import { QuirksSection } from "./quirks-section";
41
+ // import { HowToImproveSection } from "./how-to-improve-section";
42
+ // import { ComeBackBetterSection } from "./come-back-better-section";
31
43
  import { ReportFooter } from "./report-footer";
44
+ import { LeakSection } from "./leak-section";
32
45
  import { EmptyState } from "./empty-state";
33
46
  import { RunProgress } from "./run-progress";
34
47
  import { AuditProgressStrip, type RerunStatus } from "./audit-progress-strip";
@@ -59,6 +72,9 @@ interface Props {
59
72
  totalCatalogSize: number;
60
73
  }
61
74
 
75
+ /* Switched off with the persona layer — this named the project that seeded the
76
+ archetype classifier and the leaderboard row. Restore alongside
77
+ `classifyAgent` in this file.
62
78
  function inferProjectName(result: AuditResult, override?: string): string {
63
79
  if (override && override.trim()) return override;
64
80
  // Pick the cwd that appears in the most examples — proxy for "your
@@ -81,6 +97,7 @@ function inferProjectName(result: AuditResult, override?: string): string {
81
97
  if (segs.length >= 2) return `${segs[segs.length - 2]} / ${segs[segs.length - 1]}`;
82
98
  return segs[segs.length - 1] ?? "your agent";
83
99
  }
100
+ */
84
101
 
85
102
  export function AuditDashboard({ initial, projectFromUrl, totalCatalogSize }: Props) {
86
103
  const [cache, setCache] = useState<Initial>(initial);
@@ -279,16 +296,22 @@ function MainReport({
279
296
  onDismissRerun,
280
297
  }: MainReportProps) {
281
298
  const { capture } = usePostHog();
282
- const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
299
+ // Only fed the persona classifier, which is switched off with it.
300
+ // const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
283
301
  // Seed classification with the project name so the behaviour fingerprint
284
302
  // (used for tie-breaks + copy variants) is stable per project.
285
- const classification = useMemo(() => classifyAgent(result, project), [result, project]);
286
- const score = useMemo(() => deriveScore(result), [result]);
287
- const projected = useMemo(() => projectedScore(result, score), [result, score]);
288
- const grade = gradeFor(score);
289
- const projectedGrade = gradeFor(projected);
290
- const strengths = useMemo(() => deriveStrengths(result), [result]);
291
- const findings = useMemo(() => deriveFindings(result), [result]);
303
+ // Persona classification is switched off with the rest of the old report. Its
304
+ // last live use was two telemetry properties describing a persona the product
305
+ // no longer shows anyone.
306
+ // const classification = useMemo(() => classifyAgent(result, project), [result, project]);
307
+ // Score switched off — see the header of `src/audit/scoring.ts`. The four
308
+ // functions are intact and still unit-tested; nothing calls them.
309
+ // const score = useMemo(() => deriveScore(result), [result]);
310
+ // const projected = useMemo(() => projectedScore(result, score), [result, score]);
311
+ // const grade = gradeFor(score);
312
+ // const projectedGrade = gradeFor(projected);
313
+ // const strengths = useMemo(() => deriveStrengths(result), [result]);
314
+ // const findings = useMemo(() => deriveFindings(result), [result]);
292
315
 
293
316
  // One pass over result.results: detectors triggered + missing prescribed
294
317
  // policies. Both feed PostHog instrumentation; `missing` also feeds the
@@ -310,10 +333,13 @@ function MainReport({
310
333
  if (dashboardViewedRef.current) return;
311
334
  dashboardViewedRef.current = true;
312
335
  capture("audit_dashboard_viewed", {
313
- score,
314
- grade,
315
- archetype: classification.archetype,
316
- secondary: classification.secondary ?? null,
336
+ // Score switched off — see `src/audit/scoring.ts`. `missing` is the
337
+ // prescription's denominator and is unaffected, so the copy→install
338
+ // funnel keeps both of its ends.
339
+ // score,
340
+ // grade,
341
+ // archetype: classification.archetype,
342
+ // secondary: classification.secondary ?? null,
317
343
  missing,
318
344
  transcripts_scanned: result.transcripts.scanned,
319
345
  results_count: result.results.length,
@@ -321,10 +347,6 @@ function MainReport({
321
347
  });
322
348
  }, [
323
349
  capture,
324
- score,
325
- grade,
326
- classification.archetype,
327
- classification.secondary,
328
350
  missing,
329
351
  result.transcripts.scanned,
330
352
  result.results.length,
@@ -332,19 +354,41 @@ function MainReport({
332
354
  ]);
333
355
 
334
356
  /** Poster ref — captured to PNG by the poster's share buttons. */
335
- const posterRef = useRef<HTMLDivElement>(null);
357
+ // const posterRef = useRef<HTMLDivElement>(null);
336
358
 
337
359
  return (
338
360
  <div className="app">
339
361
  <AuditProgressStrip status={rerunStatus} onDismiss={onDismissRerun} />
340
362
  <div className="app-shell">
341
363
  <div className="report">
364
+ <AuditReportPlaceholder
365
+ transcripts={result.transcripts.scanned}
366
+ events={result.eventsScanned ?? 0}
367
+ projects={result.projectsScanned.length}
368
+ />
369
+ {/* The rebuilt report. It reads the leak record rather than this
370
+ scan's result, deliberately: a credential found last month and
371
+ not touched by today's transcripts is still a credential to
372
+ rotate, and a report that showed only the current scan's findings
373
+ would go quiet on exactly those. */}
374
+ <LeakSection />
375
+ {/* The whole old report is switched off — the persona poster, the
376
+ strengths and quirks sections, the punch-list with its install-all
377
+ funnel, and the invite section. It is being rebuilt bottom-up
378
+ around leak detection; see the header of `src/audit/scoring.ts`
379
+ for the reasoning and `~/Desktop/failproofai-leak-detection-
380
+ verdict-2026-09-07.md` for the measurements behind it.
381
+
382
+ Every component below is intact and still unit-tested — only the
383
+ render is commented — so restoring any one of them is deleting a
384
+ pair of comment markers.
385
+
342
386
  <AuditPoster
343
387
  ref={posterRef}
344
388
  archetypeKey={classification.archetype}
345
389
  seed={classification.variantSeed}
346
- score={score}
347
- grade={grade}
390
+ // score={score}
391
+ // grade={grade}
348
392
  missing={missing}
349
393
  auditedAt={cachedAt ?? new Date().toISOString()}
350
394
  />
@@ -352,10 +396,11 @@ function MainReport({
352
396
  <QuirksSection findings={findings} />
353
397
  <HowToImproveSection
354
398
  result={result}
355
- projected={projected}
356
- projectedGrade={projectedGrade}
399
+ // projected={projected}
400
+ // projectedGrade={projectedGrade}
357
401
  />
358
- <ComeBackBetterSection score={score} />
402
+ <ComeBackBetterSection />
403
+ */}
359
404
  </div>
360
405
  <ReportFooter cachedAt={cachedAt} />
361
406
  </div>
@@ -363,6 +408,40 @@ function MainReport({
363
408
  );
364
409
  }
365
410
 
411
+ /**
412
+ * What `/audit` shows while the report is being rebuilt.
413
+ *
414
+ * Deliberately states the scan's own numbers rather than nothing at all: the
415
+ * scan still runs, still walks every transcript across all 12 CLIs, and still
416
+ * feeds the emailed digest. Saying so is the difference between "this page is
417
+ * under construction" and "this product is broken".
418
+ */
419
+ function AuditReportPlaceholder(
420
+ { transcripts, events, projects }: { transcripts: number; events: number; projects: number },
421
+ ) {
422
+ const n = (x: number) => x.toLocaleString();
423
+ return (
424
+ <section className="audit-sec" data-screen-label="01 Scan">
425
+ <div className="audit-sec-head">
426
+ <span className="audit-sec-eyebrow">
427
+ <span className="ix">01</span>{"// scan"}
428
+ </span>
429
+ </div>
430
+ <h2 className="audit-sec-title">scan complete</h2>
431
+ <div className="audit-sec-sub">
432
+ {n(events)} tool call{events === 1 ? "" : "s"} across {n(transcripts)}{" "}
433
+ transcript{transcripts === 1 ? "" : "s"}
434
+ {projects > 0 ? ` · ${n(projects)} project${projects === 1 ? "" : "s"}` : ""}
435
+ </div>
436
+ <div className="audit-sec-sub">
437
+ {"// the report is being rebuilt around leak detection. the scan, the"}
438
+ <br />
439
+ {"// 12-CLI transcript reader and the emailed digest are unaffected."}
440
+ </div>
441
+ </section>
442
+ );
443
+ }
444
+
366
445
  interface ShellEmptyProps {
367
446
  running: boolean;
368
447
  mode?: "no-cache" | "zero-sessions";
@@ -24,7 +24,12 @@
24
24
  import React, { forwardRef, useMemo, useState } from "react";
25
25
  import { pickArchetypeVariant, type ArchetypeKey } from "@/src/audit/archetypes";
26
26
  import { type Grade } from "@/src/audit/scoring";
27
- import { getArchetypeRarityPct } from "@/src/audit/social-proof";
27
+ // Archetype rarity switched off — the percentages are eight hardcoded integers
28
+ // ("Seeded with snapshot values; swap for live aggregates once that pipeline
29
+ // lands"), and this is the ONLY surface that rendered them: baked by
30
+ // html-to-image into the PNG people post publicly. A fabricated population
31
+ // statistic on a shared card is a claim we cannot support.
32
+ // import { getArchetypeRarityPct } from "@/src/audit/social-proof";
28
33
  import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
29
34
  import { toast } from "@/app/components/toast";
30
35
  import { usePostHog } from "@/contexts/PostHogContext";
@@ -45,8 +50,11 @@ interface Props {
45
50
  archetypeKey: ArchetypeKey;
46
51
  /** Stable seed for variant selection (project name is the natural fit). */
47
52
  seed: string;
48
- score: number;
49
- grade: Grade;
53
+ /** The score is switched off (see `src/audit/scoring.ts`). Kept as optional
54
+ * props rather than removed, so restoring is un-commenting rather than
55
+ * re-threading: the dashboard simply stops passing them. */
56
+ score?: number;
57
+ grade?: Grade;
50
58
  /** Count of unenabled prescribed policies — passed to the share-text
51
59
  * templates, not rendered on the poster itself. */
52
60
  missing: number;
@@ -63,7 +71,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
63
71
  () => pickArchetypeVariant(archetypeKey, seed),
64
72
  [archetypeKey, seed],
65
73
  );
66
- const rarityPct = getArchetypeRarityPct(archetypeKey);
74
+ // const rarityPct = getArchetypeRarityPct(archetypeKey);
67
75
  const indexLabel = String(archetype.index).padStart(2, "0");
68
76
  const auditedDate = useMemo(() => formatAuditedDate(auditedAt), [auditedAt]);
69
77
 
@@ -134,8 +142,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
134
142
  }
135
143
  };
136
144
 
145
+ // Score switched off — the filename is keyed on the archetype instead, which
146
+ // is what the card now actually shows. Original:
147
+ // `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`
137
148
  const filenameFor = (channel: "x" | "linkedin" | "download") =>
138
- `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`;
149
+ `failproofai-${channel}-${archetypeKey}.png`;
139
150
 
140
151
  const handleShare = async (channel: "x" | "linkedin" | "download") => {
141
152
  if (busy) return;
@@ -143,8 +154,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
143
154
  capture("audit_card_share_clicked", {
144
155
  channel,
145
156
  source: "poster",
146
- score,
147
- grade,
157
+ // Score switched off — see `src/audit/scoring.ts`. Note for whoever reads
158
+ // the funnel: these two properties stop appearing from this release, so a
159
+ // dashboard keyed on them breaks rather than reading zero.
160
+ // score,
161
+ // grade,
148
162
  missing_policies: missing,
149
163
  });
150
164
  try {
@@ -174,10 +188,10 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
174
188
  }
175
189
 
176
190
  const shareCtx: ShareCtx = {
177
- score,
178
191
  arch: archetype.name.toLowerCase(),
179
- grade,
180
192
  missing,
193
+ // score,
194
+ // grade,
181
195
  };
182
196
  const shareText = channel === "x"
183
197
  ? pickTemplate(X_TEMPLATES, seed, shareCtx)
@@ -248,6 +262,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
248
262
  </React.Fragment>
249
263
  ))}
250
264
  </div>
265
+ {/* Rarity switched off — see the import above.
251
266
  {typeof rarityPct === "number" && (
252
267
  <div className="persona-rarity">
253
268
  <span className="lbl">{"// only"}</span>{" "}
@@ -255,13 +270,17 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
255
270
  <span className="lbl">of agents are this archetype</span>
256
271
  </div>
257
272
  )}
273
+ */}
258
274
  </div>
259
275
 
260
- {/* Score block — heroic number, centered in the card */}
276
+ {/* Score block — heroic number, centered in the card. Switched off;
277
+ see `src/audit/scoring.ts` for why. The archetype is the card's
278
+ headline now.
261
279
  <div className="poster-score">
262
280
  <span className="score-n">{score}</span>
263
281
  <span className="score-of">/100</span>
264
282
  </div>
283
+ */}
265
284
  </div>
266
285
 
267
286
  <footer className="poster-foot">
@@ -21,7 +21,12 @@ import { AuthDialog, type AuthedUser } from "./auth-dialog";
21
21
  import { InviteDialog } from "./invite-dialog";
22
22
 
23
23
  interface Props {
24
- /** Current audit score (0–100), forwarded into the invite email body. */
24
+ /** Current audit score (0–100), forwarded into the invite email body.
25
+ *
26
+ * Always undefined while the score is switched off (see
27
+ * `src/audit/scoring.ts`) — the dashboard no longer passes it. Nothing
28
+ * breaks: `/api/audit/invite` already treats it as optional and simply
29
+ * leaves it out of the email body when it is not a finite number. */
25
30
  score?: number;
26
31
  }
27
32
 
@@ -74,7 +74,10 @@ export function EmptyState({ mode, running, onStarted, onCompleted }: Props) {
74
74
  <p className="empty-sub">
75
75
  we&apos;ll walk every transcript across your installed CLIs — Claude Code,
76
76
  Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful
77
- or risky action. you&apos;ll get a tier, a score, and a punch-list.
77
+ {/* Was "a tier, a score, and a punch-list" — the tier was never
78
+ rendered and the score is switched off, so this promised two
79
+ things the page did not show. */}
80
+ or risky action. you&apos;ll get your agent&apos;s archetype and a punch-list.
78
81
  </p>
79
82
 
80
83
  <div className="empty-actions">
@@ -12,13 +12,18 @@
12
12
  */
13
13
  import { useMemo, useState } from "react";
14
14
  import type { AuditResult } from "@/src/audit/types";
15
- import { type Grade, tierName } from "@/src/audit/scoring";
15
+ // Score switched off see `src/audit/scoring.ts`.
16
+ // import { type Grade, tierName } from "@/src/audit/scoring";
17
+ import type { Grade } from "@/src/audit/scoring";
16
18
  import { usePostHog } from "@/contexts/PostHogContext";
17
19
 
18
20
  interface Props {
19
21
  result: AuditResult;
20
- projected: number;
21
- projectedGrade: Grade;
22
+ /** The projection is switched off — see `src/audit/scoring.ts`. Optional
23
+ * rather than removed so restoring it is un-commenting the two lines in
24
+ * `audit-dashboard.tsx` and the sub-heading below. */
25
+ projected?: number;
26
+ projectedGrade?: Grade;
22
27
  }
23
28
 
24
29
  const DETECTOR_TO_PRIMARY_POLICY: Record<string, string> = {
@@ -99,7 +104,7 @@ function bulkInstall(fixes: FixRow[]): string {
99
104
  return `failproofai policies --install ${fixes.map((f) => f.name).join(" ")}`;
100
105
  }
101
106
 
102
- export function HowToImproveSection({ result, projected, projectedGrade }: Props) {
107
+ export function HowToImproveSection({ result }: Props) {
103
108
  const { capture } = usePostHog();
104
109
  const fixes = useMemo(() => buildFixes(result), [result]);
105
110
  const installAllCmd = useMemo(() => bulkInstall(fixes), [fixes]);
@@ -136,9 +141,18 @@ export function HowToImproveSection({ result, projected, projectedGrade }: Props
136
141
  </button>
137
142
  </div>
138
143
  <h2 className="audit-sec-title">install or configure</h2>
139
- <div className="audit-sec-sub">
144
+ {/* The projected-score clause is switched off. It was also the one place
145
+ the product promised a number it could not deliver: `deriveScore`
146
+ never read `enabledInConfig`, so taking this prescription and
147
+ re-running returned the SAME score with the projection gone. What
148
+ replaces it is the thing that was always true — one command, enforced
149
+ on every tool call. Original:
140
150
  enable all {fixes.length === 1 ? "one" : fixes.length} → projected{" "}
141
151
  <strong>{projected}</strong> · {tierName(projectedGrade).toLowerCase()}
152
+ */}
153
+ <div className="audit-sec-sub">
154
+ enable all {fixes.length === 1 ? "one" : fixes.length} → one command,
155
+ enforced on every tool call
142
156
  </div>
143
157
 
144
158
  <div className="fix-list">
@@ -0,0 +1,179 @@
1
+ "use client";
2
+
3
+ /**
4
+ * The leak report: every credential this machine found in its own transcripts.
5
+ *
6
+ * This is what replaced the persona poster and the five scored sections. The
7
+ * old report answered "what kind of agent user are you"; this one answers the
8
+ * only question a leaked key raises — what is it, where did it go, how did it
9
+ * get there, and what do I do now.
10
+ *
11
+ * Each row is one DISTINCT credential, not one sighting: a key pasted into
12
+ * forty commands is one thing to rotate, not forty. The count of exposures is a
13
+ * column, which is the honest way to show forty without implying forty problems.
14
+ */
15
+ import { useCallback, useEffect, useState } from "react";
16
+ import {
17
+ dismissLeakAction,
18
+ getLeaksAction,
19
+ setLeakNotifyAction,
20
+ type LeakRow,
21
+ } from "@/app/actions/get-leaks";
22
+
23
+ /** Rendered in the reader's timezone, which is why it is not done server-side. */
24
+ function when(iso: string): string {
25
+ const t = Date.parse(iso);
26
+ if (!Number.isFinite(t)) return "unknown";
27
+ const days = Math.floor((Date.now() - t) / 86_400_000);
28
+ if (days <= 0) return "today";
29
+ if (days === 1) return "yesterday";
30
+ if (days < 30) return `${days}d ago`;
31
+ return new Date(t).toLocaleDateString(undefined, { month: "short", day: "numeric" });
32
+ }
33
+
34
+ /**
35
+ * What to actually do about this one.
36
+ *
37
+ * Two genuinely different situations, and conflating them is what makes
38
+ * security advice useless. A recognised prefix means a console exists and the
39
+ * key can be revoked there in a minute. An unattributed value — the majority,
40
+ * per the pattern census — has no console at all: the only move is to find
41
+ * where it came from, and the identifier name is usually the only clue.
42
+ */
43
+ function advice(row: LeakRow): string {
44
+ if (row.attributed) return `rotate it in the ${row.label.replace(/ (API )?(key|token).*$/i, "")} console`;
45
+ if (row.name) return `find what reads ${row.name}, then rotate it there`;
46
+ return "trace it to its owner, then rotate it";
47
+ }
48
+
49
+ export function LeakSection() {
50
+ const [rows, setRows] = useState<LeakRow[] | null>(null);
51
+ const [notify, setNotify] = useState(true);
52
+ const [busy, setBusy] = useState<string | null>(null);
53
+
54
+ const load = useCallback(async () => {
55
+ const payload = await getLeaksAction();
56
+ setRows(payload.rows);
57
+ setNotify(payload.notify);
58
+ }, []);
59
+
60
+ useEffect(() => {
61
+ void load();
62
+ }, [load]);
63
+
64
+ const dismiss = useCallback(
65
+ async (id: string) => {
66
+ setBusy(id);
67
+ try {
68
+ // Optimism would be wrong here: a dismissal that did not persist means
69
+ // the row comes back on the next load, and the user has to guess which
70
+ // of the two states is real.
71
+ if (await dismissLeakAction(id)) setRows((prev) => prev?.filter((r) => r.id !== id) ?? null);
72
+ } finally {
73
+ setBusy(null);
74
+ }
75
+ },
76
+ [],
77
+ );
78
+
79
+ const toggleNotify = useCallback(async () => {
80
+ const next = !notify;
81
+ setNotify(next);
82
+ if (!(await setLeakNotifyAction(next))) setNotify(!next);
83
+ }, [notify]);
84
+
85
+ if (rows === null) return null;
86
+
87
+ return (
88
+ <section className="audit-sec" data-screen-label="02 Leaks">
89
+ <div className="audit-sec-head">
90
+ <span className="audit-sec-eyebrow">
91
+ <span className="ix">02</span>{"// credentials"}
92
+ </span>
93
+ <button
94
+ type="button"
95
+ className="cbb-toggle leak-notify"
96
+ data-on={notify}
97
+ onClick={toggleNotify}
98
+ aria-label={notify ? "turn desktop notifications off" : "turn desktop notifications on"}
99
+ title="desktop notification when a new credential is found"
100
+ >
101
+ <span className="cbb-toggle-knob" />
102
+ </button>
103
+ </div>
104
+
105
+ {rows.length === 0 ? (
106
+ <>
107
+ <h2 className="audit-sec-title">no credentials found</h2>
108
+ <div className="audit-sec-sub">
109
+ {"// nothing in this machine's transcripts matched a known key format"}
110
+ <br />
111
+ {"// or a secret-named assignment."}
112
+ </div>
113
+ </>
114
+ ) : (
115
+ <>
116
+ <h2 className="audit-sec-title">
117
+ {rows.length} credential{rows.length === 1 ? "" : "s"} in your transcripts
118
+ </h2>
119
+ <div className="audit-sec-sub">
120
+ {"// one row per distinct key — an exposure count is not a key count."}
121
+ </div>
122
+
123
+ <ul className="leak-list">
124
+ {rows.map((row) => (
125
+ <li key={row.id} className="leak-row" data-attributed={row.attributed}>
126
+ <div className="leak-row-top">
127
+ <code className="leak-key">{row.display}</code>
128
+ <span className="leak-label">{row.label}</span>
129
+ {row.name ? <span className="leak-name">{row.name}</span> : null}
130
+ <button
131
+ type="button"
132
+ className="leak-dismiss"
133
+ onClick={() => void dismiss(row.id)}
134
+ disabled={busy === row.id}
135
+ >
136
+ not a secret
137
+ </button>
138
+ </div>
139
+
140
+ {/* The 5W1H, as one sentence rather than a grid of labelled
141
+ cells — a person reading a leak wants the story, and the
142
+ grid version made every row look like a form to fill in. */}
143
+ <div className="leak-how">
144
+ <span className="leak-verb">{row.mechanism}</span>
145
+ {" in "}
146
+ <span className="leak-where">{row.project}</span>
147
+ {" via "}
148
+ <span className="leak-who">{row.cli}</span>
149
+ {" · "}
150
+ <span className="leak-when">{when(row.lastSeen)}</span>
151
+ </div>
152
+
153
+ <div className="leak-foot">
154
+ <span className="leak-meta">
155
+ {row.occurrences} exposure{row.occurrences === 1 ? "" : "s"}
156
+ {row.sessions > 1 ? ` · ${row.sessions} sessions` : ""}
157
+ {" · "}
158
+ {row.length} chars
159
+ </span>
160
+ {/* The direction is the difference between a leak a gate can
161
+ stop next time and one it cannot. An input travelled OUT
162
+ of the machine with the agent's request; a result came
163
+ back INTO the transcript from a file or a command, where
164
+ no PreToolUse hook can intercept it. */}
165
+ <span className="leak-dir">
166
+ {row.direction === "input"
167
+ ? "sent by the agent — blockable at PreToolUse"
168
+ : "returned to the agent — already in the transcript"}
169
+ </span>
170
+ <span className="leak-fix">{advice(row)}</span>
171
+ </div>
172
+ </li>
173
+ ))}
174
+ </ul>
175
+ </>
176
+ )}
177
+ </section>
178
+ );
179
+ }