failproofai 1.0.0-beta.2 → 1.0.0-beta.21

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 (381) 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 +6 -6
  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/build-manifest.json +3 -3
  8. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  10. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  15. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  16. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  20. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  22. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  25. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  27. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  28. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  31. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  32. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  33. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  35. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -1
  39. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  47. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  49. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  51. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  52. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  53. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/index.html +1 -1
  56. package/.next/standalone/.next/server/app/index.rsc +15 -15
  57. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  59. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  60. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  61. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  63. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/page.js +3 -1
  65. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  68. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
  69. package/.next/standalone/.next/server/app/policies/page.js +5 -3
  70. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  73. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  74. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  75. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  79. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  80. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  81. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  84. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  85. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  86. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  89. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  90. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  91. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  92. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  93. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  94. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  95. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  96. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  97. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  98. package/.next/standalone/.next/server/chunks/[externals]__0h9_83k._.js +3 -0
  99. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__00ni9za._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__0effm9m._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0ix8gm7._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ji11a-._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0ojwjxl._.js} +2 -2
  112. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  114. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
  116. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  123. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
  124. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
  125. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wfhrp._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0_1tiya._.js} +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
  132. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t281d4._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17itmx-._.js +5 -0
  134. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1n2vcv4._.js} +2 -2
  135. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tlvv9a._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  139. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/_0vo2qyt._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/_0xej-0e._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/_10399ge._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/_149k9oy._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/ssr/_1_hwp06._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/ssr/_1ctse6u._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/ssr/_1mnhi_r._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/ssr/_1ror7k9._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/_1sdgprl._.js +5 -0
  153. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  154. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  155. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  156. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  157. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
  158. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
  160. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  161. package/.next/standalone/.next/server/middleware.js +2 -2
  162. package/.next/standalone/.next/server/pages/404.html +1 -1
  163. package/.next/standalone/.next/server/pages/500.html +1 -1
  164. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
  166. package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
  167. package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
  168. package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 113g8wtal6_c4.js} +22 -22
  170. package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
  171. package/.next/standalone/.next/static/chunks/18wqn0_3mzylf.js +1 -0
  172. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  173. package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 1xq1m8h14tnlj.js} +1 -1
  174. package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 1y3vwogqb88dx.js} +1 -1
  175. package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 1zmh-b0ulrqu7.js} +1 -1
  176. package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
  177. package/.next/standalone/.next/static/chunks/299lhw60e8gr6.js +1 -0
  178. package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 2adbpdaxp5ygq.js} +1 -1
  179. package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 2kbqcw-6swmav.js} +1 -1
  180. package/.next/standalone/.next/static/chunks/2n9n5m0963pdg.js +6 -0
  181. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  182. package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 30nrrpwymht5m.js} +1 -1
  183. package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
  184. package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
  185. package/.next/standalone/SECURITY.md +8 -2
  186. package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
  187. package/.next/standalone/app/actions/get-hooks-config.ts +2 -2
  188. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  189. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  190. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  191. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  192. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  193. package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -3
  194. package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
  195. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  196. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  197. package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -1
  198. package/.next/standalone/app/audit/_components/empty-state.tsx +0 -1
  199. package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +1 -1
  200. package/.next/standalone/app/audit/_components/quirks-section.tsx +0 -1
  201. package/.next/standalone/app/audit/_components/report-footer.tsx +0 -2
  202. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  203. package/.next/standalone/app/audit/_components/strengths-section.tsx +0 -1
  204. package/.next/standalone/app/audit/audit-styles.css +6 -149
  205. package/.next/standalone/app/components/pause-notices.tsx +111 -0
  206. package/.next/standalone/app/components/raw-log-viewer.tsx +1 -1
  207. package/.next/standalone/app/components/toast.tsx +1 -1
  208. package/.next/standalone/app/policies/hooks-client.tsx +84 -8
  209. package/.next/standalone/app/settings/page.tsx +31 -0
  210. package/.next/standalone/app/settings/settings-client.tsx +487 -0
  211. package/.next/standalone/components/navbar.tsx +2 -0
  212. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  213. package/.next/standalone/lib/auth/auth-store.ts +4 -5
  214. package/.next/standalone/lib/codex-sessions.ts +2 -1
  215. package/.next/standalone/lib/dashboard-host.ts +101 -0
  216. package/.next/standalone/lib/install-check.ts +22 -7
  217. package/.next/standalone/lib/pi-sessions.ts +99 -14
  218. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  219. package/.next/standalone/lib/telemetry-id.ts +52 -1
  220. package/.next/standalone/lib/telemetry.ts +8 -4
  221. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  222. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  223. package/.next/standalone/node_modules/react/package.json +3 -3
  224. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  225. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  226. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  227. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  228. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  229. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  230. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  231. package/.next/standalone/package.json +23 -12
  232. package/.next/standalone/proxy.ts +111 -1
  233. package/.next/standalone/server.js +1 -1
  234. package/bin/failproofai-worker.mjs +1 -1
  235. package/bin/failproofai.mjs +1041 -81
  236. package/bin/failproofaid-shim.mjs +4 -4
  237. package/dist/cli.mjs +18513 -10474
  238. package/dist/worker.mjs +1223 -299
  239. package/lib/auth/api-server-client.ts +2 -3
  240. package/lib/auth/auth-store.ts +4 -5
  241. package/lib/codex-sessions.ts +2 -1
  242. package/lib/dashboard-host.ts +101 -0
  243. package/lib/install-check.ts +22 -7
  244. package/lib/pi-sessions.ts +99 -14
  245. package/lib/telemetry-enabled.ts +54 -0
  246. package/lib/telemetry-id.ts +52 -1
  247. package/lib/telemetry.ts +8 -4
  248. package/package.json +23 -12
  249. package/scripts/build-daemon-packages.mjs +247 -0
  250. package/scripts/daemon-platforms.mjs +42 -0
  251. package/scripts/dev-hook.mjs +46 -5
  252. package/scripts/install-telemetry.mjs +8 -1
  253. package/scripts/launch.ts +25 -3
  254. package/scripts/parse-script-args.ts +27 -1
  255. package/scripts/prune-standalone.mjs +26 -0
  256. package/scripts/publish-aliases.mjs +62 -41
  257. package/scripts/repro-npm-install.sh +155 -0
  258. package/scripts/translate-docs/cache.ts +1 -1
  259. package/scripts/translate-docs/cli.ts +2 -2
  260. package/scripts/translate-docs/mintlify-nav.ts +1 -1
  261. package/scripts/translate-docs/readme-translator.ts +163 -4
  262. package/scripts/validate-mdx.ts +153 -13
  263. package/src/audit/audit-lock.ts +285 -0
  264. package/src/audit/audit-schedule.ts +111 -0
  265. package/src/audit/cache.ts +18 -5
  266. package/src/audit/cli-adapters/goose.ts +2 -1
  267. package/src/audit/cli-adapters/hermes.ts +28 -10
  268. package/src/audit/cli.ts +191 -35
  269. package/src/audit/dashboard-cache.ts +2 -3
  270. package/src/audit/report.ts +0 -1
  271. package/src/hooks/backfill-cli.ts +247 -0
  272. package/src/hooks/builtin-policies.ts +391 -3
  273. package/src/hooks/cloud-connection.ts +354 -0
  274. package/src/hooks/cloud-enrollment-cli.ts +439 -0
  275. package/src/hooks/cloud-enrollment.ts +284 -0
  276. package/src/hooks/cloud-introspect.ts +147 -0
  277. package/src/hooks/cloud-managed-policies.ts +206 -0
  278. package/src/hooks/collector-config.ts +218 -0
  279. package/src/hooks/configure-wizard.ts +1211 -233
  280. package/src/hooks/custom-hooks-loader.ts +243 -37
  281. package/src/hooks/daemon-client.ts +136 -33
  282. package/src/hooks/daemon-download.ts +264 -31
  283. package/src/hooks/daemon-service.ts +1124 -76
  284. package/src/hooks/first-run-gate.ts +50 -0
  285. package/src/hooks/flush-cli.ts +180 -0
  286. package/src/hooks/fp-config.ts +835 -0
  287. package/src/hooks/fp-home.ts +714 -0
  288. package/src/hooks/fp-reset.ts +1363 -0
  289. package/src/hooks/handler.ts +185 -6
  290. package/src/hooks/harness-cli.ts +386 -0
  291. package/src/hooks/hook-activity-store.ts +171 -24
  292. package/src/hooks/hook-logger.ts +2 -2
  293. package/src/hooks/hook-telemetry.ts +5 -1
  294. package/src/hooks/hooks-config.ts +4 -3
  295. package/src/hooks/integrations.ts +0 -1
  296. package/src/hooks/loader-utils.ts +172 -9
  297. package/src/hooks/manager.ts +41 -5
  298. package/src/hooks/migrations.ts +450 -0
  299. package/src/hooks/onboarding-attempt.ts +195 -0
  300. package/src/hooks/onboarding-lock.ts +118 -0
  301. package/src/hooks/policy-types.ts +28 -0
  302. package/src/hooks/session-pause-cli.ts +159 -0
  303. package/src/hooks/session-pause.ts +267 -0
  304. package/src/hooks/setup-state.ts +256 -0
  305. package/src/hooks/tui.ts +209 -9
  306. package/src/hooks/uninstall-cli.ts +418 -0
  307. package/src/hooks/worker-server.ts +134 -15
  308. package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
  309. package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
  310. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
  311. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
  312. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  313. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  314. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  317. package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
  318. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
  319. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  320. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  321. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  322. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0nb0syg._.js +0 -3
  323. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  324. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  325. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
  326. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  327. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
  328. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +0 -3
  329. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  330. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  331. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0oy_-36.js +0 -3
  332. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  333. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  334. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  335. package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
  336. package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
  337. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
  338. package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
  339. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  340. package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
  341. package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
  342. package/.next/standalone/Cargo.lock +0 -428
  343. package/.next/standalone/Cargo.toml +0 -9
  344. package/.next/standalone/crates/PROTOCOL.md +0 -122
  345. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
  346. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  347. package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
  348. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
  349. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  350. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  351. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  352. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  353. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  354. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  355. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  356. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  357. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  358. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  359. package/.next/standalone/integration-suite/Dockerfile +0 -47
  360. package/.next/standalone/integration-suite/README.md +0 -129
  361. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  362. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  363. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  364. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  365. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  366. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  367. package/.next/standalone/integration-suite/report.js +0 -120
  368. package/.next/standalone/integration-suite/run.sh +0 -159
  369. package/.next/standalone/openclaw-plugin/index.js +0 -223
  370. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  371. package/.next/standalone/openclaw-plugin/package.json +0 -12
  372. package/.next/standalone/osv-scanner.toml +0 -17
  373. package/.next/standalone/pi-extension/index.ts +0 -524
  374. package/.next/standalone/pi-extension/package.json +0 -12
  375. package/.next/standalone/postcss.config.mjs +0 -8
  376. package/.next/standalone/readme-arch-hq.gif +0 -0
  377. package/.next/standalone/rust-toolchain.toml +0 -4
  378. package/src/auth/cli.ts +0 -371
  379. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_buildManifest.js +0 -0
  380. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_clientMiddlewareManifest.js +0 -0
  381. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_ssgManifest.js +0 -0
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Low-level HTTP client for the FailproofAI api-server's /v0/auth/* endpoints.
3
3
  *
