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
@@ -16,11 +16,6 @@ import { INTEGRATION_TYPES, type IntegrationType } from "../hooks/types";
16
16
  import { ADAPTERS } from "./cli-adapters";
17
17
  import { AUDIT_DETECTORS } from "./detectors";
18
18
  import { severityForBuiltin } from "./features";
19
- import { findSecrets, flattenToolInput } from "./leak-scan";
20
- import { fingerprintSecret, fingerprintId } from "./leak-fingerprint";
21
- import { readLeakIdentity, readLeakRecord, writeLeakRecord } from "./leak-store";
22
- import { upsertFinding, describeMechanism, emptyRecord } from "./leak-record";
23
- import { shortenPaths } from "./redact-example";
24
19
  import { readCachedTranscript, writeCachedTranscriptResult } from "./cache";
25
20
  import { initReplay, replayEvent, restoreReplay } from "./replay";
26
21
  import {
@@ -37,64 +32,6 @@ import {
37
32
 
38
33
  const TRANSCRIPT_CONCURRENCY = 8;
39
34
 
40
- /**
41
- * Raw transcript bytes allowed in flight at once.
42
- *
43
- * The concurrency above counts FILES, which is the wrong unit. `streamEventsFrom`
44
- * returns an array — despite the name it materialises every event of a
45
- * transcript into JS objects — and a 57 MB JSONL becomes several hundred MB of
46
- * them. On a real 1.15 GB corpus, 7 files hold 224 MB and the largest is
47
- * 57.5 MB, so eight workers could put a quarter of the corpus in memory
48
- * simultaneously. That machine printed `Aborted(OOM)` 22 times in one run.
49
- *
50
- * This is NOT what fixed those lines, and the distinction is worth keeping
51
- * honest: they were measured at ~22 per run, appear within the first two
52
- * seconds, are unchanged by this budget at 48 MB or at 16 MB, and do not fail
53
- * the run — its counts come back correct. Their source is still unidentified.
54
- * This bounds a real and separate failure mode (eight of the seven largest
55
- * files in flight at once); it should not be read as having fixed the other.
56
- *
57
- * 48 MB of raw input is the budget because the expansion factor from JSONL to
58
- * parsed objects is roughly 5-10x, which keeps the peak inside a default heap
59
- * with room for the eight small-file workers this is meant not to slow down.
60
- * Chosen to bound memory, not to be exactly right: the failure it prevents is a
61
- * crash, and the cost of being conservative is that one very large file scans
62
- * alone for a moment.
63
- */
64
- const TRANSCRIPT_BYTE_BUDGET = 48 * 1024 * 1024;
65
-
66
- /**
67
- * Admission control by weight, so a worker waits for MEMORY as well as a slot.
68
- *
69
- * A file bigger than the whole budget is admitted alone rather than refused —
70
- * otherwise the largest transcript on the machine could never be scanned, which
71
- * is precisely the one most likely to hold something.
72
- */
73
- export class ByteGate {
74
- private inFlight = 0;
75
- private waiting: Array<() => void> = [];
76
-
77
- constructor(private readonly budget: number) {}
78
-
79
- async acquire(bytes: number): Promise<void> {
80
- const want = Math.max(0, bytes);
81
- while (this.inFlight > 0 && this.inFlight + want > this.budget) {
82
- await new Promise<void>((resolve) => this.waiting.push(resolve));
83
- }
84
- this.inFlight += want;
85
- }
86
-
87
- release(bytes: number): void {
88
- this.inFlight -= Math.max(0, bytes);
89
- if (this.inFlight < 0) this.inFlight = 0;
90
- // Wake everyone and let each re-test its own weight: a small task behind a
91
- // large one should not be held by a queue position it does not need.
92
- const waiters = this.waiting;
93
- this.waiting = [];
94
- for (const wake of waiters) wake();
95
- }
96
- }
97
-
98
35
  /** Canonicalize a policy name to its short, qualified form for display
99
36
  * (`failproofai/foo` → `foo`). */
100
37
  function shortPolicyName(name: string): string {
@@ -176,50 +113,6 @@ interface ScanOutcome {
176
113
  resumed: boolean;
177
114
  }
178
115
 
179
- const CREDENTIAL_RESEARCH_MARKERS: ReadonlyArray<RegExp> = [
180
- /\b(?:secret|credential)[-_ ](?:scanner|detection|detector|pattern|regex|corpus)\b/i,
181
- /\b(?:trufflehog|trufflesecurity|gitleaks|detect-secrets|secret-detection-rules)\b/i,
182
- /\b(?:SECRET_PATTERNS|findSecrets|isDocsLiteral|leak-scan)\b/,
183
- /\b(?:fixture|placeholder|synthetic|sample) (?:api )?(?:key|token|secret|credential)s?\b/i,
184
- /\b(?:grep|rg|ripgrep|scan|hunt|search)\b[^\n]{0,240}\b(?:secret|credential|token)s?\b/i,
185
- ];
186
-
187
- const CREDENTIAL_RESEARCH_PURPOSE =
188
- /\b(?:hunt|scan|search|research|test)(?:ing)?\b[^\n]{0,80}\b(?:secret|credential|token)s?\b|\b(?:secret|credential|token)s?\b[^\n]{0,80}\b(?:scanner|detector|detection|research|fixtures?|corpus)\b/i;
189
-
190
- /**
191
- * Deliberate secret-detector research is full of realistic fake credentials.
192
- * Reporting those as leaks is the feedback loop that made one measured session
193
- * contribute 455 of the 500 rows in its own audit.
194
- *
195
- * Require independent signals across tool INPUTS. Results are excluded from
196
- * classification: a normal command can print hostile text, while commands and
197
- * URLs state what the agent intentionally set out to inspect. Two signals keep
198
- * an ordinary discussion that happens to say "secret scanner" from silencing a
199
- * session; dedicated detector/test work reliably carries several.
200
- */
201
- export function isCredentialResearchSession(
202
- events: NormalizedToolEvent[],
203
- sessionDescription?: string,
204
- ): boolean {
205
- if (sessionDescription && CREDENTIAL_RESEARCH_PURPOSE.test(sessionDescription)) return true;
206
- const matched = new Set<number>();
207
- let evidenceEvents = 0;
208
- for (const event of events) {
209
- const intent = flattenToolInput(event.toolInput);
210
- let eventMatched = false;
211
- for (let i = 0; i < CREDENTIAL_RESEARCH_MARKERS.length; i++) {
212
- if (CREDENTIAL_RESEARCH_MARKERS[i].test(intent)) {
213
- matched.add(i);
214
- eventMatched = true;
215
- }
216
- }
217
- if (eventMatched) evidenceEvents++;
218
- if (matched.size >= 2 || evidenceEvents >= 3) return true;
219
- }
220
- return false;
221
- }
222
-
223
116
  async function scanOneTranscript(
224
117
  meta: TranscriptMetadata,
225
118
  resume?: { fromByte: number; detectorState: DetectorSessionState },
@@ -288,27 +181,21 @@ async function scanOneTranscript(
288
181
  // Capture the session's cwd from the first event that carried one — every
289
182
  // event in a single transcript shares the same cwd by construction.
290
183
  result.cwd = result.cwd || events[0].cwd || "";
291
- const suppressLeakScan = isCredentialResearchSession(events, meta.sessionDescription);
292
- if (suppressLeakScan) result.leakScanSuppressed = "credential-research";
293
184
 
294
185
  for (const event of events) {
295
- // The 8 behavioural detectors are switched off with the rest of the old
296
- // audit see the header of `src/audit/scoring.ts`. The modules and their
297
- // unit tests are untouched; nothing calls them. `sessionState` is still
298
- // threaded through so a resumed scan keeps its shape and restoring this
299
- // loop needs no plumbing.
300
- // for (const detector of AUDIT_DETECTORS) {
301
- // const hit = detector.detect(event, sessionState);
302
- // if (!hit) continue;
303
- // recordHit(
304
- // result,
305
- // detector.name,
306
- // event.timestamp,
307
- // event.cwd,
308
- // truncateExample(hit.example),
309
- // );
310
- // }
311
- // Replay through every builtin policy.
186
+ // Run audit detectors first (stateful, must see every event).
187
+ for (const detector of AUDIT_DETECTORS) {
188
+ const hit = detector.detect(event, sessionState);
189
+ if (!hit) continue;
190
+ recordHit(
191
+ result,
192
+ detector.name,
193
+ event.timestamp,
194
+ event.cwd,
195
+ truncateExample(hit.example),
196
+ );
197
+ }
198
+ // Then replay through every builtin policy.
312
199
  let replayHits;
313
200
  try {
314
201
  replayHits = await replayEvent(event);
@@ -325,129 +212,11 @@ async function scanOneTranscript(
325
212
  truncateExample(example),
326
213
  );
327
214
  }
328
-
329
- // Credential VALUES, which the replay above cannot report: it returns a
330
- // decision per policy, never the text that matched. Scanned separately so
331
- // the leak report can name WHICH key, and so detection can be tuned for
332
- // precision without dragging the redactor's recall down with it.
333
- if (!suppressLeakScan) recordLeaks(result, event);
334
215
  }
335
216
 
336
217
  return { result, bytesScanned, detectorState: sessionState, resumed };
337
218
  }
338
219
 
339
- /**
340
- * Fingerprint every credential in one event and attach the sightings.
341
- *
342
- * The raw value exists only inside this function. It is read out of the event,
343
- * hashed into a salted id, rendered into a mask, and dropped — nothing that
344
- * leaves here can reconstruct it.
345
- *
346
- * Input and result are scanned separately and tagged, because they are
347
- * different exposures: an input is what the agent SENT (deniable at PreToolUse
348
- * on 12/12 CLIs, and 39.5% of measured appearances) while a result is what it
349
- * RECEIVED (blockable on 2/12, and only after the fact).
350
- */
351
- function recordLeaks(result: TranscriptAuditResult, event: NormalizedToolEvent): void {
352
- const salt = leakSalt();
353
- if (!salt) return;
354
-
355
- const filePath = (event.toolInput as { file_path?: unknown }).file_path;
356
- const shortPath = typeof filePath === "string" ? shortenPaths(filePath) : null;
357
-
358
- const scan = (text: string, direction: "input" | "result"): void => {
359
- for (const match of findSecrets(text)) {
360
- (result.leaks ??= []).push({
361
- id: fingerprintId(match.value, salt),
362
- fingerprint: fingerprintSecret(match.value, match.rule),
363
- name: match.name,
364
- rule: match.rule,
365
- shaped: match.shaped,
366
- timestamp: event.timestamp,
367
- cwd: event.cwd,
368
- toolName: event.toolName,
369
- direction,
370
- path: shortPath,
371
- });
372
- }
373
- };
374
-
375
- scan(flattenToolInput(event.toolInput), "input");
376
- if (event.toolResultText !== undefined) scan(event.toolResultText, "result");
377
- }
378
-
379
- /**
380
- * The machine's HMAC salt, read once per process.
381
- *
382
- * Cached because `scanOneTranscript` runs this per EVENT — 340,918 of them on
383
- * the measured corpus — and re-reading a file that many times would dominate
384
- * the scan. `null` when the store is unreadable, which disables leak recording
385
- * rather than failing the audit: a scan that cannot fingerprint is still a scan
386
- * that can count.
387
- */
388
- let cachedSalt: string | null | undefined;
389
- function leakSalt(): string | null {
390
- if (cachedSalt === undefined) {
391
- try {
392
- cachedSalt = readLeakIdentity().salt;
393
- } catch {
394
- cachedSalt = null;
395
- }
396
- }
397
- return cachedSalt;
398
- }
399
-
400
- /**
401
- * Merge this scan's sightings into `~/.failproofai/audit/leaks.json`.
402
- *
403
- * Returns the ids seen for the FIRST time, which is the set worth telling a
404
- * human about. Never throws: a scan that finds leaks it cannot write down has
405
- * still found them, and the caller reports on the in-memory result either way.
406
- */
407
- export function persistLeaks(perTranscript: TranscriptAuditResult[], replaceExisting: boolean): string[] {
408
- const fresh: string[] = [];
409
- try {
410
- const previous = readLeakRecord();
411
- const previousIds = new Set(previous.findings.map((f) => f.id));
412
- // A successful all-history scan is authoritative. Rebuilding is what lets
413
- // detector precision fixes remove findings they now reject; merging made
414
- // every false positive live for 90 days even after the code was fixed.
415
- // Scoped or incomplete scans still merge because absence outside their
416
- // coverage says nothing.
417
- const record = replaceExisting
418
- ? emptyRecord(previous.salt, new Date().toISOString())
419
- : previous;
420
- for (const t of perTranscript) {
421
- for (const leak of t.leaks ?? []) {
422
- const { isNew } = upsertFinding(record, {
423
- id: leak.id,
424
- fingerprint: leak.fingerprint,
425
- name: leak.name,
426
- rule: leak.rule,
427
- // A vendor shape is what licenses an alert to name a console; a
428
- // name-only match knows something leaked, not who issued it.
429
- confidence: leak.shaped ? "doc-verified" : "name-only",
430
- sighting: {
431
- cli: t.cli,
432
- sessionId: t.sessionId,
433
- cwd: shortenPaths(leak.cwd),
434
- at: leak.timestamp,
435
- mechanism: describeMechanism(leak.toolName, leak.direction, leak.path),
436
- },
437
- });
438
- // During an authoritative rebuild every insertion is new to the empty
439
- // record, but only ids absent from the previous record are new to the
440
- // HUMAN and eligible for a notification.
441
- if (isNew && !previousIds.has(leak.id)) fresh.push(leak.id);
442
- }
443
- }
444
- writeLeakRecord(record);
445
- } catch {
446
- // Bookkeeping failure must not fail the audit — see leak-store.ts.
447
- }
448
- return fresh;
449
- }
450
-
451
220
  function formatPolicyExample(_policyName: string, event: NormalizedToolEvent): string {
452
221
  if (event.toolName === "Bash") {
453
222
  const command = (event.toolInput as { command?: unknown }).command;
@@ -467,7 +236,7 @@ function formatPolicyExample(_policyName: string, event: NormalizedToolEvent): s
467
236
  * came from the FIRST event in the file and the tail's came from the first
468
237
  * event after the offset, so the older one wins.
469
238
  */
470
- export function mergeIncremental(
239
+ function mergeIncremental(
471
240
  cached: TranscriptAuditResult,
472
241
  tail: TranscriptAuditResult,
473
242
  ): TranscriptAuditResult {
@@ -481,12 +250,6 @@ export function mergeIncremental(
481
250
  examplesByName: {},
482
251
  rangeByName: { ...cached.rangeByName },
483
252
  };
484
- if (cached.leakScanSuppressed || tail.leakScanSuppressed) {
485
- out.leakScanSuppressed = "credential-research";
486
- out.leaks = [];
487
- } else {
488
- out.leaks = [...(cached.leaks ?? []), ...(tail.leaks ?? [])];
489
- }
490
253
  for (const [name, list] of Object.entries(cached.examplesByName)) {
491
254
  out.examplesByName[name] = [...list];
492
255
  }
@@ -669,22 +432,18 @@ async function runAuditInner(opts: RunAuditOptions, startedAt: number): Promise<
669
432
 
670
433
  // 1. Discover transcripts across all selected CLIs.
671
434
  const allTranscripts: TranscriptMetadata[] = [];
672
- let discoveryErrors = 0;
673
435
  for (const cli of clis) {
674
436
  const adapter = ADAPTERS[cli];
675
437
  let list: TranscriptMetadata[];
676
438
  try {
677
439
  list = await adapter.listTranscripts({ projects: opts.projects, sinceMs });
678
440
  } catch {
679
- discoveryErrors++;
680
441
  continue; // adapter failures shouldn't kill the whole audit
681
442
  }
682
443
  allTranscripts.push(...list);
683
444
  }
684
445
 
685
- // 2. Scan each transcript (cache-aware), 8 at a time and within a memory
686
- // budget — see TRANSCRIPT_BYTE_BUDGET for why a file count is the wrong unit.
687
- const gate = new ByteGate(TRANSCRIPT_BYTE_BUDGET);
446
+ // 2. Scan each transcript (cache-aware), 8 in parallel.
688
447
  let skipped = 0;
689
448
  let errors = 0;
690
449
  const tasks = allTranscripts.map((meta) => async (): Promise<TranscriptAuditResult> => {
@@ -698,9 +457,6 @@ async function runAuditInner(opts: RunAuditOptions, startedAt: number): Promise<
698
457
  : undefined;
699
458
  cachedPrefix = found?.kind === "resume" ? found.result : null;
700
459
  }
701
- // Memory admission. Taken AFTER the cache check above, so a cache hit — the
702
- // common case on a warm machine — never waits for a slot it does not use.
703
- await gate.acquire(meta.sizeBytes);
704
460
  try {
705
461
  const scan = await scanOneTranscript(meta, resume);
706
462
  // A resumed scan produced hits for the TAIL only; the cached result holds
@@ -744,11 +500,6 @@ async function runAuditInner(opts: RunAuditOptions, startedAt: number): Promise<
744
500
  examplesByName: {},
745
501
  rangeByName: {},
746
502
  };
747
- } finally {
748
- // Released on every path, including the error one — a task that threw
749
- // still freed its memory, and holding its weight would shrink the budget
750
- // permanently over a long scan until nothing could be admitted at all.
751
- gate.release(meta.sizeBytes);
752
503
  }
753
504
  });
754
505
 
@@ -776,30 +527,8 @@ async function runAuditInner(opts: RunAuditOptions, startedAt: number): Promise<
776
527
  eventsScanned += t.eventsScanned ?? 0;
777
528
  }
778
529
 
779
- // Fold every sighting into the persistent record. This is the ONLY place the
780
- // leak record is written: `scanOneTranscript` runs 8-way concurrent and a
781
- // per-transcript write would be eight processes racing one file — the exact
782
- // shape that lost updates when it was measured (2 concurrent writers, 5/5
783
- // trials, the loser's entry gone permanently).
784
- //
785
- // `newFindingIds` is what a notice keys on: an id already in the record must
786
- // not alert again however many fresh sightings it accumulates, and a
787
- // credential seen for the first time must alert even though its rule has
788
- // fired a thousand times before.
789
- const coversAllHistory =
790
- opts.clis === undefined &&
791
- opts.projects === undefined &&
792
- opts.since === undefined &&
793
- discoveryErrors === 0 &&
794
- errors === 0 &&
795
- skipped === 0;
796
- const newFindingIds = persistLeaks(perTranscript, coversAllHistory);
797
- const leakIds = [...new Set(perTranscript.flatMap((t) => (t.leaks ?? []).map((leak) => leak.id)))];
798
-
799
530
  const auditResult: AuditResult = {
800
531
  version: 2,
801
- leakIds,
802
- newLeakIds: newFindingIds,
803
532
  scannedAt: new Date(startedAt).toISOString(),
804
533
  scope: {
805
534
  cli: clis,
@@ -7,8 +7,9 @@
7
7
  * - `machine_id` is what the api-server keys reports on. Regenerate it and the
8
8
  * server sees a brand-new machine, which burns a slot off the account's cap
9
9
  * on every logout and splits one box's history into two.
10
- * - `last_reported_at` is how far the last credential alert reached. Reset it
11
- * and the next report can re-cover old exposures as though they just happened.
10
+ * - `last_reported_at` is how far the last digest reached. Reset it and the
11
+ * next report re-covers months of history, and the user gets a digest of
12
+ * everything that ever happened as though it just did.
12
13
  *
13
14
  * That is why this is a separate file from `session.json` rather than two more
14
15
  * keys in it: signing out deletes the session, and neither of these may go with
@@ -21,7 +22,7 @@
21
22
  * `state/telemetry-id` is already a stable per-machine random id and would have
22
23
  * been free to reuse. It is deliberately not reused: that id is the anonymous
23
24
  * PostHog person, and sending it alongside a verified email address would link
24
- * the two the moment somebody adds email alerts. Opting into email
25
+ * the two the moment somebody turns emailed reports on. Opting into a digest
25
26
  * should not de-anonymise telemetry, so this feature gets its own id and the
26
27
  * two never meet.
27
28
  */
@@ -65,8 +66,8 @@ export function readMachineIdentity(home?: string): MachineIdentity | null {
65
66
  /**
66
67
  * Read the identity, creating it on first call.
67
68
  *
68
- * Only ever called from the email-reporting path, so a machine without a valid
69
- * email identity never gets an id at all — there is nothing to mint one for.
69
+ * Only ever called from the reporting path, so a machine that never opts into
70
+ * emailed reports never gets an id at all — there is nothing to mint one for.
70
71
  */
71
72
  export function ensureMachineIdentity(home?: string): MachineIdentity {
72
73
  const existing = readMachineIdentity(home);
@@ -80,13 +81,13 @@ export function ensureMachineIdentity(home?: string): MachineIdentity {
80
81
  }
81
82
 
82
83
  /**
83
- * Record how far the last delivered credential alert reached.
84
+ * Record how far the last DELIVERED digest reached.
84
85
  *
85
86
  * The value is the server's `next_window_from`, not the window this run
86
87
  * scanned. The server is authoritative because it knows which reports actually
87
88
  * produced an email — a report held by the cooldown, or one whose send failed,
88
89
  * must not advance the watermark or its findings are silently dropped from every
89
- * future alert.
90
+ * future digest.
90
91
  */
91
92
  export function recordReportWatermark(nextWindowFrom: string, home?: string): void {
92
93
  const current = ensureMachineIdentity(home);
@@ -104,7 +105,7 @@ export function deleteMachineIdentity(home?: string): void {
104
105
  /**
105
106
  * A display name for this machine — its hostname.
106
107
  *
107
- * Shown in the email alert so somebody with three boxes can tell which one is
108
+ * Shown in the digest so somebody with three boxes can tell which one is
108
109
  * misbehaving, which is the whole reason it is sent. Falls back to `undefined`
109
110
  * rather than a placeholder: the server keeps whatever label it already has when
110
111
  * one is omitted, so guessing here would overwrite a good name with a bad one.