failproofai 1.0.4-beta.8 → 1.0.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 (245) 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 +4 -4
  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 +2 -2
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  13. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  23. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  24. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
  27. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +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/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  34. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  36. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +4 -37
  37. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/index.html +1 -1
  41. package/.next/standalone/.next/server/app/index.rsc +14 -14
  42. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  43. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  46. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/page.js +2 -2
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  52. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  53. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
  58. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -5
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  67. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  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 -15
  73. package/.next/standalone/.next/server/app/settings/page.js +4 -4
  74. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/chunks/{[externals]__0hmcwgg._.js → [externals]__0-9ti7z._.js} +1 -1
  77. package/.next/standalone/.next/server/chunks/{[externals]__14b7cc6._.js → [externals]__0u3wrvq._.js} +1 -1
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  87. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/{_1_eakl4._.js → _1-akg6w._.js} +2 -2
  90. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
  91. package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__187jks6._.js → [root-of-the-server]__00-s7h8._.js} +1 -1
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1fa1jd9._.js → [root-of-the-server]__09flfsx._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  102. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0kokc8z._.js → [root-of-the-server]__0n_lxhg._.js} +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  106. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1kpd1za._.js → [root-of-the-server]__190won8._.js} +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  110. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00y9ww2._.js → [root-of-the-server]__1sc84bb._.js} +2 -21
  111. package/.next/standalone/.next/server/chunks/ssr/_02yoeej._.js +3 -0
  112. package/.next/standalone/.next/server/chunks/ssr/{_00bqkhj._.js → _04rbnaq._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/ssr/{_0s-77v5._.js → _0h_douw._.js} +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +14 -51
  118. package/.next/standalone/.next/server/chunks/ssr/{_14c6q09._.js → _1if9ixi._.js} +2 -21
  119. package/.next/standalone/.next/server/chunks/ssr/{_07t3jol._.js → _1u8-lu2._.js} +2 -2
  120. package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +69 -1
  124. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  133. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  134. package/.next/standalone/.next/server/pages/404.html +1 -1
  135. package/.next/standalone/.next/server/pages/500.html +1 -1
  136. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  137. package/.next/standalone/.next/server/server-reference-manifest.json +21 -65
  138. package/.next/standalone/.next/static/chunks/{1302duobnz07l.js → 0-tyrpwuc3lla.js} +1 -1
  139. package/.next/standalone/.next/static/chunks/0ca7octhod0g2.js +69 -0
  140. package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
  141. package/.next/standalone/.next/static/chunks/{2n85g-crgcx5j.js → 1j6o9min_6qa9.js} +1 -1
  142. package/.next/standalone/.next/static/chunks/27cl8h-c2w7cd.js +1 -0
  143. package/.next/standalone/.next/static/chunks/{0jpd8dv2vk930.css → 29-iqnp9v_axt.css} +1 -1
  144. package/.next/standalone/.next/static/chunks/{3uyhmm01g64k4.css → 2k9f4tyv04809.css} +0 -1
  145. package/.next/standalone/.next/static/chunks/{1chm9piluggc-.js → 2lwipmv5etc8m.js} +1 -1
  146. package/.next/standalone/.next/static/chunks/2pd16tn5qmx13.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{2ukucf6g-kbs5.js → 2u8vh73tpw28_.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/{1_x7pqemw5n_w.js → 31u94yukw9fz-.js} +1 -1
  149. package/.next/standalone/.next/static/chunks/{2mizt96igig1y.js → 3p9_8x5ezce-9.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
  151. package/.next/standalone/.next/static/chunks/{0vm4wdx3w-2_w.js → 3tjji451hrgc0.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
  153. package/.next/standalone/.next/static/chunks/{turbopack-3s3c-u_u_l0u9.js → turbopack-3p73obe0xmrxm.js} +1 -1
  154. package/.next/standalone/.opencode/plugins/failproofai.mjs +27 -51
  155. package/.next/standalone/app/actions/get-scheduled-audit.ts +0 -5
  156. package/.next/standalone/app/actions/update-scheduled-audit.ts +57 -18
  157. package/.next/standalone/app/api/auth/status/route.ts +2 -3
  158. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +34 -140
  159. package/.next/standalone/app/audit/_components/audit-poster.tsx +10 -29
  160. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -6
  161. package/.next/standalone/app/audit/_components/empty-state.tsx +1 -4
  162. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +5 -19
  163. package/.next/standalone/app/audit/_components/share-templates.ts +44 -110
  164. package/.next/standalone/app/audit/audit-styles.css +1 -132
  165. package/.next/standalone/app/settings/page.tsx +1 -1
  166. package/.next/standalone/app/settings/settings-client.tsx +45 -50
  167. package/.next/standalone/lib/auth/api-server-client.ts +5 -33
  168. package/.next/standalone/lib/claude-sessions.ts +27 -101
  169. package/.next/standalone/lib/codex-sessions.ts +4 -20
  170. package/.next/standalone/lib/copilot-sessions.ts +1 -32
  171. package/.next/standalone/lib/cursor-sessions.ts +5 -62
  172. package/.next/standalone/lib/download-session.ts +13 -58
  173. package/.next/standalone/lib/openclaw-projects.ts +18 -54
  174. package/.next/standalone/lib/openclaw-sessions.ts +17 -75
  175. package/.next/standalone/lib/sqlite-reader.ts +1 -78
  176. package/.next/standalone/package.json +9 -9
  177. package/.next/standalone/server.js +1 -1
  178. package/bin/failproofai.mjs +1 -1
  179. package/dist/cli.mjs +664 -2121
  180. package/dist/worker.mjs +105 -422
  181. package/lib/auth/api-server-client.ts +5 -33
  182. package/lib/claude-sessions.ts +27 -101
  183. package/lib/codex-sessions.ts +4 -20
  184. package/lib/copilot-sessions.ts +1 -32
  185. package/lib/cursor-sessions.ts +5 -62
  186. package/lib/download-session.ts +13 -58
  187. package/lib/openclaw-projects.ts +18 -54
  188. package/lib/openclaw-sessions.ts +17 -75
  189. package/lib/sqlite-reader.ts +1 -78
  190. package/package.json +9 -9
  191. package/pi-extension/index.ts +5 -50
  192. package/src/audit/cache.ts +0 -6
  193. package/src/audit/cli-adapters/claude.ts +0 -13
  194. package/src/audit/cli-login.ts +1 -4
  195. package/src/audit/cli.ts +7 -157
  196. package/src/audit/harm-report.ts +4 -111
  197. package/src/audit/index.ts +15 -286
  198. package/src/audit/machine-store.ts +9 -8
  199. package/src/audit/redact-example.ts +15 -324
  200. package/src/audit/report-harm.ts +37 -33
  201. package/src/audit/report.ts +3 -6
  202. package/src/audit/schedule-cli.ts +33 -54
  203. package/src/audit/scoring.ts +0 -49
  204. package/src/audit/types.ts +0 -52
  205. package/src/hooks/builtin-policies.ts +2 -67
  206. package/src/hooks/configure-wizard.ts +0 -55
  207. package/src/hooks/fp-config.ts +48 -83
  208. package/src/hooks/fp-home.ts +0 -27
  209. package/src/hooks/integrations.ts +14 -51
  210. package/src/hooks/uninstall-cli.ts +2 -17
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0faqz9x._.js +0 -22
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__112b4go._.js +0 -24
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l71g3y._.js +0 -22
  215. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-4ja6q._.js +0 -4
  217. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cr3xu._.js +0 -4
  218. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04px77e._.js +0 -4
  219. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egr5s1._.js +0 -6
  220. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0i0l1di._.js +0 -4
  221. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n2txhk._.js +0 -3
  222. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0r3c1_0._.js +0 -4
  223. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17xnxlx._.js +0 -4
  224. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ckpmkk._.js +0 -4
  225. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kaotix._.js +0 -22
  226. package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +0 -3
  227. package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +0 -3
  228. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +0 -3
  229. package/.next/standalone/.next/static/chunks/0igq884ziuv-8.js +0 -1
  230. package/.next/standalone/.next/static/chunks/349jkb79v5g6z.js +0 -1
  231. package/.next/standalone/.next/static/chunks/3a4gth64hwtwf.js +0 -1
  232. package/.next/standalone/app/actions/get-leaks.ts +0 -104
  233. package/.next/standalone/app/audit/_components/leak-section.tsx +0 -192
  234. package/.next/standalone/lib/openclaw-db.ts +0 -271
  235. package/lib/openclaw-db.ts +0 -271
  236. package/src/audit/desktop-notify.ts +0 -420
  237. package/src/audit/leak-fingerprint.ts +0 -198
  238. package/src/audit/leak-record.ts +0 -248
  239. package/src/audit/leak-scan.ts +0 -268
  240. package/src/audit/leak-store.ts +0 -220
  241. package/src/audit/macos-notifier.ts +0 -298
  242. package/src/hooks/notice.ts +0 -24
  243. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_buildManifest.js +0 -0
  244. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_clientMiddlewareManifest.js +0 -0
  245. /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_ssgManifest.js +0 -0
@@ -7,31 +7,20 @@
7
7
  * appends a clipboard paste hint. Pure — no React, no DOM — so it's
8
8
  * unit-testable and shared by the client poster component.
9
9
  *
10
- * ## The score is switched off (2026-09-07)
11
- *
12
- * Every template but one used to interpolate `${score}/100`. The score is
13
- * commented out across the audit see the header of `src/audit/scoring.ts` for
14
- * why — so the copy is archetype-only. Eighteen templates lost a subordinate
15
- * clause; two whose entire premise WAS the number ("my agent scored X/100,
16
- * think yours can beat it?") are commented out in place rather than reworded,
17
- * because there is nothing left of them once the number goes.
18
- *
19
- * `ShareCtx.score` is kept and still populated so restoring is re-adding the
20
- * clause, not re-plumbing the type. The originals are preserved verbatim in the
21
- * block at the bottom of this file.
10
+ * Each template references the score and/or archetype and ends on the
11
+ * install-free CTA + handle. We intentionally never include a URL: a bare link
12
+ * would make X/LinkedIn render a link-preview card and swallow the copied
13
+ * image. The npx command is the call to action instead.
22
14
  */
23
15
  import type { Grade } from "@/src/audit/scoring";
24
16
 
25
17
  export interface ShareCtx {
18
+ score: number;
26
19
  /** Lowercased archetype name, e.g. "the cowboy". */
27
20
  arch: string;
21
+ grade: Grade;
28
22
  /** Count of unenabled prescribed policies. */
29
23
  missing: number;
30
- /** Optional ONLY because the score is switched off and nothing upstream
31
- * computes them any more. Restoring the score means making these required
32
- * again — which the compiler will then walk you through. */
33
- score?: number;
34
- grade?: Grade;
35
24
  }
36
25
 
37
26
  /** Call-to-action + handle per channel. No URL by design (see file header). */
@@ -44,107 +33,52 @@ const PASTE_LINE = "[ audit card copied to your clipboard — paste it into the
44
33
 
45
34
  /** Short, curiosity-forward — for X / Twitter. Ends on the npx CTA + @failproofai. */
46
35
  export const X_TEMPLATES: ((c: ShareCtx) => string)[] = [
47
- ({ arch }) =>
48
- `my agent has a personality and it's ${arch}. did not see that coming.\n\nran a failproof audit, all local. find out what yours is → ${X_CTA}`,
49
- ({ arch }) =>
50
- `spent 30 seconds auditing my agent. learned more about it than i did all month.\n\nit's ${arch}. → ${X_CTA}`,
51
- // Score-spined nothing survives removing the number:
52
- // ({ score }) =>
53
- // `my agent scored ${score}/100. think yours can beat it?\n\n30 sec audit, runs locally, gives you a personality too → ${X_CTA}`,
36
+ ({ score, arch }) =>
37
+ `my agent has a personality and it's ${arch}. did not see that coming.\n\nran a failproof audit, scored ${score}/100, all local. find out what yours is → ${X_CTA}`,
38
+ ({ score, arch }) =>
39
+ `spent 30 seconds auditing my agent. learned more about it than i did all month.\n\nit's ${arch}. scored ${score}/100. → ${X_CTA}`,
40
+ ({ score }) =>
41
+ `my agent scored ${score}/100. think yours can beat it?\n\n30 sec audit, runs locally, gives you a personality too → ${X_CTA}`,
54
42
  ({ arch }) =>
55
43
  `turns out my agent is ${arch} and honestly it explains everything.\n\n→ ${X_CTA}`,
56
- ({ arch }) =>
57
- `ran a failproof audit on my agent. it reads how it actually behaves when things break, not just the happy path.\n\npersonality: ${arch}. all local → ${X_CTA}`,
58
- ({ arch }) =>
59
- `got to know my agent today. it's ${arch}. wow.\n\naudit yours in 30s, nothing leaves your machine → ${X_CTA}`,
60
- ({ arch }) =>
61
- `your agent has a personality. you just haven't met it.\n\ni met mine today: ${arch}. → ${X_CTA}`,
62
- ({ arch }) =>
63
- `everyone talks about what their agent can build. nobody talks about how it acts when it breaks.\n\nmine's ${arch}, all local → ${X_CTA}`,
64
- ({ arch }) =>
65
- `every agent builder should run this once.\n\n30 sec, local, gives your agent a personality. mine: ${arch}.\n\n${X_CTA}`,
66
- ({ arch }) =>
67
- `i'll go first: ${arch}.\n\nwhat's your agent? 30 sec audit, no signup → ${X_CTA}`,
44
+ ({ score, arch }) =>
45
+ `ran a failproof audit on my agent. it reads how it actually behaves when things break, not just the happy path.\n\npersonality: ${arch}. score: ${score}/100. all local → ${X_CTA}`,
46
+ ({ score, arch }) =>
47
+ `got to know my agent today. it's ${arch} and scored ${score}/100. wow.\n\naudit yours in 30s, nothing leaves your machine → ${X_CTA}`,
48
+ ({ score, arch }) =>
49
+ `your agent has a personality. you just haven't met it.\n\ni met mine today: ${arch}, ${score}/100. → ${X_CTA}`,
50
+ ({ score, arch }) =>
51
+ `everyone talks about what their agent can build. nobody talks about how it acts when it breaks.\n\nmine's ${arch}, scored ${score}/100, all local → ${X_CTA}`,
52
+ ({ score, arch }) =>
53
+ `every agent builder should run this once.\n\n30 sec, local, gives your agent a personality and a quality score. mine: ${arch}, ${score}/100.\n\n${X_CTA}`,
54
+ ({ score, arch }) =>
55
+ `i'll go first: ${arch}, ${score}/100.\n\nwhat's your agent? 30 sec audit, no signup → ${X_CTA}`,
68
56
  ];
69
57
 
70
58
  /** Longer, reflective — for LinkedIn. Ends on the npx CTA + @Failproof AI. */
71
59
  export const LI_TEMPLATES: ((c: ShareCtx) => string)[] = [
72
- ({ arch }) =>
73
- `I've spent more hours with my agent this month than with most people I know. Today I realized I couldn't tell you a single thing about how it actually behaves.\n\nSo I audited it. Turns out it's ${arch}.\n\nThe audit reads its real history, including how it handles failures, not just the clean runs. 30 seconds, runs entirely locally.\n\nMeet yours: ${LI_CTA}`,
74
- ({ arch }) =>
75
- `Took a personality test today. It wasn't for me — it was for my AI agent.\n\nThe result: ${arch}.\n\nIt works by reading the agent's actual run history, so the personality comes from how it really behaves, not a vibe. Took 30 seconds and stayed local.\n\nTry it on yours: ${LI_CTA}`,
76
- ({ arch }) =>
77
- `Everyone asks what their AI agent can build. I just found out mine has a temper.\n\nAfter watching how it reacts to a failed command, the audit called it ${arch}.\n\nOddly useful to see it written down. 30 seconds, all local: ${LI_CTA}`,
78
- ({ arch }) =>
79
- `My agent does this very specific thing every time a command fails. Today I learned there's basically a name for it.\n\nRan a quick audit and it came back ${arch}. The whole read is based on how the agent handles things going wrong, which is where its real character shows.\n\n30 seconds, nothing leaves your machine: ${LI_CTA}`,
80
- // Score-spined the opening line is the number:
81
- // ({ score, arch }) =>
82
- // `My AI agent scored ${score}/100 today. I didn't know you could even measure that.\n\nThe same audit also handed it a personality: ${arch}.\n\nIt reads the agent's real history and tells you where it's solid and where it slips. Took 30 seconds and ran fully locally: ${LI_CTA}`,
83
- ({ arch }) =>
84
- `I'm usually the first to scroll past "audit your X" tools. This one took 30 seconds and actually told me something.\n\nMy agent is ${arch}.\n\nIt reads the agent's real run history rather than asking me anything, and none of it leaves the machine: ${LI_CTA}`,
85
- ({ arch }) =>
86
- `What is your agent's personality? I realized today that I had no answer.\n\nSo I checked. Mine is ${arch}, with a clear read on where it shines and where it slips.\n\nIf you work with agents, run it and drop yours in the comments. 30 seconds, fully local: ${LI_CTA}`,
87
- ({ arch }) =>
88
- `I trusted my agent a little less after this morning, and quite a bit more by the afternoon.\n\nI ran an audit on it. It showed me exactly where it's reliable and where it isn't, and gave it a personality: ${arch}.\n\nKnowing the gaps is what made me trust it more. 30 seconds, all local: ${LI_CTA}`,
89
- ({ arch }) =>
90
- `I almost skipped this because I assumed it would quietly ship my code off somewhere. It doesn't. Everything runs locally.\n\n30 seconds later I had my agent's personality: ${arch}.\n\nIf privacy is usually what stops you from trying these, this one is different: ${LI_CTA}`,
91
- ({ arch }) =>
92
- `Found out my AI agent has a personality type. Found out it's ${arch}. Found out that explains a lot.\n\nThe audit reads how the agent handles failures, not just the runs where everything goes fine.\n\n30 seconds, runs locally: ${LI_CTA}`,
60
+ ({ score, arch }) =>
61
+ `I've spent more hours with my agent this month than with most people I know. Today I realized I couldn't tell you a single thing about how it actually behaves.\n\nSo I audited it. Turns out it's ${arch}, and it scored ${score}/100.\n\nThe audit reads its real history, including how it handles failures, not just the clean runs. 30 seconds, runs entirely locally.\n\nMeet yours: ${LI_CTA}`,
62
+ ({ score, arch }) =>
63
+ `Took a personality test today. It wasn't for me — it was for my AI agent.\n\nThe result: ${arch}, with a quality score of ${score}/100.\n\nIt works by reading the agent's actual run history, so the personality comes from how it really behaves, not a vibe. Took 30 seconds and stayed local.\n\nTry it on yours: ${LI_CTA}`,
64
+ ({ score, arch }) =>
65
+ `Everyone asks what their AI agent can build. I just found out mine has a temper.\n\nAfter watching how it reacts to a failed command, the audit called it ${arch}. It also scored its quality at ${score}/100.\n\nOddly useful to see it written down. 30 seconds, all local: ${LI_CTA}`,
66
+ ({ score, arch }) =>
67
+ `My agent does this very specific thing every time a command fails. Today I learned there's basically a name for it.\n\nRan a quick audit and it came back ${arch}, scored ${score}/100. The whole read is based on how the agent handles things going wrong, which is where its real character shows.\n\n30 seconds, nothing leaves your machine: ${LI_CTA}`,
68
+ ({ score, arch }) =>
69
+ `My AI agent scored ${score}/100 today. I didn't know you could even measure that.\n\nThe same audit also handed it a personality: ${arch}.\n\nIt reads the agent's real history and tells you where it's solid and where it slips. Took 30 seconds and ran fully locally: ${LI_CTA}`,
70
+ ({ score, arch }) =>
71
+ `I'm usually the first to scroll past "audit your X" tools. This one took 30 seconds and actually told me something.\n\nMy agent is ${arch}, and it scored ${score}/100.\n\nIt reads the agent's real run history rather than asking me anything, and none of it leaves the machine: ${LI_CTA}`,
72
+ ({ score, arch }) =>
73
+ `What is your agent's personality? I realized today that I had no answer.\n\nSo I checked. Mine is ${arch}, scored ${score}/100, with a clear read on where it shines and where it slips.\n\nIf you work with agents, run it and drop yours in the comments. 30 seconds, fully local: ${LI_CTA}`,
74
+ ({ score, arch }) =>
75
+ `I trusted my agent a little less after this morning, and quite a bit more by the afternoon.\n\nI ran an audit on it. It showed me exactly where it's reliable and where it isn't, gave it a personality (${arch}), and scored it ${score}/100.\n\nKnowing the gaps is what made me trust it more. 30 seconds, all local: ${LI_CTA}`,
76
+ ({ score, arch }) =>
77
+ `I almost skipped this because I assumed it would quietly ship my code off somewhere. It doesn't. Everything runs locally.\n\n30 seconds later I had my agent's personality (${arch}) and a quality score (${score}/100).\n\nIf privacy is usually what stops you from trying these, this one is different: ${LI_CTA}`,
78
+ ({ score, arch }) =>
79
+ `Found out my AI agent has a personality type. Found out it's ${arch}. Found out that explains a lot.\n\nIt scored ${score}/100 too. The audit reads how the agent handles failures, not just the runs where everything goes fine.\n\n30 seconds, runs locally: ${LI_CTA}`,
93
80
  ];
94
81
 
95
- /* ---------------------------------------------------------------------------
96
- * THE ORIGINAL SCORE-BEARING COPY, verbatim. To restore: put these two arrays
97
- * back in place of the two above, and re-enable the score at its source (see
98
- * `src/audit/scoring.ts`). Nothing else has to change — `ShareCtx` is unchanged
99
- * and `score` is still populated by `audit-poster.tsx`.
100
- *
101
- * export const X_TEMPLATES: ((c: ShareCtx) => string)[] = [
102
- * ({ score, arch }) =>
103
- * `my agent has a personality and it's ${arch}. did not see that coming.\n\nran a failproof audit, scored ${score}/100, all local. find out what yours is → ${X_CTA}`,
104
- * ({ score, arch }) =>
105
- * `spent 30 seconds auditing my agent. learned more about it than i did all month.\n\nit's ${arch}. scored ${score}/100. → ${X_CTA}`,
106
- * ({ score }) =>
107
- * `my agent scored ${score}/100. think yours can beat it?\n\n30 sec audit, runs locally, gives you a personality too → ${X_CTA}`,
108
- * ({ arch }) =>
109
- * `turns out my agent is ${arch} and honestly it explains everything.\n\n→ ${X_CTA}`,
110
- * ({ score, arch }) =>
111
- * `ran a failproof audit on my agent. it reads how it actually behaves when things break, not just the happy path.\n\npersonality: ${arch}. score: ${score}/100. all local → ${X_CTA}`,
112
- * ({ score, arch }) =>
113
- * `got to know my agent today. it's ${arch} and scored ${score}/100. wow.\n\naudit yours in 30s, nothing leaves your machine → ${X_CTA}`,
114
- * ({ score, arch }) =>
115
- * `your agent has a personality. you just haven't met it.\n\ni met mine today: ${arch}, ${score}/100. → ${X_CTA}`,
116
- * ({ score, arch }) =>
117
- * `everyone talks about what their agent can build. nobody talks about how it acts when it breaks.\n\nmine's ${arch}, scored ${score}/100, all local → ${X_CTA}`,
118
- * ({ score, arch }) =>
119
- * `every agent builder should run this once.\n\n30 sec, local, gives your agent a personality and a quality score. mine: ${arch}, ${score}/100.\n\n${X_CTA}`,
120
- * ({ score, arch }) =>
121
- * `i'll go first: ${arch}, ${score}/100.\n\nwhat's your agent? 30 sec audit, no signup → ${X_CTA}`,
122
- * ];
123
- *
124
- * export const LI_TEMPLATES: ((c: ShareCtx) => string)[] = [
125
- * ({ score, arch }) =>
126
- * `I've spent more hours with my agent this month than with most people I know. Today I realized I couldn't tell you a single thing about how it actually behaves.\n\nSo I audited it. Turns out it's ${arch}, and it scored ${score}/100.\n\nThe audit reads its real history, including how it handles failures, not just the clean runs. 30 seconds, runs entirely locally.\n\nMeet yours: ${LI_CTA}`,
127
- * ({ score, arch }) =>
128
- * `Took a personality test today. It wasn't for me — it was for my AI agent.\n\nThe result: ${arch}, with a quality score of ${score}/100.\n\nIt works by reading the agent's actual run history, so the personality comes from how it really behaves, not a vibe. Took 30 seconds and stayed local.\n\nTry it on yours: ${LI_CTA}`,
129
- * ({ score, arch }) =>
130
- * `Everyone asks what their AI agent can build. I just found out mine has a temper.\n\nAfter watching how it reacts to a failed command, the audit called it ${arch}. It also scored its quality at ${score}/100.\n\nOddly useful to see it written down. 30 seconds, all local: ${LI_CTA}`,
131
- * ({ score, arch }) =>
132
- * `My agent does this very specific thing every time a command fails. Today I learned there's basically a name for it.\n\nRan a quick audit and it came back ${arch}, scored ${score}/100. The whole read is based on how the agent handles things going wrong, which is where its real character shows.\n\n30 seconds, nothing leaves your machine: ${LI_CTA}`,
133
- * ({ score, arch }) =>
134
- * `My AI agent scored ${score}/100 today. I didn't know you could even measure that.\n\nThe same audit also handed it a personality: ${arch}.\n\nIt reads the agent's real history and tells you where it's solid and where it slips. Took 30 seconds and ran fully locally: ${LI_CTA}`,
135
- * ({ score, arch }) =>
136
- * `I'm usually the first to scroll past "audit your X" tools. This one took 30 seconds and actually told me something.\n\nMy agent is ${arch}, and it scored ${score}/100.\n\nIt reads the agent's real run history rather than asking me anything, and none of it leaves the machine: ${LI_CTA}`,
137
- * ({ score, arch }) =>
138
- * `What is your agent's personality? I realized today that I had no answer.\n\nSo I checked. Mine is ${arch}, scored ${score}/100, with a clear read on where it shines and where it slips.\n\nIf you work with agents, run it and drop yours in the comments. 30 seconds, fully local: ${LI_CTA}`,
139
- * ({ score, arch }) =>
140
- * `I trusted my agent a little less after this morning, and quite a bit more by the afternoon.\n\nI ran an audit on it. It showed me exactly where it's reliable and where it isn't, gave it a personality (${arch}), and scored it ${score}/100.\n\nKnowing the gaps is what made me trust it more. 30 seconds, all local: ${LI_CTA}`,
141
- * ({ score, arch }) =>
142
- * `I almost skipped this because I assumed it would quietly ship my code off somewhere. It doesn't. Everything runs locally.\n\n30 seconds later I had my agent's personality (${arch}) and a quality score (${score}/100).\n\nIf privacy is usually what stops you from trying these, this one is different: ${LI_CTA}`,
143
- * ({ score, arch }) =>
144
- * `Found out my AI agent has a personality type. Found out it's ${arch}. Found out that explains a lot.\n\nIt scored ${score}/100 too. The audit reads how the agent handles failures, not just the runs where everything goes fine.\n\n30 seconds, runs locally: ${LI_CTA}`,
145
- * ];
146
- * ------------------------------------------------------------------------- */
147
-
148
82
  /** djb2 hash — stable per seed so the template choice is deterministic. */
149
83
  function hashStr(s: string): number {
150
84
  let h = 5381;
@@ -496,13 +496,7 @@
496
496
  .poster-meta .of { color: var(--dim); }
497
497
  .poster-meta .poster-sep { color: var(--dim); }
498
498
 
499
- /* body — vertical centered stack: sigil → persona → score ───
500
- The score block is switched off (see `src/audit/scoring.ts`), so this stack
501
- currently renders two children, not three. It is a centered flex column with
502
- a gap and no fixed track count, so it re-centers on its own. The
503
- `.poster-score` rules below are kept, dormant, against the score coming
504
- back — do not tidy them away, or restoring it means rewriting the styles
505
- too. */
499
+ /* body — vertical centered stack: sigil → persona → score ─── */
506
500
  .poster-body {
507
501
  display: flex;
508
502
  flex-direction: column;
@@ -561,7 +555,6 @@
561
555
  .poster-persona .persona-rarity .pct { color: var(--accent-pink); font-weight: 600; }
562
556
 
563
557
  /* score — score-n + /100, baseline-aligned and centered in the card. */
564
- /* Dormant while the score is switched off — see `src/audit/scoring.ts`. */
565
558
  .poster-score {
566
559
  display: flex;
567
560
  align-items: baseline;
@@ -1164,127 +1157,3 @@
1164
1157
  .share-card {
1165
1158
  max-width: 420px;
1166
1159
  }
1167
-
1168
- /* ── Leak report ─────────────────────────────────────────────────────────────
1169
- One block per distinct credential. Deliberately NOT a table: a table implies
1170
- columns worth scanning down, and the interesting comparison between two
1171
- leaked keys is never "which is longer" — it is what each one is and where it
1172
- went, which reads as a sentence. */
1173
-
1174
- .leak-notify { margin-left: auto; }
1175
-
1176
- .leak-list {
1177
- list-style: none;
1178
- margin: 20px 0 0;
1179
- padding: 0;
1180
- display: flex;
1181
- flex-direction: column;
1182
- gap: 10px;
1183
- }
1184
-
1185
- .leak-row {
1186
- border: 1px solid var(--line-2);
1187
- /* Hard-offset pixel shadow, never a blur — the house rule. Pink is the
1188
- accent that means "this one is yours to act on". */
1189
- box-shadow: 4px 4px 0 0 var(--line-2);
1190
- padding: 14px 16px;
1191
- display: flex;
1192
- flex-direction: column;
1193
- gap: 8px;
1194
- background: var(--bg);
1195
- }
1196
- /* A recognised prefix means a console exists and this is revocable in a
1197
- minute. That is a real difference in what the reader has to do, so it is the
1198
- one thing the row's own frame encodes. */
1199
- .leak-row[data-attributed="true"] { box-shadow: 4px 4px 0 0 var(--accent-pink); }
1200
-
1201
- .leak-row-top {
1202
- display: flex;
1203
- align-items: baseline;
1204
- gap: 10px;
1205
- flex-wrap: wrap;
1206
- }
1207
-
1208
- .leak-key {
1209
- font-family: var(--font-mono);
1210
- font-size: 14px;
1211
- color: var(--ink);
1212
- letter-spacing: 0.02em;
1213
- /* The masked value can be long; it must wrap rather than push the row wide. */
1214
- word-break: break-all;
1215
- }
1216
-
1217
- .leak-label {
1218
- font-family: var(--font-mono);
1219
- font-size: 11px;
1220
- color: var(--accent-pink);
1221
- text-transform: lowercase;
1222
- }
1223
-
1224
- .leak-name {
1225
- font-family: var(--font-mono);
1226
- font-size: 11px;
1227
- color: var(--ink-2);
1228
- border: 1px solid var(--line-2);
1229
- padding: 1px 5px;
1230
- }
1231
-
1232
- .leak-dismiss {
1233
- margin-left: auto;
1234
- font-family: var(--font-mono);
1235
- font-size: 10px;
1236
- color: var(--dim);
1237
- background: none;
1238
- border: 1px solid var(--line-2);
1239
- padding: 3px 8px;
1240
- cursor: pointer;
1241
- text-transform: lowercase;
1242
- }
1243
- .leak-dismiss:hover:not(:disabled) { color: var(--ink); border-color: var(--ink-2); }
1244
- .leak-dismiss:disabled { opacity: 0.4; cursor: not-allowed; }
1245
- .leak-dismiss:focus-visible { outline: 2px solid var(--accent-pink); outline-offset: 2px; }
1246
-
1247
- .leak-how {
1248
- font-family: var(--font-mono);
1249
- font-size: 12px;
1250
- color: var(--ink-2);
1251
- line-height: 1.6;
1252
- }
1253
- .leak-verb { color: var(--ink); }
1254
- .leak-where { color: var(--accent-green); }
1255
- .leak-who { color: var(--ink); }
1256
- .leak-when { color: var(--dim); }
1257
-
1258
- .leak-foot {
1259
- display: flex;
1260
- gap: 14px;
1261
- flex-wrap: wrap;
1262
- font-family: var(--font-mono);
1263
- font-size: 10px;
1264
- color: var(--dim);
1265
- }
1266
- .leak-fix { color: var(--accent-green); }
1267
- .leak-dir { color: var(--ink-2); }
1268
-
1269
- @media (max-width: 640px) {
1270
- .leak-dismiss { margin-left: 0; }
1271
- .leak-foot { gap: 6px; flex-direction: column; }
1272
- }
1273
-
1274
- /* The re-scan control in the scan header. Sits at the end of the eyebrow row so
1275
- it reads as an action on that section rather than a page-level nav item. */
1276
- .scan-rerun {
1277
- margin-left: auto;
1278
- font-family: var(--font-mono);
1279
- font-size: 10.5px;
1280
- letter-spacing: 0.1em;
1281
- text-transform: lowercase;
1282
- color: var(--ink-2);
1283
- background: none;
1284
- border: 1px solid var(--line-2);
1285
- padding: 4px 10px;
1286
- cursor: pointer;
1287
- }
1288
- .scan-rerun:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-pink); }
1289
- .scan-rerun:disabled { opacity: 0.5; cursor: not-allowed; }
1290
- .scan-rerun:focus-visible { outline: 2px solid var(--accent-pink); outline-offset: 2px; }
@@ -30,7 +30,7 @@ export const dynamic = "force-dynamic";
30
30
  export default async function SettingsPage() {
31
31
  // Same gate the audit, policies and projects pages carry. It was dropped in
32
32
  // the rewrite, and this is the page that least deserves to lose it: it shows
33
- // the address credential alerts go to and can sign the machine out, on a dashboard an
33
+ // the address digests go to and can sign the machine out, on a dashboard an
34
34
  // operator may deliberately be exposing beyond localhost.
35
35
  const disabled = (process.env.FAILPROOFAI_DISABLE_PAGES ?? "")
36
36
  .split(",").map((s) => s.trim()).filter(Boolean);
@@ -29,8 +29,12 @@
29
29
  * acts and for anything wrong, mint for the thing that is alive. No third hue,
30
30
  * no new webfont, one hard pixel shadow on the console. That is the budget.
31
31
  *
32
- * Scheduling and system notifications are separate machine settings. Turning
33
- * notifications off must not silently stop the seven-day scan or email alerts.
32
+ * ## One switch, not two
33
+ *
34
+ * Scheduling and mailing are the same decision — the reason to put a scan on a
35
+ * timer is to be told what it found. So there is one toggle, it requires a
36
+ * sign-in, and "signed out with the timer on" is a real state the page names
37
+ * rather than a contradiction it prevents.
34
38
  */
35
39
 
36
40
  import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
@@ -40,7 +44,6 @@ import {
40
44
  } from "@/app/actions/get-scheduled-audit";
41
45
  import {
42
46
  setAutoAuditAction,
43
- setAuditNotifyAction,
44
47
  setAuditIntervalAction,
45
48
  } from "@/app/actions/update-scheduled-audit";
46
49
  import { triggerRun, RerunError } from "@/app/audit/_components/rerun-button";
@@ -184,9 +187,12 @@ function Toggle({
184
187
  * What the scan does, as three labelled lines. This is the old footer paragraph
185
188
  * restructured — same claims, scannable instead of a wall.
186
189
  *
187
- * "sends" names the exact remote payload. The scheduled scan itself stays
188
- * local, and no report is attempted unless a credential leak exists and the
189
- * machine has a verified email identity.
190
+ * "sends" ENUMERATES rather than saying "only counts and redacted examples".
191
+ * That was very nearly true, and very nearly true is the worse kind: the report
192
+ * carries the machine's name too its hostname, which routinely carries its
193
+ * owner's. A list a person can check beats a stronger claim they cannot, and
194
+ * this panel is the one place they would come to check. The digest email states
195
+ * the same three, in the same order.
190
196
  */
191
197
  const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
192
198
  {
@@ -196,7 +202,7 @@ const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
196
202
  { label: "runs", body: "entirely on this machine. the transcripts never leave it." },
197
203
  {
198
204
  label: "sends",
199
- body: "only the newest masked credential exposure, its CLI, time, location, and this machine's name — only when email is configured.",
205
+ body: "counts, redacted examples, and this machine's name — and only when a scan finds something harmful.",
200
206
  },
201
207
  ];
202
208
 
@@ -205,7 +211,6 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
205
211
  // about whether scheduled audits are on. See the note in `page.tsx`.
206
212
  const [view, setView] = useState<ScheduledAuditView | null>(initial);
207
213
  const [auto, setAuto] = useState(initial?.auto ?? false);
208
- const [notify, setNotify] = useState(initial?.notify ?? true);
209
214
  const [intervalDays, setIntervalDays] = useState(initial?.intervalDays ?? 7);
210
215
  const [busy, setBusy] = useState(false);
211
216
  const [running, setRunning] = useState(false);
@@ -249,7 +254,6 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
249
254
  setView(next);
250
255
  hasView.current = true;
251
256
  setAuto(next.auto);
252
- setNotify(next.notify);
253
257
  setIntervalDays(next.intervalDays);
254
258
  setNowMs(Date.now());
255
259
  setLoadError(false);
@@ -308,6 +312,26 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
308
312
  setBusy(true);
309
313
  try {
310
314
  const res = await setAutoAuditAction(true);
315
+ if (!res.ok) {
316
+ setAuto(false);
317
+ if (res.reason === "unreachable") {
318
+ // The session is fine and the network is not. Offering a code prompt
319
+ // here would name a failure the user does not have and hand them a
320
+ // flow that cannot succeed either — and abandoning it mid-way is how
321
+ // a working session gets replaced with none at all.
322
+ toast("could not reach the server. check your connection and try again.");
323
+ return;
324
+ }
325
+ // The server rejected the session this page had been showing an address
326
+ // for — expired, or minted against a different api-server. The local
327
+ // file is the only thing that said "signed in", and `whoAmI` has since
328
+ // cleared it, so re-read before opening the dialog: otherwise the page
329
+ // asks for an email while still displaying one.
330
+ await reload();
331
+ setAuthOpen(true);
332
+ toast("that sign-in expired. one more code and it's on.");
333
+ return;
334
+ }
311
335
  setAuto(res.auto);
312
336
  toast("scheduled audits on.");
313
337
  await reload();
@@ -336,23 +360,13 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
336
360
  }
337
361
  return;
338
362
  }
339
- await enable();
340
- }, [auto, enable, reload]);
341
-
342
- const onNotifyToggle = useCallback(async () => {
343
- const requested = !notify;
344
- setBusy(true);
345
- try {
346
- const res = await setAuditNotifyAction(requested);
347
- setNotify(res.notify);
348
- setView((v) => (v ? { ...v, notify: res.notify } : v));
349
- toast(res.notify ? "system notifications on." : "system notifications off.");
350
- } catch {
351
- toast(`could not turn system notifications ${requested ? "on" : "off"}.`);
352
- } finally {
353
- setBusy(false);
363
+ // Turning it on needs somewhere to send the digest.
364
+ if (!signedIn) {
365
+ setAuthOpen(true);
366
+ return;
354
367
  }
355
- }, [notify]);
368
+ await enable();
369
+ }, [auto, enable, reload, signedIn]);
356
370
 
357
371
  const commitInterval = useCallback(
358
372
  async (raw: number) => {
@@ -389,7 +403,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
389
403
  setBusy(true);
390
404
  try {
391
405
  await fetch("/api/auth/logout", { method: "POST" });
392
- toast("signed out. scans and desktop notifications continue; email alerts pause.");
406
+ toast("signed out. scans continue; digests pause.");
393
407
  await reload();
394
408
  } catch {
395
409
  toast("could not sign out.");
@@ -520,9 +534,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
520
534
  </span>
521
535
  <span className="set-dim">
522
536
  {auto
523
- ? signedIn
524
- ? "scheduled scans are active; the latest masked leak is emailed."
525
- : "scheduled scans are active. add an email below for masked alerts."
537
+ ? "you'll get an email only when a scan finds something."
526
538
  : "off. nothing runs and nothing is sent."}
527
539
  </span>
528
540
  </div>
@@ -554,23 +566,6 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
554
566
  </span>
555
567
  </div>
556
568
 
557
- <div className="set-row set-row-main">
558
- <Toggle
559
- enabled={notify}
560
- disabled={busy || view === null}
561
- onChange={() => void onNotifyToggle()}
562
- label={notify ? "turn off system notifications" : "turn on system notifications"}
563
- />
564
- <div className="set-row-copy">
565
- <span className="set-strong">system notifications.</span>
566
- <span className="set-dim">
567
- {notify
568
- ? "on when a scheduled scan finds credentials."
569
- : "off. scheduled scans and email alerts continue."}
570
- </span>
571
- </div>
572
- </div>
573
-
574
569
  <div className="set-rule" />
575
570
 
576
571
  <div className="set-row set-row-identity">
@@ -598,7 +593,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
598
593
  // from it at all.
599
594
  <>
600
595
  <span className="set-warn">
601
- signed out — scheduled scans continue; email alerts are paused.
596
+ signed out — scans continue, digests are paused.
602
597
  </span>
603
598
  <button
604
599
  type="button"
@@ -606,13 +601,13 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
606
601
  disabled={busy}
607
602
  onClick={() => setAuthOpen(true)}
608
603
  >
609
- add email alerts
604
+ sign in to resume
610
605
  </button>
611
606
  </>
612
607
  ) : (
613
608
  <span className="set-dim">
614
- scans and system notifications work without an account.
615
- add email later if you want alerts in your inbox.
609
+ turning this on asks for an email, so there is somewhere to
610
+ send the report.
616
611
  </span>
617
612
  )}
618
613
  </div>
@@ -253,49 +253,21 @@ export interface AuditReportBody {
253
253
  last_seen?: string;
254
254
  examples: string[];
255
255
  }[];
256
- /**
257
- * Credentials this machine found in its own transcripts.
258
- *
259
- * Optional on the wire, so a CLI newer than the api-server sends a field the
260
- * server ignores rather than failing the whole report — the harmful counts
261
- * are the older half of the contract and must keep arriving either way.
262
- *
263
- * Every field here is a mask, a label, a length or a location. There is no
264
- * shape of this object that carries a secret, because the record it is built
265
- * from never stores one: `recordLeaks` fingerprints at detection time and
266
- * keeps only the masked rendering.
267
- */
268
- leaks?: {
269
- id: string;
270
- display: string;
271
- label: string;
272
- length: number;
273
- attributed: boolean;
274
- name: string | null;
275
- confidence: string;
276
- first_seen: string;
277
- last_seen: string;
278
- occurrences: number;
279
- cli: string;
280
- project: string;
281
- mechanism: string;
282
- direction: "input" | "result";
283
- }[];
284
256
  }
285
257
 
286
258
  export interface AuditReportResult {
287
259
  report_id: string;
288
260
  /** Whether this report produced an email. */
289
261
  emailed: boolean;
290
- /** `no_leak`, `cooldown`, `send_failed`, or null when mail went out. */
262
+ /** `below_threshold`, `cooldown`, `send_failed`, or null when mail went out. */
291
263
  reason: string | null;
292
264
  /**
293
265
  * Where the next window starts, per the SERVER.
294
266
  *
295
267
  * Persisted verbatim rather than computed locally. The server anchors it on
296
- * the last DELIVERED alert, so a report held by the cooldown — or one whose
268
+ * the last DELIVERED digest, so a report held by the cooldown — or one whose
297
269
  * send failed — correctly leaves the watermark where it was, and its findings
298
- * turn up in the next alert instead of falling into a gap. A machine that
270
+ * turn up in the next digest instead of falling into a gap. A machine that
299
271
  * lost `machine.json` also resyncs here rather than re-reporting from the
300
272
  * beginning of time.
301
273
  */
@@ -303,11 +275,11 @@ export interface AuditReportResult {
303
275
  }
304
276
 
305
277
  /**
306
- * Submit one scheduled scan's harmful findings and newest masked credential.
278
+ * Submit one scheduled scan's harmful findings.
307
279
  *
308
280
  * Called only by the audit child, and only on `--scheduled`. The destination
309
281
  * address is never sent: the api-server takes it from the access-token claims,
310
- * so a report cannot name where its email alert goes.
282
+ * so a report cannot name where its digest goes.
311
283
  */
312
284
  export async function submitAuditReport(
313
285
  accessToken: string,