4
- * Shared by both the CLI (failproofai auth ...) and the dashboard's Next.js
5
- * API route proxies. Has no filesystem access — token persistence lives in
6
- * `./auth-store.ts`.
4
+ * Used by the dashboard's Next.js API route proxies. Has no filesystem access
5
+ * — token persistence lives in `./auth-store.ts`.
7
6
  *
8
7
  * The base URL is resolved from FAILPROOF_API_URL (preferred) or the legacy
9
8
  * FAILPROOFAI_API_URL, falling back to the hosted api-server. Local-dev
@@ -2,16 +2,15 @@
2
2
  * Persistence layer for the FailproofAI auth.json file.
3
3
  *
4
4
  * Tokens live at ~/.failproofai/auth.json with mode 0600. The dashboard's
5
- * Next.js API routes and the CLI both read/write through here so the user's
6
- * session survives across `failproofai` (dashboard) and `failproofai auth`
7
- * (CLI) invocations.
5
+ * Next.js API routes read and write through here, so a session survives across
6
+ * dashboard runs.
8
7
  */
9
8
 
10
9
  import { existsSync, readFileSync, rmSync } from "node:fs";
11
- import { homedir } from "node:os";
12
10
  import { join } from "node:path";
13
11
 
14
12
  import { writeJsonAtomically } from "../atomic-write";
