failproofai 1.0.0-beta.5 → 1.0.0-beta.7

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 (368) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  26. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  33. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  39. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  47. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  50. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/index.html +1 -1
  53. package/.next/standalone/.next/server/app/index.rsc +15 -15
  54. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  56. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  57. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  58. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +3 -1
  61. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  64. package/.next/standalone/.next/server/app/policies/page.js +4 -2
  65. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  69. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  77. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  78. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  81. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  82. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  83. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  84. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  85. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  86. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  87. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  88. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  89. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  90. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
  97. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
  98. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
  102. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
  109. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
  110. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
  111. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  112. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c_qcgp._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kbc9bs._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
  125. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/_194yh9u._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
  135. package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  141. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
  143. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/standalone/.next/server/middleware.js +2 -2
  145. package/.next/standalone/.next/server/pages/404.html +1 -1
  146. package/.next/standalone/.next/server/pages/500.html +1 -1
  147. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
  149. package/.next/standalone/.next/static/chunks/0945ybz3fsvba.js +1 -0
  150. package/.next/standalone/.next/static/chunks/{0cr85wuc9gc9k.js → 0gr76ps8_zm26.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/{2cqk4a9s893oy.js → 0m-m7wgtxdoav.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/{1tp9dex23zs8y.js → 11l6oh9cyb35a.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/166f8qnh03jqc.js +6 -0
  154. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  155. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{2s9g1dsde_ts1.js → 2aej0vi1fm87m.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{1bkynffp3q1f7.js → 2bxld6-rym3ah.js} +22 -22
  158. package/.next/standalone/.next/static/chunks/{2v1rpzrh2i1wv.js → 2p2kz4aj1tooj.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  160. package/.next/standalone/.next/static/chunks/36pc3_8fpb6ic.js +1 -0
  161. package/.next/standalone/.next/static/chunks/{2k285z07qce47.js → 3t1wr_kx6brwc.js} +1 -1
  162. package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
  163. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  164. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  165. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  166. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  167. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  168. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  169. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  170. package/.next/standalone/app/audit/audit-styles.css +6 -149
  171. package/.next/standalone/app/settings/page.tsx +31 -0
  172. package/.next/standalone/app/settings/settings-client.tsx +488 -0
  173. package/.next/standalone/components/navbar.tsx +2 -0
  174. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  175. package/.next/standalone/lib/auth/auth-store.ts +4 -4
  176. package/.next/standalone/lib/codex-sessions.ts +2 -1
  177. package/.next/standalone/lib/dashboard-host.ts +101 -0
  178. package/.next/standalone/lib/install-check.ts +22 -7
  179. package/.next/standalone/lib/pi-sessions.ts +99 -14
  180. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  181. package/.next/standalone/lib/telemetry-id.ts +52 -1
  182. package/.next/standalone/lib/telemetry.ts +8 -4
  183. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  184. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  185. package/.next/standalone/node_modules/react/package.json +3 -3
  186. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  187. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  188. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  189. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  190. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  191. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  192. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  193. package/.next/standalone/package.json +7 -6
  194. package/.next/standalone/proxy.ts +89 -1
  195. package/.next/standalone/server.js +1 -1
  196. package/bin/failproofai.mjs +202 -86
  197. package/dist/cli.mjs +17803 -15346
  198. package/dist/worker.mjs +1620 -257
  199. package/lib/auth/api-server-client.ts +2 -3
  200. package/lib/auth/auth-store.ts +4 -4
  201. package/lib/codex-sessions.ts +2 -1
  202. package/lib/dashboard-host.ts +101 -0
  203. package/lib/install-check.ts +22 -7
  204. package/lib/pi-sessions.ts +99 -14
  205. package/lib/telemetry-enabled.ts +54 -0
  206. package/lib/telemetry-id.ts +52 -1
  207. package/lib/telemetry.ts +8 -4
  208. package/package.json +7 -6
  209. package/scripts/install-telemetry.mjs +8 -1
  210. package/scripts/launch.ts +25 -3
  211. package/scripts/parse-script-args.ts +17 -1
  212. package/scripts/prune-standalone.mjs +20 -0
  213. package/scripts/translate-docs/readme-translator.ts +163 -4
  214. package/scripts/validate-mdx.ts +153 -13
  215. package/src/audit/audit-lock.ts +285 -0
  216. package/src/audit/audit-schedule.ts +111 -0
  217. package/src/audit/cache.ts +18 -4
  218. package/src/audit/cli-adapters/goose.ts +2 -1
  219. package/src/audit/cli-adapters/hermes.ts +28 -10
  220. package/src/audit/cli.ts +191 -35
  221. package/src/audit/dashboard-cache.ts +2 -1
  222. package/src/hooks/builtin-policies.ts +100 -3
  223. package/src/hooks/cloud-connection.ts +160 -14
  224. package/src/hooks/cloud-enrollment-cli.ts +119 -21
  225. package/src/hooks/cloud-enrollment.ts +87 -17
  226. package/src/hooks/cloud-introspect.ts +147 -0
  227. package/src/hooks/cloud-managed-policies.ts +33 -2
  228. package/src/hooks/collector-config.ts +25 -43
  229. package/src/hooks/configure-wizard.ts +750 -318
  230. package/src/hooks/custom-hooks-loader.ts +125 -11
  231. package/src/hooks/daemon-client.ts +83 -17
  232. package/src/hooks/daemon-download.ts +54 -2
  233. package/src/hooks/daemon-service.ts +605 -29
  234. package/src/hooks/first-run-gate.ts +39 -0
  235. package/src/hooks/fp-config.ts +497 -0
  236. package/src/hooks/fp-home.ts +355 -0
  237. package/src/hooks/fp-reset.ts +332 -0
  238. package/src/hooks/handler.ts +42 -1
  239. package/src/hooks/hook-activity-store.ts +35 -20
  240. package/src/hooks/hook-logger.ts +2 -1
  241. package/src/hooks/hook-telemetry.ts +5 -1
  242. package/src/hooks/hooks-config.ts +4 -3
  243. package/src/hooks/loader-utils.ts +166 -5
  244. package/src/hooks/manager.ts +5 -4
  245. package/src/hooks/onboarding-attempt.ts +179 -0
  246. package/src/hooks/onboarding-lock.ts +119 -0
  247. package/src/hooks/policy-types.ts +0 -7
  248. package/src/hooks/session-pause-cli.ts +15 -13
  249. package/src/hooks/session-pause.ts +36 -4
  250. package/src/hooks/setup-state.ts +256 -0
  251. package/src/hooks/tui.ts +4 -4
  252. package/src/hooks/worker-server.ts +53 -1
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__094nop3._.js +0 -3
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  267. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  268. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  269. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1gak4pd._.js +0 -3
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  272. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_12c_2cz.js +0 -3
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  275. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  276. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  277. package/.next/standalone/.next/static/chunks/1bw4dfzflelcv.js +0 -1
  278. package/.next/standalone/.next/static/chunks/34sjdw8lv46e3.js +0 -6
  279. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  280. package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
  281. package/.next/standalone/Cargo.lock +0 -2222
  282. package/.next/standalone/Cargo.toml +0 -9
  283. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  284. package/.next/standalone/crates/PROTOCOL.md +0 -122
  285. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  286. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  287. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  288. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  289. package/.next/standalone/crates/failproofaid/src/main.rs +0 -639
  290. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  291. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  292. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  293. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  294. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  295. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -420
  296. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  297. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  298. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -487
  299. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  300. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  301. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  302. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  304. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  306. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  308. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  310. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  312. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -314
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  314. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  315. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  317. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  319. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  321. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -535
  322. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -390
  323. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  324. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  325. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -739
  326. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -394
  327. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -976
  328. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  329. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1020
  330. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  331. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1009
  332. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1130
  333. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -622
  334. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -855
  335. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  336. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1192
  337. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  338. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  339. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  340. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  341. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  342. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  343. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  344. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  345. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  346. package/.next/standalone/integration-suite/Dockerfile +0 -47
  347. package/.next/standalone/integration-suite/README.md +0 -129
  348. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  349. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  350. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  351. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  352. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  353. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  354. package/.next/standalone/integration-suite/report.js +0 -120
  355. package/.next/standalone/integration-suite/run.sh +0 -159
  356. package/.next/standalone/openclaw-plugin/index.js +0 -223
  357. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  358. package/.next/standalone/openclaw-plugin/package.json +0 -12
  359. package/.next/standalone/osv-scanner.toml +0 -17
  360. package/.next/standalone/pi-extension/index.ts +0 -524
  361. package/.next/standalone/pi-extension/package.json +0 -12
  362. package/.next/standalone/postcss.config.mjs +0 -8
  363. package/.next/standalone/readme-arch-hq.gif +0 -0
  364. package/.next/standalone/rust-toolchain.toml +0 -4
  365. package/src/auth/cli.ts +0 -371
  366. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_buildManifest.js +0 -0
  367. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_clientMiddlewareManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_ssgManifest.js +0 -0
@@ -1,535 +0,0 @@
1
- //! Durable hand-off between a source and the uploader.
2
- //!
3
- //! A source transforms records into events and pushes them here; this writes
4
- //! them as `<prefix>-<tag>-<run>-<seq>.jsonl` batches into the spool directory,
5
- //! where the watcher and sweeper pick them up. The agent's own files and
6
- //! databases are never handed to the uploader, so they are never touched.
7
- //!
8
- //! # Three properties that are load-bearing
9
- //!
10
- //! **Writes are atomic.** Every batch is written to `.tmp`, fsynced, then
11
- //! renamed into place. A reader therefore never observes a partial file — and
12
- //! `.tmp` is not `.jsonl`, so the watcher skips it without needing to know
13
- //! this. Combined with "flush before advancing the cursor", a crash costs at
14
- //! most a re-ship, never a lost event.
15
- //!
16
- //! **No single line may exceed the batch cap.** A line larger than one request
17
- //! could never be delivered: it would fail, be parked, be retried at the same
18
- //! size, and fail identically forever. So oversized events are first shrunk by
19
- //! truncating their long string fields, and dropped with a warning if they are
20
- //! still too big. Dropping is the honest outcome — the record remains in the
21
- //! agent's own store, so nothing is lost from the source, whereas parking it
22
- //! would quietly accumulate undeliverable files.
23
- //!
24
- //! **Truncation is deterministic.** The server dedups on a content hash, so
25
- //! the same input must always produce the same bytes. That rules out anything
26
- //! involving time, randomness or a sampled decision, and is why truncation
27
- //! marks the original length rather than, say, the moment it was cut.
28
-
29
- use std::path::{Path, PathBuf};
30
- use std::time::{SystemTime, UNIX_EPOCH};
31
-
32
- use serde_json::Value;
33
- use tokio::io::AsyncWriteExt;
34
-
35
- use crate::config::Redact;
36
-
37
- /// Longest single string field kept intact. One oversized tool output must not
38
- /// be able to push a whole batch past the body cap.
39
- const MAX_FIELD_BYTES: usize = 1024 * 1024;
40
-
41
- /// Floor for the batch cap. A cap below this would roll a new file per event.
42
- const MIN_BATCH_BYTES: u64 = 64 * 1024;
43
-
44
- /// Default batch cap, matching the ingest endpoint's body limit.
45
- pub const DEFAULT_MAX_BATCH_BYTES: u64 = 8 * 1024 * 1024;
46
-
47
- /// Accumulates events and writes them out as size-bounded batches.
48
- pub struct SpoolWriter {
49
- dir: PathBuf,
50
- max_batch_bytes: u64,
51
- prefix: String,
52
- tag: String,
53
- run_id: u128,
54
- seq: u64,
55
- buf: String,
56
- buf_bytes: u64,
57
- redact: Redact,
58
- redacted: u64,
59
- /// The machine this daemon runs on, stamped on every event so the server
60
- /// can tell one machine from another. Without it, events carry only
61
- /// `agent_id` — a per-project/per-harness identity — and the fleet views
62
- /// mistake every project for a separate machine. `None` on a machine that
63
- /// was never enrolled with a machine id (older config); such events simply
64
- /// carry no machine and are excluded from machine-level counts rather than
65
- /// guessed into one.
66
- machine_id: Option<String>,
67
- }
68
-
69
- impl SpoolWriter {
70
- /// `prefix` is the source kind (`claude`, `hooks`, …) and `tag` a short
71
- /// session identifier; together they make a derived batch recognisable in
72
- /// a shared spool directory.
73
- ///
74
- /// `run_id` is nanoseconds since the epoch, which distinguishes files
75
- /// across restarts. Without it a fresh run would reuse `seq` 0 and could
76
- /// clobber a batch the previous run had written but not yet uploaded.
77
- pub fn new(dir: PathBuf, max_batch_bytes: u64, prefix: &str, tag: &str) -> Self {
78
- let run_id = SystemTime::now()
79
- .duration_since(UNIX_EPOCH)
80
- .map(|d| d.as_nanos())
81
- .unwrap_or(0);
82
- SpoolWriter {
83
- dir,
84
- max_batch_bytes: max_batch_bytes.max(MIN_BATCH_BYTES),
85
- prefix: sanitize_tag(prefix),
86
- tag: sanitize_tag(tag),
87
- run_id,
88
- seq: 0,
89
- buf: String::new(),
90
- buf_bytes: 0,
91
- // Default ON. A source that forgets to opt in still gets scrubbed,
92
- // which is the safe direction for the mistake to fall.
93
- redact: Redact::Minimal,
94
- redacted: 0,
95
- machine_id: None,
96
- }
97
- }
98
-
99
- /// Override the redaction mode. Only `Redact::Off` changes anything —
100
- /// scrubbing is on by default so a new source cannot ship secrets by
101
- /// omission.
102
- pub fn with_redact(mut self, mode: Redact) -> Self {
103
- self.redact = mode;
104
- self
105
- }
106
-
107
- /// Set the machine id stamped on every event. An empty string is treated as
108
- /// absent, so a misconfigured blank id never becomes a machine of its own.
109
- pub fn with_machine_id(mut self, id: Option<String>) -> Self {
110
- self.machine_id = id.filter(|s| !s.is_empty());
111
- self
112
- }
113
-
114
- /// How many string leaves have been scrubbed. Reported without ever
115
- /// logging what was removed.
116
- pub fn redacted_count(&self) -> u64 {
117
- self.redacted
118
- }
119
-
120
- /// Events buffered but not yet written. Used by callers that must flush
121
- /// before advancing a cursor.
122
- pub fn pending_bytes(&self) -> u64 {
123
- self.buf_bytes
124
- }
125
-
126
- /// Append one event, rolling to a new batch first if it would not fit.
127
- pub async fn push(&mut self, mut event: Value) -> std::io::Result<()> {
128
- // The single choke point every event passes through, which is why
129
- // redaction happens here rather than in each transform: a source
130
- // cannot forget it, and a new source inherits it for free. It runs
131
- // BEFORE serialization, so what gets hashed, spooled and uploaded is
132
- // the scrubbed form — there is no window where the raw value exists on
133
- // disk.
134
- self.redacted += crate::redact::scrub_value(&mut event, self.redact) as u64;
135
-
136
- // Stamp the machine id here, at the one point every event passes
137
- // through — a source cannot forget it and a new source inherits it. Set
138
- // only when absent, so an event that already names its machine (a
139
- // re-shipped batch, say) is never overwritten.
140
- if let Some(id) = &self.machine_id
141
- && let Some(obj) = event.as_object_mut()
142
- {
143
- obj.entry("machine_id")
144
- .or_insert_with(|| Value::String(id.clone()));
145
- }
146
-
147
- let mut line = serialize(&event)?;
148
-
149
- if line.len() as u64 > self.max_batch_bytes {
150
- truncate_strings(&mut event, MAX_FIELD_BYTES);
151
- line = serialize(&event)?;
152
- }
153
-
154
- if line.len() as u64 > self.max_batch_bytes {
155
- // Still over cap after truncation — thousands of small fields, say.
156
- // Shipping it would produce a batch the server refuses, which then
157
- // parks and retries forever at the same size.
158
- let event_type = event.get("type").and_then(|v| v.as_str()).unwrap_or("?");
159
- tracing::warn!(
160
- source = %self.prefix,
161
- bytes = line.len(),
162
- max = self.max_batch_bytes,
163
- event_type,
164
- "dropping an event that exceeds the batch cap even after truncation; \
165
- it remains in the agent's own store"
166
- );
167
- return Ok(());
168
- }
169
-
170
- let needed = line.len() as u64 + 1; // +1 for the newline
171
- if !self.buf.is_empty() && self.buf_bytes + needed > self.max_batch_bytes {
172
- self.flush().await?;
173
- }
174
- self.buf.push_str(&line);
175
- self.buf.push('\n');
176
- self.buf_bytes += needed;
177
- Ok(())
178
- }
179
-
180
- /// Durably write the buffered events as one batch. No-op when empty.
181
- ///
182
- /// Callers MUST await this before advancing any persisted cursor. The
183
- /// ordering is the whole durability argument: flush-then-advance costs a
184
- /// re-ship on crash (which the server dedups), advance-then-flush loses
185
- /// the events outright.
186
- pub async fn flush(&mut self) -> std::io::Result<()> {
187
- if self.buf.is_empty() {
188
- return Ok(());
189
- }
190
- tokio::fs::create_dir_all(&self.dir).await?;
191
-
192
- let stem = format!("{}-{}-{}-{}", self.prefix, self.tag, self.run_id, self.seq);
193
- let tmp = self.dir.join(format!("{stem}.tmp"));
194
- let final_path = self.dir.join(format!("{stem}.jsonl"));
195
-
196
- let mut f = tokio::fs::File::create(&tmp).await?;
197
- f.write_all(self.buf.as_bytes()).await?;
198
- // fsync before rename: a rename is atomic with respect to readers, but
199
- // it does not guarantee the CONTENT reached disk. Without this, a
200
- // power loss can leave a correctly-named, zero-length batch — which
201
- // the uploader would happily deliver as an empty file.
202
- f.sync_all().await?;
203
- drop(f);
204
- tokio::fs::rename(&tmp, &final_path).await?;
205
-
206
- self.seq += 1;
207
- self.buf.clear();
208
- self.buf_bytes = 0;
209
- Ok(())
210
- }
211
- }
212
-
213
- fn serialize(event: &Value) -> std::io::Result<String> {
214
- serde_json::to_string(event).map_err(std::io::Error::other)
215
- }
216
-
217
- /// Keep a tag filename-safe and bounded.
218
- ///
219
- /// The charset matters beyond hygiene: batch filenames encode retry state as
220
- /// suffixes, so a tag containing a `.` could make a fresh batch parse as one
221
- /// that had already been retried.
222
- fn sanitize_tag(tag: &str) -> String {
223
- let cleaned: String = tag
224
- .chars()
225
- .filter(|c| c.is_ascii_alphanumeric() || *c == '-')
226
- .take(16)
227
- .collect();
228
- if cleaned.is_empty() {
229
- "session".to_string()
230
- } else {
231
- cleaned
232
- }
233
- }
234
-
235
- /// Recursively truncate long string leaves to `cap` bytes at a char boundary.
236
- ///
237
- /// Deterministic by construction: the marker records the ORIGINAL length, so
238
- /// the same input always yields identical bytes. The truncated payload feeds
239
- /// the server's dedup hash, so a marker containing a timestamp or a counter
240
- /// would make every re-read of the same event look like a new one.
241
- fn truncate_strings(v: &mut Value, cap: usize) {
242
- match v {
243
- Value::String(s) if s.len() > cap => {
244
- let orig = s.len();
245
- let mut end = cap;
246
- while end > 0 && !s.is_char_boundary(end) {
247
- end -= 1;
248
- }
249
- let mut truncated = s[..end].to_string();
250
- truncated.push_str(&format!("…[truncated {orig} bytes]"));
251
- *s = truncated;
252
- }
253
- Value::Array(a) => a.iter_mut().for_each(|e| truncate_strings(e, cap)),
254
- Value::Object(o) => o.values_mut().for_each(|e| truncate_strings(e, cap)),
255
- _ => {}
256
- }
257
- }
258
-
259
- /// True if `path` is a batch the uploader should pick up.
260
- ///
261
- /// `.tmp` files are excluded by the extension check rather than by a separate
262
- /// rule, which is exactly why the atomic write works: a batch is invisible
263
- /// until the instant it is renamed.
264
- pub fn is_batch_file(path: &Path) -> bool {
265
- path.extension().and_then(|e| e.to_str()) == Some("jsonl")
266
- }
267
-
268
- #[cfg(test)]
269
- mod tests {
270
- use super::*;
271
- use serde_json::json;
272
-
273
- fn tmpdir(name: &str) -> PathBuf {
274
- let d = std::env::temp_dir().join(format!(
275
- "fpai-spool-{}-{}-{}",
276
- name,
277
- std::process::id(),
278
- SystemTime::now()
279
- .duration_since(UNIX_EPOCH)
280
- .unwrap()
281
- .as_nanos()
282
- ));
283
- std::fs::create_dir_all(&d).unwrap();
284
- d
285
- }
286
-
287
- fn batches(dir: &Path) -> Vec<String> {
288
- let mut v: Vec<String> = std::fs::read_dir(dir)
289
- .unwrap()
290
- .filter_map(|e| e.ok())
291
- .map(|e| e.file_name().to_string_lossy().to_string())
292
- .filter(|n| n.ends_with(".jsonl"))
293
- .collect();
294
- v.sort();
295
- v
296
- }
297
-
298
- #[tokio::test]
299
- async fn writes_one_batch_and_leaves_no_tmp_behind() {
300
- let dir = tmpdir("basic");
301
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "hooks", "sess1");
302
- w.push(json!({"type": "hook_triggered", "n": 1}))
303
- .await
304
- .unwrap();
305
- w.push(json!({"type": "hook_completed", "n": 1}))
306
- .await
307
- .unwrap();
308
- w.flush().await.unwrap();
309
-
310
- let files = batches(&dir);
311
- assert_eq!(files.len(), 1, "expected one batch, got {files:?}");
312
- assert!(files[0].starts_with("hooks-sess1-"));
313
-
314
- let leftover_tmp = std::fs::read_dir(&dir)
315
- .unwrap()
316
- .filter_map(|e| e.ok())
317
- .any(|e| e.file_name().to_string_lossy().ends_with(".tmp"));
318
- assert!(!leftover_tmp, "a .tmp file survived the rename");
319
-
320
- let body = std::fs::read_to_string(dir.join(&files[0])).unwrap();
321
- assert_eq!(body.lines().count(), 2);
322
- std::fs::remove_dir_all(&dir).ok();
323
- }
324
-
325
- #[tokio::test]
326
- async fn rolls_to_a_new_batch_at_the_size_cap() {
327
- let dir = tmpdir("roll");
328
- let mut w = SpoolWriter::new(dir.clone(), MIN_BATCH_BYTES, "claude", "s");
329
- for i in 0..100 {
330
- w.push(json!({"type": "tool_use", "n": i, "pad": "x".repeat(2000)}))
331
- .await
332
- .unwrap();
333
- }
334
- w.flush().await.unwrap();
335
- assert!(
336
- batches(&dir).len() > 1,
337
- "200KB of events under a 64KB cap must roll into several batches"
338
- );
339
- std::fs::remove_dir_all(&dir).ok();
340
- }
341
-
342
- #[tokio::test]
343
- async fn no_written_line_ever_exceeds_the_batch_cap() {
344
- // The invariant the uploader depends on: a line bigger than one
345
- // request can never be delivered, so it must never reach the spool.
346
- let dir = tmpdir("cap");
347
- let mut w = SpoolWriter::new(dir.clone(), MIN_BATCH_BYTES, "claude", "s");
348
- w.push(json!({"type": "tool_result", "output": "y".repeat(3 * 1024 * 1024)}))
349
- .await
350
- .unwrap();
351
- w.flush().await.unwrap();
352
-
353
- for f in batches(&dir) {
354
- let body = std::fs::read_to_string(dir.join(&f)).unwrap();
355
- for line in body.lines() {
356
- assert!(
357
- line.len() as u64 <= MIN_BATCH_BYTES,
358
- "a {}-byte line was written under a {MIN_BATCH_BYTES}-byte cap",
359
- line.len()
360
- );
361
- }
362
- }
363
- std::fs::remove_dir_all(&dir).ok();
364
- }
365
-
366
- #[tokio::test]
367
- async fn drops_an_event_that_is_still_over_cap_after_truncation() {
368
- // Many small fields: no single field is big enough for truncation to
369
- // shrink, so the whole line stays over cap and must be dropped rather
370
- // than written into a batch that could never be delivered.
371
- let dir = tmpdir("drop");
372
- let mut w = SpoolWriter::new(dir.clone(), MIN_BATCH_BYTES, "claude", "s");
373
- let mut obj = serde_json::Map::new();
374
- obj.insert("type".into(), json!("tool_result"));
375
- for i in 0..2000 {
376
- obj.insert(format!("f{i}"), json!("x".repeat(64)));
377
- }
378
- w.push(Value::Object(obj)).await.unwrap();
379
- w.flush().await.unwrap();
380
-
381
- assert!(
382
- batches(&dir).is_empty(),
383
- "an undeliverable event must be dropped, never spooled"
384
- );
385
- std::fs::remove_dir_all(&dir).ok();
386
- }
387
-
388
- #[test]
389
- fn truncation_is_deterministic() {
390
- // Non-negotiable: the server dedups on a content hash, so a re-read of
391
- // the same source bytes must produce identical output.
392
- let big = "a".repeat(3 * 1024 * 1024);
393
- let mut a = json!({"output": big.clone()});
394
- let mut b = json!({"output": big});
395
- truncate_strings(&mut a, MAX_FIELD_BYTES);
396
- truncate_strings(&mut b, MAX_FIELD_BYTES);
397
- assert_eq!(a, b);
398
- let s = a["output"].as_str().unwrap();
399
- assert!(s.contains("truncated"));
400
- assert!(s.len() < 2 * 1024 * 1024);
401
- }
402
-
403
- #[test]
404
- fn truncation_cuts_on_a_char_boundary() {
405
- // Cutting mid-codepoint would panic on the slice, or emit invalid
406
- // UTF-8 that the server rejects for the whole batch.
407
- let mut v = json!({ "output": "é".repeat(200) });
408
- truncate_strings(&mut v, 101); // lands mid-codepoint: 'é' is 2 bytes
409
- assert!(v["output"].as_str().unwrap().contains("truncated"));
410
- }
411
-
412
- #[test]
413
- fn a_tag_cannot_smuggle_retry_state_into_a_filename() {
414
- // Retry state is encoded as filename suffixes, so a tag containing a
415
- // dot could make a brand-new batch parse as an already-retried one.
416
- assert_eq!(sanitize_tag("sess.a1.c404"), "sessa1c404");
417
- assert_eq!(sanitize_tag("../../etc/passwd"), "etcpasswd");
418
- assert_eq!(sanitize_tag(""), "session");
419
- assert_eq!(sanitize_tag("!!!"), "session");
420
- assert_eq!(sanitize_tag(&"a".repeat(64)).len(), 16);
421
- }
422
-
423
- #[tokio::test]
424
- async fn flushing_an_empty_writer_creates_nothing() {
425
- let dir = tmpdir("empty");
426
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "hooks", "s");
427
- w.flush().await.unwrap();
428
- assert!(batches(&dir).is_empty());
429
- std::fs::remove_dir_all(&dir).ok();
430
- }
431
-
432
- #[tokio::test]
433
- async fn secrets_are_scrubbed_before_anything_touches_disk() {
434
- // The choke-point guarantee: a source that never thinks about
435
- // redaction still cannot write a credential into the spool.
436
- let dir = tmpdir("redact");
437
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "claude", "s");
438
- w.push(json!({
439
- "type": "tool_use",
440
- "input": {"command": "curl -H 'Authorization: Bearer ghp_abcdefghijklmnopqrstuvwxyz0123'"}
441
- }))
442
- .await
443
- .unwrap();
444
- w.flush().await.unwrap();
445
-
446
- let name = batches(&dir).remove(0);
447
- let body = std::fs::read_to_string(dir.join(&name)).unwrap();
448
- assert!(
449
- !body.contains("ghp_abcdefghij"),
450
- "a credential reached disk: {body}"
451
- );
452
- assert!(
453
- body.contains("[redacted:"),
454
- "redaction marker missing: {body}"
455
- );
456
- assert!(w.redacted_count() >= 1);
457
- std::fs::remove_dir_all(&dir).ok();
458
- }
459
-
460
- #[tokio::test]
461
- async fn machine_id_is_stamped_on_every_event_when_set() {
462
- // Without this the server can only group by agent_id — a per-project
463
- // identity — so one machine's many projects each read as a machine.
464
- let dir = tmpdir("machine-id");
465
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "claude", "s")
466
- .with_machine_id(Some("beta-test".into()));
467
- w.push(json!({"type": "agent_start", "agent_id": "claude-foo"}))
468
- .await
469
- .unwrap();
470
- w.flush().await.unwrap();
471
- let name = batches(&dir).remove(0);
472
- let body = std::fs::read_to_string(dir.join(&name)).unwrap();
473
- assert!(body.contains(r#""machine_id":"beta-test""#), "got {body}");
474
- std::fs::remove_dir_all(&dir).ok();
475
- }
476
-
477
- #[tokio::test]
478
- async fn an_absent_machine_id_stamps_nothing() {
479
- // A machine that was never given an id must not invent one — the server
480
- // excludes machine-less events rather than bucket them.
481
- let dir = tmpdir("no-machine-id");
482
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "claude", "s");
483
- w.push(json!({"type": "agent_start"})).await.unwrap();
484
- w.flush().await.unwrap();
485
- let name = batches(&dir).remove(0);
486
- assert!(
487
- !std::fs::read_to_string(dir.join(&name))
488
- .unwrap()
489
- .contains("machine_id")
490
- );
491
- std::fs::remove_dir_all(&dir).ok();
492
- }
493
-
494
- #[tokio::test]
495
- async fn an_empty_machine_id_is_treated_as_absent() {
496
- let dir = tmpdir("blank-machine-id");
497
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "claude", "s")
498
- .with_machine_id(Some(String::new()));
499
- w.push(json!({"type": "agent_start"})).await.unwrap();
500
- w.flush().await.unwrap();
501
- let name = batches(&dir).remove(0);
502
- assert!(
503
- !std::fs::read_to_string(dir.join(&name))
504
- .unwrap()
505
- .contains("machine_id")
506
- );
507
- std::fs::remove_dir_all(&dir).ok();
508
- }
509
-
510
- #[tokio::test]
511
- async fn redaction_can_be_turned_off() {
512
- let dir = tmpdir("redact-off");
513
- let mut w = SpoolWriter::new(dir.clone(), DEFAULT_MAX_BATCH_BYTES, "claude", "s")
514
- .with_redact(Redact::Off);
515
- w.push(json!({"output": "ghp_abcdefghijklmnopqrstuvwxyz0123"}))
516
- .await
517
- .unwrap();
518
- w.flush().await.unwrap();
519
- let name = batches(&dir).remove(0);
520
- assert!(
521
- std::fs::read_to_string(dir.join(&name))
522
- .unwrap()
523
- .contains("ghp_abcdefghij")
524
- );
525
- std::fs::remove_dir_all(&dir).ok();
526
- }
527
-
528
- #[test]
529
- fn only_jsonl_is_a_batch() {
530
- assert!(is_batch_file(Path::new("/s/hooks-a-1-0.jsonl")));
531
- // The atomic-write contract depends on this exclusion.
532
- assert!(!is_batch_file(Path::new("/s/hooks-a-1-0.tmp")));
533
- assert!(!is_batch_file(Path::new("/s/hooks-a-1-0.jsonl.poison")));
534
- }
535
- }