13
+ import { failproofaiHome } from "../../src/hooks/fp-home";
15
14
  import {
16
15
  AuthApiError,
17
16
  decodeJwt,
@@ -31,7 +30,7 @@ export interface StoredAuth {
31
30
  export function getAuthDir(): string {
32
31
  const override = process.env.FAILPROOFAI_AUTH_DIR;
33
32
  if (override) return override;
34
- return join(homedir(), ".failproofai");
33
+ return failproofaiHome();
35
34
  }
36
35
 
37
36
  export function getAuthFilePath(): string {
@@ -33,10 +33,11 @@ import {
33
33
  type LogSource,
34
34
  } from "./log-entries";
35
35
  import { formatDuration } from "./format-duration";
36
+ import { codexSessionPathsFile } from "../src/hooks/fp-home";
36
37
 
37
38
  // ── Transcript discovery ──
38
39
 
39
- const CACHE_PATH = join(homedir(), ".failproofai", "cache", "codex-session-paths.json");
40
+ const CACHE_PATH = codexSessionPathsFile();
40
41
 
41
42
  function readCache(): Record<string, string> {
42
43
  try {
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Where the local dashboard binds, and which `Host` headers it will answer to.
3
+ *
4
+ * Shared by `scripts/launch.ts` (which chooses the bind address) and `proxy.ts`
5
+ * (which enforces it per request), because the two drifting apart is exactly how
6
+ * a lockdown quietly stops working.
7
+ *
8
+ * ## Why this exists
9
+ *
10
+ * The dashboard is a WRITE surface for this machine's security configuration —
11
+ * it can toggle policies and uninstall failproofai's hooks from every agent CLI
12
+ * — and it has no authentication of any kind. It used to bind `0.0.0.0`
13
+ * unconditionally, so every peer on the network could do all of that.
14
+ *
15
+ * Binding loopback is necessary but NOT sufficient. A malicious page can still
16
+ * reach `http://localhost:8020` from the victim's own browser, and DNS
17
+ * rebinding (a domain that resolves to 127.0.0.1 after first load) defeats an
18
+ * `Origin === Host` comparison, because both then read as the attacker's
19
+ * domain. So the defence is three layers, and all three are load-bearing:
20
+ *
21
+ * 1. bind loopback by default (kills every off-machine caller);
22
+ * 2. answer only to a loopback `Host` (kills DNS rebinding, whose whole trick
23
+ * is arriving with an attacker-controlled Host);
24
+ * 3. reject cross-origin mutating requests by `Origin` (kills the ordinary
25
+ * drive-by, which arrives with a perfectly valid `Host: localhost:8020`
26
+ * and an `Origin` of the attacker's site).
27
+ *
28
+ * Layer 2 is what makes layer 3 trustworthy: without it, a rebound page is
29
+ * same-origin by the browser's own reckoning and layer 3 waves it through.
30
+ */
31
+
32
+ /** The default bind address. Loopback — never the wildcard. */
33
+ export const DEFAULT_DASHBOARD_HOST = "127.0.0.1";
34
+
35
+ /**
36
+ * Hostnames that mean "this machine". `[::1]` is the bracketed form as it
37
+ * appears in a `Host` header; `::1` is the bare form as it appears in a bind
38
+ * address.
39
+ */
40
+ const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "[::1]", "0:0:0:0:0:0:0:1"]);
41
+
42
+ /**
43
+ * True for any address that only this machine can reach.
44
+ *
45
+ * The whole 127.0.0.0/8 block is loopback, not just 127.0.0.1 — some setups
46
+ * bind 127.0.0.2 to separate services, and treating that as external would
47
+ * refuse a request the user deliberately arranged.
48
+ */
49
+ export function isLoopbackHostname(hostname: string): boolean {
50
+ const h = hostname.trim().toLowerCase();
51
+ if (!h) return false;
52
+ if (LOOPBACK_HOSTNAMES.has(h)) return true;
53
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
54
+ }
55
+
56
+ /**
57
+ * The hostname part of a `Host` header, with the port removed.
58
+ *
59
+ * IPv6 literals are bracketed (`[::1]:8020`), so the port cannot be found by
60
+ * looking for the last colon without handling the brackets first — `[::1]`
61
+ * is all colons.
62
+ */
63
+ export function hostnameFromHostHeader(hostHeader: string): string {
64
+ const raw = hostHeader.trim();
65
+ if (raw.startsWith("[")) {
66
+ const end = raw.indexOf("]");
67
+ return end === -1 ? raw : raw.slice(0, end + 1);
68
+ }
69
+ const colon = raw.indexOf(":");
70
+ return colon === -1 ? raw : raw.slice(0, colon);
71
+ }
72
+
73
+ /** The hostname part of an `Origin` header (`http://localhost:8020` -> `localhost`). */
74
+ export function hostnameFromOrigin(origin: string): string | null {
75
+ const raw = origin.trim();
76
+ // "null" is a real Origin value (sandboxed iframes, some redirects). It is
77
+ // never ours, so it must not parse into something permissive.
78
+ if (!raw || raw === "null") return null;
79
+ try {
80
+ const url = new URL(raw);
81
+ // `URL.hostname` KEEPS the brackets on an IPv6 literal ("[::1]"), which is
82
+ // already the form a Host header carries — so it passes through untouched.
83
+ // Bracketing it again would produce "[[::1]]" and never match anything.
84
+ return url.hostname || null;
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Resolve the bind address from an explicit flag, then the environment, then
92
+ * the safe default.
93
+ *
94
+ * An explicit non-loopback value is honoured — someone running the dashboard in
95
+ * a container or on a remote dev box has a real need — but it is a deliberate
96
+ * act, and `launch.ts` says so out loud when it happens.
97
+ */
98
+ export function resolveDashboardHost(flagHost?: string, envHost?: string): string {
99
+ const chosen = (flagHost ?? envHost ?? "").trim();
100
+ return chosen || DEFAULT_DASHBOARD_HOST;
101
+ }
@@ -19,10 +19,12 @@
19
19
  * real command.
20
20
  */
21
21
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
22
- import { resolve } from "node:path";
22
+ import { dirname, resolve } from "node:path";
23
23
  import { platform, arch, release, homedir, hostname } from "node:os";
24
24
  import { createHmac } from "node:crypto";
25
25
  import { trackInstallEvent } from "../scripts/install-telemetry.mjs";
26
+ import { isTelemetryEnabled } from "./telemetry-enabled";
27
+ import { globalPolicyConfigFile, lastVersionFile } from "../src/hooks/fp-home";
26
28
 
27
29
  const FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
28
30
  const NAMESPACE = "failproofai-telemetry-v1";
@@ -47,7 +49,7 @@ function hashToId(raw: string): string {
47
49
  }
48
50
 
49
51
  function lastVersionPath(): string {
50
- return resolve(homedir(), ".failproofai", "last-version");
52
+ return lastVersionFile();
51
53
  }
52
54
 
53
55
  /**
@@ -110,7 +112,13 @@ export interface HooksState {
110
112
  * script so the event keeps its historical meaning.
111
113
  */
112
114
  export function checkHooks(): HooksState {
113
- const hooksConfigPath = resolve(homedir(), ".failproofai", "policies-config.json");
115
+ // Layout 2's global policy config, via the one module allowed to join a path
116
+ // onto the home. This read `~/.failproofai/policies-config.json` — layout 1's
117
+ // location — so on every layout-2 machine it found nothing and reported the
118
+ // install as unconfigured with zero policies, which is what
119
+ // `package_installed` telemetry has been recording for every user since the
120
+ // reorganisation.
121
+ const hooksConfigPath = globalPolicyConfigFile();
114
122
  if (!existsSync(hooksConfigPath)) {
115
123
  return { configured: false, registered: false, policyCount: 0 };
116
124
  }
@@ -177,6 +185,13 @@ export async function maybeReportInstall(version: string): Promise<void> {
177
185
  // Steady state: same version already reported. Costs one stat() per command.
178
186
  if (previousVersion === version) return;
179
187
 
188
+ // The install dispatcher is a dependency-free .mjs and cannot import the
189
+ // TOML config, so it is told the verdict rather than resolving it. Passed
190
+ // per call rather than checked here, because the version marker below must
191
+ // still be written when telemetry is off — otherwise this whole check would
192
+ // re-run on every single CLI invocation, forever.
193
+ const telemetryEnabled = isTelemetryEnabled();
194
+
180
195
  const hooks = checkHooks();
181
196
 
182
197
  const events: Promise<void>[] = [];
@@ -187,7 +202,7 @@ export async function maybeReportInstall(version: string): Promise<void> {
187
202
  arch: arch(),
188
203
  os_release: release(),
189
204
  node_version: process.versions.node,
190
- }, { version, timeoutMs: REPORT_TIMEOUT_MS }),
205
+ }, { version, timeoutMs: REPORT_TIMEOUT_MS, enabled: telemetryEnabled }),
191
206
  );
192
207
  } else {
193
208
  const cmp = compareSemver(previousVersion, version);
@@ -198,7 +213,7 @@ export async function maybeReportInstall(version: string): Promise<void> {
198
213
  direction: cmp < 0 ? "upgrade" : "downgrade",
199
214
  platform: platform(),
200
215
  arch: arch(),
201
- }, { version, timeoutMs: REPORT_TIMEOUT_MS }),
216
+ }, { version, timeoutMs: REPORT_TIMEOUT_MS, enabled: telemetryEnabled }),
202
217
  );
203
218
  }
204
219
 
@@ -212,13 +227,13 @@ export async function maybeReportInstall(version: string): Promise<void> {
212
227
  hooks_configured: hooks.configured,
213
228
  hooks_registered: hooks.registered,
214
229
  enabled_policy_count: hooks.policyCount,
215
- }, { version, timeoutMs: REPORT_TIMEOUT_MS }),
230
+ }, { version, timeoutMs: REPORT_TIMEOUT_MS, enabled: telemetryEnabled }),
216
231
  );
217
232
 
218
233
  // Record before awaiting: a dropped event is cheaper than re-reporting the
219
234
  // same version on every command because delivery kept failing.
220
235
  try {
221
- mkdirSync(resolve(homedir(), ".failproofai"), { recursive: true });
236
+ mkdirSync(dirname(versionFile), { recursive: true });
222
237
  writeFileSync(versionFile, version, "utf8");
223
238
  } catch {
224
239
  // best-effort
@@ -17,12 +17,33 @@
17
17
  * {type: "message", id, parentId, timestamp,
18
18
  * message: {role, content[], timestamp}}
19
19
  *
20
- * `message.content[]` items can be `{type: "text", text}` or
21
- * `{type: "thinking", thinking, thinkingSignature}`. Tool-call blocks are not
22
- * yet observed in this codebase (no tool-using runs were captured during
23
- * Phase 0); when Pi does emit them, this parser preserves them as-is via the
24
- * fallback "system" branch and the test suite asserts at least the
25
- * round-trip rather than a specific shape.
20
+ * `message.content[]` items can be `{type: "text", text}`,
21
+ * `{type: "thinking", thinking, thinkingSignature}` or `{type: "toolCall",
22
+ * id, name, arguments}`.
23
+ *
24
+ * Tool calls WERE previously believed not to exist here the header used to
25
+ * say no tool-using run had been captured, so `toolCall` blocks fell through
26
+ * to the generic "system" branch and every tool event Pi emits was silently
27
+ * dropped from the audit path. A live capture (pi 0.73.1 and 0.83.0, driven
28
+ * against a real provider) shows Pi emits them in full:
29
+ *
30
+ * assistant turn content[] contains {type:"toolCall", id, name, arguments}
31
+ * and the message carries stopReason:"toolUse"
32
+ * tool result its own record with a THIRD role —
33
+ * message.role === "toolResult", carrying toolCallId,
34
+ * toolName, content[], isError, timestamp (epoch ms)
35
+ *
36
+ * `toolResult.toolCallId` pairs exactly with `toolCall.id`, one result record
37
+ * per call, emitted in call order — so results attach by id, never by
38
+ * position. Pi supplies no duration, so it is derived from the gap between
39
+ * the call and its result, the same way the OpenClaw parser does it.
40
+ *
41
+ * The format is identical across the 0.73.1 (`@mariozechner/pi-coding-agent`)
42
+ * and 0.83.0 (`@earendil-works/pi-coding-agent`) packages — same `version: 3`
43
+ * header, same record types — so one parser covers both. One difference worth
44
+ * guarding: 0.83.0 emits leading prose alongside the calls
45
+ * (`["text","toolCall","toolCall"]`) where 0.73.1 emitted only the calls, so
46
+ * assistant content must not be assumed homogeneous.
26
47
  */
27
48
  import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
28
49
  import { readFile } from "node:fs/promises";
@@ -38,8 +59,10 @@ import {
38
59
  type GenericEntry,
39
60
  type QueueOperationEntry,
40
61
  type ContentBlock,
62
+ type ToolUseBlock,
41
63
  type LogSource,
42
64
  } from "./log-entries";
65
+ import { formatDuration } from "./format-duration";
43
66
 
44
67
  // ── Paths ──
45
68
 
@@ -107,6 +130,10 @@ interface PiSessionRecord {
107
130
  role?: string;
108
131
  content?: Array<Record<string, unknown>>;
109
132
  timestamp?: number;
133
+ /** Present on `role: "toolResult"` records — pairs with a `toolCall.id`. */
134
+ toolCallId?: string;
135
+ toolName?: string;
136
+ isError?: boolean;
110
137
  };
111
138
  }
112
139
 
@@ -129,9 +156,19 @@ function extractMessageText(content: Array<Record<string, unknown>> | undefined)
129
156
  return parts.join("\n\n");
130
157
  }
131
158
 
132
- /** Build a list of ContentBlocks for the assistant entry, preserving text and
133
- * thinking blocks. Skips blocks with non-string payloads (typeof guards). */
134
- function buildAssistantContent(content: Array<Record<string, unknown>> | undefined): ContentBlock[] {
159
+ /** Build a list of ContentBlocks for the assistant entry, preserving text,
160
+ * thinking and tool-call blocks. Skips blocks with non-string payloads
161
+ * (typeof guards).
162
+ *
163
+ * Every `tool_use` block built here is also handed to `onToolUse` so the
164
+ * caller can index it by id and attach the matching `toolResult` record when
165
+ * it arrives on a later line. The block is passed by reference and mutated
166
+ * in place, exactly as the OpenClaw parser does — the entry has already been
167
+ * pushed by then, so there is nothing else to attach it to. */
168
+ function buildAssistantContent(
169
+ content: Array<Record<string, unknown>> | undefined,
170
+ onToolUse?: (block: ToolUseBlock) => void,
171
+ ): ContentBlock[] {
135
172
  if (!Array.isArray(content)) return [];
136
173
  const blocks: ContentBlock[] = [];
137
174
  for (const block of content) {
@@ -143,6 +180,22 @@ function buildAssistantContent(content: Array<Record<string, unknown>> | undefin
143
180
  if (block?.type === "thinking" && typeof block.thinking === "string" && block.thinking.length > 0) {
144
181
  blocks.push({ type: "text", text: `[thinking] ${block.thinking}` });
145
182
  }
183
+ if (block?.type === "toolCall") {
184
+ // Fall back to a positional id only when Pi omits one. A synthetic id
185
+ // still renders, but it can never pair with a result — so it must not
186
+ // collide with a real one, hence the index suffix.
187
+ const id = typeof block.id === "string" && block.id.length > 0
188
+ ? block.id
189
+ : `${typeof block.name === "string" ? block.name : "tool"}-${blocks.length}`;
190
+ const name = typeof block.name === "string" ? block.name : "tool";
191
+ const input =
192
+ block.arguments && typeof block.arguments === "object" && !Array.isArray(block.arguments)
193
+ ? (block.arguments as Record<string, unknown>)
194
+ : {};
195
+ const toolUse: ToolUseBlock = { type: "tool_use", id, name, input };
196
+ blocks.push(toolUse);
197
+ onToolUse?.(toolUse);
198
+ }
146
199
  }
147
200
  return blocks;
148
201
  }
@@ -161,6 +214,12 @@ export async function parsePiLog(
161
214
  const rawLines: Record<string, unknown>[] = [];
162
215
  let cwd: string | undefined;
163
216
  let seenSessionStart = false;
217
+ // In-flight tool calls, so a later `toolResult` record can attach its output
218
+ // to the block it belongs to. Keyed by the provider's own call id, never by
219
+ // position — Pi emits results in call order today, but pairing by order
220
+ // would break silently the first time it does not.
221
+ const toolUseById = new Map<string, ToolUseBlock>();
222
+ const toolUseStartMs = new Map<string, number>();
164
223
 
165
224
  for (let i = 0; i < lines.length; i++) {
166
225
  if (i > 0 && i % 200 === 0) await new Promise<void>((r) => setImmediate(r));
@@ -215,7 +274,10 @@ export async function parsePiLog(
215
274
  }
216
275
 
217
276
  if (role === "assistant") {
218
- const blocks = buildAssistantContent(content);
277
+ const blocks = buildAssistantContent(content, (block) => {
278
+ toolUseById.set(block.id, block);
279
+ toolUseStartMs.set(block.id, date.getTime());
280
+ });
219
281
  if (blocks.length === 0) {
220
282
  entries.push({
221
283
  type: "system",
@@ -232,6 +294,33 @@ export async function parsePiLog(
232
294
  continue;
233
295
  }
234
296
 
297
+ // Pi's third role: a tool result, on its own record, pairing back to an
298
+ // assistant turn's toolCall by id. Attaching it to that block is what
299
+ // makes the tool's OUTPUT visible — without this the call renders with
300
+ // no result and the audit path sees no `toolResultText` at all.
301
+ if (role === "toolResult") {
302
+ const callId = raw.message.toolCallId;
303
+ const block = typeof callId === "string" ? toolUseById.get(callId) : undefined;
304
+ if (block) {
305
+ // Pi records no duration on the result, so derive it from the gap
306
+ // between the call and its result. `startMs` is always present for
307
+ // a block we indexed; the fallback keeps the arithmetic total.
308
+ const startMs = (typeof callId === "string" && toolUseStartMs.get(callId)) || date.getTime();
309
+ const durationMs = Math.max(0, date.getTime() - startMs);
310
+ block.result = {
311
+ timestamp,
312
+ timestampFormatted: formatTimestamp(date),
313
+ content: extractMessageText(content),
314
+ durationMs,
315
+ durationFormatted: formatDuration(durationMs),
316
+ };
317
+ continue;
318
+ }
319
+ // Orphan result — the call was never seen (truncated file, or a
320
+ // resumed session whose earlier half is in another file). Fall
321
+ // through so the record is preserved rather than dropped.
322
+ }
323
+
235
324
  // Unknown role — preserve raw so nothing is dropped.
236
325
  entries.push({
237
326
  type: "system",
@@ -319,7 +408,3 @@ export function readPiTranscriptSync(sessionId: string): string | null {
319
408
  return null;
320
409
  }
321
410
  }
322
-
323
- /** Suppress unused-import warning for formatTimestamp; reserved for tool-call
324
- * rendering once Pi emits it (see header comment). */
325
- void formatTimestamp;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The single answer to "may we send telemetry?", shared by every dispatcher.
3
+ *
4
+ * There are four of them — the CLI and hook path (`src/hooks/hook-telemetry.ts`),
5
+ * the Next.js server (`lib/telemetry.ts`), the browser (`lib/client-telemetry.ts`,
6
+ * which inherits this via `getTelemetryConfig`), and the install script
7
+ * (`scripts/install-telemetry.mjs`, gated by its TypeScript caller). A dispatcher
8
+ * that resolved this differently would be an opt-out that silently does not hold,
9
+ * which is worse than having no opt-out at all.
10
+ *
11
+ * ## Two sources, and why the file is the important one
12
+ *
13
+ * `FAILPROOFAI_TELEMETRY_DISABLED=1` is read from `process.env`. That works for
14
+ * anything the user launches from a shell, and it is kept because people already
15
+ * rely on it. It cannot work for **failproofaid**: the daemon is a system-scope
16
+ * service unit whose environment carries essentially nothing, so an exported
17
+ * variable is structurally incapable of reaching it. `[telemetry] enabled` in
18
+ * config.toml is a file both the CLI and the daemon read, which is why it is the
19
+ * off-switch the documentation points at.
20
+ *
21
+ * They resolve to the **more restrictive** of the two: either says stop, and we
22
+ * stop. The environment can never re-enable something the file switched off —
23
+ * an env var that could override a written preference is not an opt-out.
24
+ */
25
+ import { readConfig } from "../src/hooks/fp-config";
26
+
27
+ /**
28
+ * Resolved fresh on every call, deliberately.
29
+ *
30
+ * Memoising this was the obvious optimisation and it is the wrong one. An
31
+ * opt-out that a long-lived process (the dashboard server, the warm worker)
32
+ * keeps ignoring until it restarts is an opt-out that does not hold, and
33
+ * "telemetry stopped only after a reboot" is precisely the bug that destroys
34
+ * trust in a switch like this.
35
+ *
36
+ * The cost is a small `readFileSync` + TOML parse per event. That is affordable
37
+ * even on the hook path's 150ms fail-closed budget: the same `readConfig()` is
38
+ * already called there by `isDaemonConfigured()`, so this adds a repeat of work
39
+ * the path was already paying for, on a file of a few hundred bytes.
40
+ */
41
+ export function isTelemetryEnabled(): boolean {
42
+ // Checked first, and cheaply: an env lookup costs nothing, and letting it
43
+ // short-circuit means an operator who exported the variable never pays for a
44
+ // file read at all.
45
+ if (process.env.FAILPROOFAI_TELEMETRY_DISABLED === "1") return false;
46
+ try {
47
+ return readConfig().telemetry.enabled;
48
+ } catch {
49
+ // readConfig already resolves an unreadable or malformed file to the
50
+ // defaults, so reaching here means something more unusual. Fall back to the
51
+ // shipped default rather than inventing a third answer.
52
+ return true;
53
+ }
54
+ }
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import os from "node:os";
4
4
  import crypto from "node:crypto";
5
5
  import { execSync } from "node:child_process";
6
+ import { telemetryIdFile } from "../src/hooks/fp-home";
6
7
 
7
8
  const NAMESPACE = "failproofai-telemetry-v1";
8
9
  const ID_DIR = path.join(os.homedir(), ".failproofai");
@@ -66,6 +67,52 @@ function getFileBasedId(): string {
66
67
  return id;
67
68
  }
68
69
 
70
+ /**
71
+ * Publish the resolved id where **failproofaid** can read it.
72
+ *
73
+ * The daemon cannot re-derive this. Tier 2 below hashes `os.arch()` and
74
+ * `os.cpus()[0].model`, which are Node's own formatting — Node says `x64` where
75
+ * Rust says `x86_64` — and a near-miss there does not fail, it silently files
76
+ * one machine under two different PostHog persons with nothing in the data to
77
+ * say so. So the CLI writes what it resolved and the daemon reads it
78
+ * (`telemetry_id_path()` in `crates/failproofaid/src/paths.rs`).
79
+ *
80
+ * Deliberately NOT gated on the telemetry opt-out. This sends nothing — it
81
+ * writes one anonymous HMAC to a local file — and gating it would mean a machine
82
+ * that opted out and later opted back in reported under a different identity
83
+ * until the next CLI run. The gate belongs on the sending, which is where both
84
+ * this process and the daemon apply it.
85
+ *
86
+ * Every failure is swallowed. This runs on the hook path, and a telemetry id
87
+ * that could not be published is worth exactly nothing next to a tool call that
88
+ * did not complete. The read-and-compare is what keeps the common case to one
89
+ * small `readFileSync` rather than a write per process.
90
+ */
91
+ function publishInstanceId(id: string): void {
92
+ let target: string;
93
+ try {
94
+ target = telemetryIdFile();
95
+ if (fs.readFileSync(target, "utf-8").trim() === id) return;
96
+ } catch {
97
+ // Absent or unreadable — fall through and try to write it.
98
+ try {
99
+ target = telemetryIdFile();
100
+ } catch {
101
+ return;
102
+ }
103
+ }
104
+ try {
105
+ fs.mkdirSync(path.dirname(target), { recursive: true });
106
+ // tmp → rename, and the tmp name carries the pid: two CLI processes racing
107
+ // here is ordinary (every hook call resolves an id), and a shared temp name
108
+ // would let one truncate the file the other is renaming into place, leaving
109
+ // a torn id that the daemon would then adopt as a permanent, wrong person.
110
+ const tmp = `${target}.${process.pid}.tmp`;
111
+ fs.writeFileSync(tmp, id, { encoding: "utf-8", mode: 0o600 });
112
+ fs.renameSync(tmp, target);
113
+ } catch {}
114
+ }
115
+
69
116
  /**
70
117
  * Returns a stable, anonymous machine ID for telemetry.
71
118
  *
@@ -75,7 +122,8 @@ function getFileBasedId(): string {
75
122
  * 3. File-based random UUID at `~/.failproofai/instance-id` (final fallback)
76
123
  *
77
124
  * All raw values are HMAC-hashed with an app-specific namespace so no PII
78
- * is transmitted. The result is cached in-process to avoid repeated I/O.
125
+ * is transmitted. The result is cached in-process to avoid repeated I/O, and
126
+ * published once per process for the daemon (see `publishInstanceId`).
79
127
  */
80
128
  export function getInstanceId(): string {
81
129
  if (cachedId) return cachedId;
@@ -83,15 +131,18 @@ export function getInstanceId(): string {
83
131
  const machineId = getPlatformMachineId();
84
132
  if (machineId) {
85
133
  cachedId = hashToId(machineId);
134
+ publishInstanceId(cachedId);
86
135
  return cachedId;
87
136
  }
88
137
 
89
138
  const sysProps = getSystemPropertiesId();
90
139
  if (sysProps) {
91
140
  cachedId = hashToId(sysProps);
141
+ publishInstanceId(cachedId);
92
142
  return cachedId;
93
143
  }
94
144
 
95
145
  cachedId = getFileBasedId();
146
+ publishInstanceId(cachedId);
96
147
  return cachedId;
97
148
  }
package/lib/telemetry.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { getInstanceId } from "./telemetry-id";
12
+ import { isTelemetryEnabled } from "./telemetry-enabled";
12
13
  import { version } from "../package.json";
13
14
  import { POSTHOG_API_KEY, POSTHOG_PRODUCT } from "../src/posthog-key";
14
15
 
@@ -74,10 +75,13 @@ async function resilientFetch(
74
75
  return new Response("{}", { status: 200 });
75
76
  }
76
77
 
77
- /** Returns true unless the user has explicitly opted out. */
78
- export function isTelemetryEnabled(): boolean {
79
- return process.env.FAILPROOFAI_TELEMETRY_DISABLED !== "1";
80
- }
78
+ /**
79
+ * Returns true unless the user has explicitly opted out, via either
80
+ * `FAILPROOFAI_TELEMETRY_DISABLED=1` or `[telemetry] enabled = false` in
81
+ * config.toml. Re-exported from the shared resolver so all four dispatchers
82
+ * cannot drift — see lib/telemetry-enabled.ts.
83
+ */
84
+ export { isTelemetryEnabled };
81
85
 
82
86
  /**
83
87
  * Lazily import posthog-node and create a client.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.21",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs",
@@ -76,19 +76,19 @@
76
76
  "@anthropic-ai/sdk": "^0.115.0",
77
77
  "@mdx-js/mdx": "^3.1.1",
78
78
  "@tailwindcss/postcss": "^4.3.1",
79
- "@tanstack/react-virtual": "^3.14.3",
79
+ "@tanstack/react-virtual": "^3.14.9",
80
80
  "@testing-library/jest-dom": "^7.0.0",
81
81
  "@testing-library/react": "^16.3.2",
82
82
  "@testing-library/user-event": "^14.6.1",
83
- "@types/node": "26.1.1",
84
- "@types/react": "19.2.17",
83
+ "@types/node": "26.1.2",
84
+ "@types/react": "19.2.18",
85
85
  "@types/react-dom": "^19.2.3",
86
- "@vitejs/plugin-react": "^6.0.1",
86
+ "@vitejs/plugin-react": "^6.0.5",
87
87
  "clsx": "^2.1.1",
88
88
  "eslint": "^10.5.0",
89
89
  "eslint-config-next": "^16.2.9",
90
- "jsdom": "^30.0.0",
91
- "lucide-react": "^1.18.0",
90
+ "jsdom": "^30.0.1",
91
+ "lucide-react": "^1.28.0",
92
92
  "next": "^16.2.11",
93
93
  "react": "^19.2.4",
94
94
  "react-dom": "^19.2.4",
@@ -99,17 +99,28 @@
99
99
  },
100
100
  "dependencies": {
101
101
  "html-to-image": "^1.11.13",
102
- "html2canvas": "^1.4.1",
103
- "posthog-node": "^5.37.1",
102
+ "posthog-node": "^5.47.7",
104
103
  "sql.js": "^1.14.1",
105
104
  "yaml": "^2.9.0"
106
105
  },
107
106
  "overrides": {
108
- "postcss": "8.5.23",
107
+ "postcss": "8.5.26",
109
108
  "eslint-plugin-react-hooks": "7.0.1",
110
109
  "vite": "8.0.16",
111
- "undici": "7.28.0",
112
- "brace-expansion": "5.0.8",
110
+ "undici": "7.29.0",
111
+ "brace-expansion": "5.0.9",
113
112
  "sharp": "0.35.0"
113
+ },
114
+ "optionalDependencies": {
115
+ "@failproofai/failproofaid-linux-x64": "1.0.0-beta.21",
116
+ "@failproofai/failproofaid-linux-arm64": "1.0.0-beta.21",
117
+ "@failproofai/failproofaid-darwin-x64": "1.0.0-beta.21",
118
+ "@failproofai/failproofaid-darwin-arm64": "1.0.0-beta.21"
119
+ },
120
+ "failproofaidBinaries": {
121
+ "linux-x64": "78848fa69407c528ee9fea02bdd64c2f95aad6a84a4d3973780200c5644fe61c",
122
+ "linux-arm64": "eef4b4a435c44ba913610f213436bd34a873299ef774961a1af70cf76da934a2",
123
+ "darwin-x64": "abdfbdac60f06db20c69b11ff477123481622da3e03bd531e1896b8757ce0884",
124
+ "darwin-arm64": "49fc9dc590c38ece592aa46e734bb6c0743559e3dbb90996e8381056abb2f5da"
114
125
  }
115
126
  }