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
package/dist/worker.mjs CHANGED
@@ -362,6 +362,43 @@ function normalizeCliPayload(cli, parsed) {
362
362
  }
363
363
  }
364
364
 
365
+ // src/hooks/fp-home.ts
366
+ import { homedir } from "node:os";
367
+ import { resolve } from "node:path";
368
+ function failproofaiHome(home) {
369
+ if (home)
370
+ return resolve(home, ".failproofai");
371
+ return process.env.FAILPROOFAI_HOME || resolve(homedir(), ".failproofai");
372
+ }
373
+ var LAYOUT_VERSION = 3, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), hookActivityDir = (home) => atHome(home, "hook-activity"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), sessionPauseDir = () => resolve(stateDir(), "sessions"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
374
+ var init_fp_home = __esm(() => {
375
+ customPoliciesDir = policiesDir;
376
+ legacy = {
377
+ policyConfig: () => at("policies-config.json"),
378
+ configToml: () => at("config.toml"),
379
+ credentialsToml: () => at("credentials.toml"),
380
+ localPoliciesDir: () => at("policies", "local-policies"),
381
+ localPolicyConfig: () => at("policies", "local-policies", "policies-config.json"),
382
+ customPoliciesDir: () => at("policies", "custom-policies"),
383
+ policyConfigLocal: () => at("policies-config.local.json"),
384
+ cloudCredentials: () => at("cloud.json"),
385
+ ingestCredentials: () => at("ingest.json"),
386
+ launcherMarker: () => at(".launcher-configured"),
387
+ lastVersion: () => at("last-version"),
388
+ auditDashboard: () => at("audit-dashboard.json"),
389
+ cacheDir: () => at("cache"),
390
+ hookActivityDir: () => at("cache", "hook-activity"),
391
+ auditCacheDir: () => at("cache", "audit"),
392
+ codexSessionPaths: () => at("cache", "codex-session-paths.json"),
393
+ spoolDir: () => at("spool"),
394
+ failedDir: () => at("failed"),
395
+ collectorHealth: () => at("collector-health.json"),
396
+ sessionPauseDir: () => at("state", "sessions"),
397
+ cloudManagedPolicies: () => at("policies", "cloud-managed"),
398
+ onboardingLock: () => at(".onboarding.lock")
399
+ };
400
+ });
401
+
365
402
  // src/hooks/hook-logger.ts
366
403
  import {
367
404
  appendFileSync,
@@ -371,7 +408,6 @@ import {
371
408
  statSync
372
409
  } from "node:fs";
373
410
  import { join } from "node:path";
374
- import { homedir } from "node:os";
375
411
  function ensureResolved() {
376
412
  if (resolved)
377
413
  return;
@@ -443,15 +479,16 @@ function hookLogError(msg) {
443
479
  }
444
480
  var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
445
481
  var init_hook_logger = __esm(() => {
482
+ init_fp_home();
446
483
  LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
447
484
  MAX_FILE_SIZE = 512 * 1024;
448
- DEFAULT_LOG_DIR = join(homedir(), ".failproofai", "logs");
485
+ DEFAULT_LOG_DIR = logsDir();
449
486
  logDir = DEFAULT_LOG_DIR;
450
487
  });
451
488
 
452
489
  // src/hooks/hooks-config.ts
453
490
  import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
454
- import { resolve, dirname } from "node:path";
491
+ import { resolve as resolve2, dirname } from "node:path";
455
492
  import { homedir as homedir2 } from "node:os";
456
493
  function readConfigAt(path) {
457
494
  if (!existsSync2(path))
@@ -466,9 +503,9 @@ function readConfigAt(path) {
466
503
  }
467
504
  function findProjectConfigDir(start) {
468
505
  const home = homedir2();
469
- let dir = resolve(start);
506
+ let dir = resolve2(start);
470
507
  while (dir !== home) {
471
- const marker = resolve(dir, ".failproofai");
508
+ const marker = resolve2(dir, ".failproofai");
472
509
  try {
473
510
  if (statSync2(marker).isDirectory())
474
511
  return dir;
@@ -478,13 +515,13 @@ function findProjectConfigDir(start) {
478
515
  break;
479
516
  dir = parent;
480
517
  }
481
- return resolve(start);
518
+ return resolve2(start);
482
519
  }
483
520
  function readMergedHooksConfig(cwd) {
484
521
  const base = findProjectConfigDir(cwd ?? process.cwd());
485
- const projectPath = resolve(base, ".failproofai", "policies-config.json");
486
- const localPath = resolve(base, ".failproofai", "policies-config.local.json");
487
- const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
522
+ const projectPath = resolve2(base, ".failproofai", "policies-config.json");
523
+ const localPath = resolve2(base, ".failproofai", "policies-config.local.json");
524
+ const globalPath = globalPolicyConfigFile();
488
525
  const project = readConfigAt(projectPath);
489
526
  const local = readConfigAt(localPath);
490
527
  const global_ = readConfigAt(globalPath);
@@ -522,6 +559,7 @@ function readMergedHooksConfig(cwd) {
522
559
  }
523
560
  var init_hooks_config = __esm(() => {
524
561
  init_hook_logger();
562
+ init_fp_home();
525
563
  });
526
564
 
527
565
  // src/hooks/policy-helpers.ts
@@ -596,7 +634,7 @@ function clearPolicies() {
596
634
  var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
597
635
 
598
636
  // src/hooks/builtin-policies.ts
599
- import { resolve as resolve2, join as join2 } from "node:path";
637
+ import { resolve as resolve3, join as join2 } from "node:path";
600
638
  import { statSync as statSync3 } from "node:fs";
601
639
  import { readFile, writeFile } from "node:fs/promises";
602
640
  import { execSync, execFileSync } from "node:child_process";
@@ -655,6 +693,65 @@ function getFilePath(ctx) {
655
693
  function parseArgvTokens(cmd) {
656
694
  return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
657
695
  }
696
+ function namesSelfPause(command) {
697
+ for (const segment of command.split(SEGMENT_SEPARATORS)) {
698
+ const tokens = segment.split(/\s+/).filter(Boolean);
699
+ let i = 0;
700
+ while (i < tokens.length) {
701
+ const token = tokens[i];
702
+ const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(token.slice(token.lastIndexOf("/") + 1));
703
+ if (!isSkippable)
704
+ break;
705
+ i++;
706
+ }
707
+ if (i >= tokens.length)
708
+ continue;
709
+ if (!SELF_BINARY_TOKEN_RE.test(tokens[i]))
710
+ continue;
711
+ const args = tokens.slice(i + 1);
712
+ const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
713
+ if (configAt === -1)
714
+ continue;
715
+ if (args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a)))
716
+ return true;
717
+ }
718
+ return false;
719
+ }
720
+ function decodeAnsiC(body) {
721
+ return body.replace(/\\(x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{1,4}|U[0-9A-Fa-f]{1,8}|[0-7]{1,3}|.)/g, (_, seq) => {
722
+ try {
723
+ if (seq[0] === "x")
724
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
725
+ if (seq[0] === "u" || seq[0] === "U")
726
+ return String.fromCodePoint(parseInt(seq.slice(1), 16));
727
+ if (/^[0-7]+$/.test(seq))
728
+ return String.fromCharCode(parseInt(seq, 8));
729
+ } catch {
730
+ return seq;
731
+ }
732
+ const named = {
733
+ a: "\x07",
734
+ b: "\b",
735
+ e: "\x1B",
736
+ f: "\f",
737
+ n: `
738
+ `,
739
+ r: "\r",
740
+ t: "\t",
741
+ v: "\v",
742
+ "\\": "\\",
743
+ "'": "'",
744
+ '"': '"',
745
+ "?": "?"
746
+ };
747
+ return named[seq] ?? seq;
748
+ });
749
+ }
750
+ function stripShellQuoting(command) {
751
+ const joined = command.replace(/\\\r?\n/g, "");
752
+ const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body) => decodeAnsiC(body));
753
+ return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
754
+ }
658
755
  function statGitHeadMtimeMs(cwd) {
659
756
  try {
660
757
  return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
@@ -899,11 +996,82 @@ function blockEnvFiles(ctx) {
899
996
  }
900
997
  return allow();
901
998
  }
999
+ function quoteAwareSegments(command) {
1000
+ const segments = [];
1001
+ let tokens = [];
1002
+ let token = "";
1003
+ let quote = null;
1004
+ const endToken = () => {
1005
+ if (token)
1006
+ tokens.push(token);
1007
+ token = "";
1008
+ };
1009
+ const endSegment = () => {
1010
+ endToken();
1011
+ if (tokens.length)
1012
+ segments.push(tokens);
1013
+ tokens = [];
1014
+ };
1015
+ for (let i = 0;i < command.length; i++) {
1016
+ const c = command[i];
1017
+ if (quote) {
1018
+ if (c === "\\" && quote === '"' && i + 1 < command.length) {
1019
+ token += command[++i];
1020
+ continue;
1021
+ }
1022
+ if (c === quote) {
1023
+ quote = null;
1024
+ continue;
1025
+ }
1026
+ token += c;
1027
+ continue;
1028
+ }
1029
+ if (c === "\\" && i + 1 < command.length) {
1030
+ token += command[++i];
1031
+ continue;
1032
+ }
1033
+ if (c === '"' || c === "'") {
1034
+ quote = c;
1035
+ continue;
1036
+ }
1037
+ if (/\s/.test(c)) {
1038
+ endToken();
1039
+ continue;
1040
+ }
1041
+ if (/[;&|\n\r(){}`]/.test(c)) {
1042
+ endSegment();
1043
+ continue;
1044
+ }
1045
+ token += c;
1046
+ }
1047
+ endSegment();
1048
+ return segments;
1049
+ }
1050
+ function namesElevation(command, depth = 0) {
1051
+ for (const tokens of quoteAwareSegments(command)) {
1052
+ let i = 0;
1053
+ while (i < tokens.length) {
1054
+ const token = tokens[i];
1055
+ const base = token.slice(token.lastIndexOf("/") + 1);
1056
+ if (base === "sudo" || base === "doas")
1057
+ return true;
1058
+ const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(base);
1059
+ if (!isSkippable)
1060
+ break;
1061
+ if (EVAL_FLAG_RE.test(token) && depth < 3 && i + 1 < tokens.length) {
1062
+ if (namesElevation(tokens[i + 1], depth + 1))
1063
+ return true;
1064
+ }
1065
+ i++;
1066
+ }
1067
+ }
1068
+ return false;
1069
+ }
902
1070
  function blockSudo(ctx) {
903
1071
  if (ctx.toolName !== "Bash")
904
1072
  return allow();
905
1073
  const cmd = getCommand(ctx).trimStart();
906
- if (SUDO_RE.test(cmd) || cmd.startsWith("sudo ")) {
1074
+ if (namesElevation(cmd)) {
907
1075
  const allowPatterns = ctx.params?.allowPatterns ?? [];
908
1076
  if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p)))
909
1077
  return allow();
@@ -917,6 +1085,15 @@ function blockSudo(ctx) {
917
1085
  }
918
1086
  return allow();
919
1087
  }
1088
+ function blockSelfPause(ctx) {
1089
+ if (ctx.toolName !== "Bash")
1090
+ return allow();
1091
+ const cmd = getCommand(ctx);
1092
+ if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
1093
+ return deny("Pausing failproofai enforcement is a human action, not an agent one. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
1094
+ }
1095
+ return allow();
1096
+ }
920
1097
  function blockCurlPipeSh(ctx) {
921
1098
  if (ctx.toolName !== "Bash")
922
1099
  return allow();
@@ -1142,7 +1319,7 @@ function blockReadOutsideCwd(ctx) {
1142
1319
  const paths = extractAbsolutePaths(cmd);
1143
1320
  const cwdWithSep2 = cwd.endsWith("/") ? cwd : cwd + "/";
1144
1321
  for (const p of paths) {
1145
- const resolved3 = resolve2(cwd, p);
1322
+ const resolved3 = resolve3(cwd, p);
1146
1323
  if (isClaudeSettingsFile(resolved3)) {
1147
1324
  return deny(`Reading agent settings file blocked: ${resolved3}`);
1148
1325
  }
@@ -1163,7 +1340,7 @@ function blockReadOutsideCwd(ctx) {
1163
1340
  const target = filePath || searchPath;
1164
1341
  if (!target)
1165
1342
  return allow();
1166
- const resolved2 = resolve2(cwd, target);
1343
+ const resolved2 = resolve3(cwd, target);
1167
1344
  if (isClaudeSettingsFile(resolved2)) {
1168
1345
  return deny(`Reading agent settings file blocked: ${resolved2}`);
1169
1346
  }
@@ -1654,7 +1831,7 @@ function registerBuiltinPolicies(enabledNames) {
1654
1831
  }
1655
1832
  }
1656
1833
  }
1657
- var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1834
+ var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1658
1835
  var init_builtin_policies = __esm(() => {
1659
1836
  init_hook_logger();
1660
1837
  isClaudeInternalPath = isAgentInternalPath;
@@ -1691,10 +1868,48 @@ var init_builtin_policies = __esm(() => {
1691
1868
  CMD_ECHO_ENV_RE = /echo\s+%[A-Za-z_]/i;
1692
1869
  ENV_FILE_PATH_RE = /(?:^|[\\/])\.env(?:\.|$)/;
1693
1870
  ENV_CMD_RE = /\.env(?:\b|\s|$|\.)/;
1694
- SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
1695
1871
  PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
1696
1872
  RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
1697
1873
  CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
1874
+ SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
1875
+ COMMAND_PREFIX_TOKENS = new Set([
1876
+ "npx",
1877
+ "bunx",
1878
+ "pnpx",
1879
+ "npm",
1880
+ "pnpm",
1881
+ "yarn",
1882
+ "dlx",
1883
+ "exec",
1884
+ "run",
1885
+ "node",
1886
+ "bun",
1887
+ "deno",
1888
+ "env",
1889
+ "command",
1890
+ "builtin",
1891
+ "nohup",
1892
+ "setsid",
1893
+ "time",
1894
+ "timeout",
1895
+ "nice",
1896
+ "stdbuf",
1897
+ "xargs",
1898
+ "sudo",
1899
+ "doas",
1900
+ "sh",
1901
+ "bash",
1902
+ "zsh",
1903
+ "dash",
1904
+ "ksh",
1905
+ "fish",
1906
+ "ash"
1907
+ ]);
1908
+ SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
1909
+ CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
1910
+ PAUSE_FLAG_RE = /^--pause(?:=|$)/;
1911
+ ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
1912
+ RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
1698
1913
  PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
1699
1914
  SHORT_FLAG_BUNDLE_RE = /^-[a-zA-Z]*f[a-zA-Z]*$/;
1700
1915
  SAFE_FORCE_PREFIXES = ["--force-with-lease", "--force-if-includes"];
@@ -1738,6 +1953,7 @@ var init_builtin_policies = __esm(() => {
1738
1953
  HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
1739
1954
  GH_PIPELINE_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gh\s+(?:workflow\s+(?:run|enable|disable)|run\s+(?:rerun|cancel)|pr\s+merge|release\s+(?:create|delete)|cache\s+delete|secret\s+(?:set|delete))\b/;
1740
1955
  gitBranchCache = new Map;
1956
+ EVAL_FLAG_RE = /^-{1,2}c$/;
1741
1957
  HOME_PREFIX_RE = /^(?:~[A-Za-z0-9_.-]*|\$HOME|\$\{HOME\})(?=$|\/)/;
1742
1958
  RM_CMD_RE = /^(?:\/\S*\/)?rm$/;
1743
1959
  FIND_CMD_RE = /^(?:\/\S*\/)?find$/;
@@ -1842,6 +2058,16 @@ var init_builtin_policies = __esm(() => {
1842
2058
  }
1843
2059
  }
1844
2060
  },
2061
+ {
2062
+ name: "block-self-pause",
2063
+ displayTitle: "Tried to pause failproofai enforcement",
2064
+ impact: "An agent that can pause enforcement can switch off every other policy.",
2065
+ description: "Block agents from pausing failproofai enforcement",
2066
+ fn: blockSelfPause,
2067
+ match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
2068
+ defaultEnabled: true,
2069
+ category: "Dangerous Commands"
2070
+ },
1845
2071
  {
1846
2072
  name: "block-sudo",
1847
2073
  displayTitle: "Tried to run a command with sudo",
@@ -2285,12 +2511,154 @@ var init_builtin_policies = __esm(() => {
2285
2511
  });
2286
2512
 
2287
2513
  // package.json
2288
- var version = "1.0.0-beta.2";
2514
+ var version = "1.0.0-beta.21";
2289
2515
  var init_package = () => {};
2290
2516
 
2291
2517
  // src/posthog-key.ts
2292
2518
  var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
2293
2519
 
2520
+ // src/hooks/fp-config.ts
2521
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
2522
+ function detectLayout() {
2523
+ const raw = readVersionFile();
2524
+ if (raw) {
2525
+ if (raw.layout === LAYOUT_VERSION)
2526
+ return { kind: "current", version: raw };
2527
+ return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
2528
+ }
2529
+ if (existsSync3(configFile())) {
2530
+ return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
2531
+ }
2532
+ if (existsSync3(legacy.configToml()))
2533
+ return { kind: "stale", found: LAYOUT_VERSION - 1 };
2534
+ const layoutOneMarkers = [
2535
+ legacy.policyConfig(),
2536
+ legacy.cacheDir(),
2537
+ legacy.launcherMarker(),
2538
+ legacy.lastVersion(),
2539
+ legacy.cloudCredentials(),
2540
+ legacy.ingestCredentials(),
2541
+ legacy.auditDashboard()
2542
+ ];
2543
+ return layoutOneMarkers.some((p) => existsSync3(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
2544
+ }
2545
+ function readLegacyTomlVersion(text) {
2546
+ const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
2547
+ if (!Number.isFinite(layout))
2548
+ return null;
2549
+ const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
2550
+ return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
2551
+ }
2552
+ function readVersionFile() {
2553
+ let text;
2554
+ try {
2555
+ text = readFileSync2(versionFile(), "utf8");
2556
+ } catch {
2557
+ return null;
2558
+ }
2559
+ try {
2560
+ const parsed = JSON.parse(text);
2561
+ const layout = Number(parsed.layout);
2562
+ if (!Number.isFinite(layout))
2563
+ return null;
2564
+ return {
2565
+ layout,
2566
+ cli: typeof parsed.cli === "string" ? parsed.cli : "",
2567
+ daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
2568
+ };
2569
+ } catch {
2570
+ return readLegacyTomlVersion(text);
2571
+ }
2572
+ }
2573
+ function readIntervalDays(raw) {
2574
+ if (typeof raw !== "number" || !Number.isFinite(raw))
2575
+ return DEFAULT_AUDIT_INTERVAL_DAYS;
2576
+ const days = Math.floor(raw);
2577
+ if (days < MIN_AUDIT_INTERVAL_DAYS)
2578
+ return DEFAULT_AUDIT_INTERVAL_DAYS;
2579
+ return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
2580
+ }
2581
+ function readSources(raw) {
2582
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
2583
+ return;
2584
+ const out = {};
2585
+ for (const [name, table] of Object.entries(raw)) {
2586
+ if (!table || typeof table !== "object" || Array.isArray(table))
2587
+ continue;
2588
+ const entries = table.extra_paths;
2589
+ if (!Array.isArray(entries))
2590
+ continue;
2591
+ const paths = entries.filter((e) => typeof e === "string" && e.trim() !== "");
2592
+ if (paths.length > 0)
2593
+ out[name] = { extraPaths: paths };
2594
+ }
2595
+ return Object.keys(out).length > 0 ? out : undefined;
2596
+ }
2597
+ function readConfig() {
2598
+ try {
2599
+ const parsed = JSON.parse(readFileSync2(configFile(), "utf8"));
2600
+ return projectConfig(parsed);
2601
+ } catch {
2602
+ return structuredClone(DEFAULT_CONFIG);
2603
+ }
2604
+ }
2605
+ function projectConfig(parsed) {
2606
+ {
2607
+ const mode = parsed.mode?.kind;
2608
+ const daemon = parsed.daemon ?? {};
2609
+ const collector = parsed.collector ?? {};
2610
+ const telemetry = parsed.telemetry ?? {};
2611
+ const audit = parsed.audit ?? {};
2612
+ return {
2613
+ mode: mode === "cloud" ? "cloud" : "oss",
2614
+ daemon: { configured: daemon.configured === true },
2615
+ collector: {
2616
+ sessions: collector.sessions === true,
2617
+ hooks: collector.hooks !== false,
2618
+ hooksVerbosity: collector.hooks_verbosity === "all" || collector.hooks_verbosity === "off" ? collector.hooks_verbosity : "decisions",
2619
+ redact: collector.redact === "off" ? "off" : "minimal",
2620
+ environment: typeof collector.environment === "string" ? collector.environment : "local",
2621
+ machineId: typeof collector.machine_id === "string" ? collector.machine_id : undefined,
2622
+ sources: readSources(collector.sources)
2623
+ },
2624
+ telemetry: { enabled: telemetry.enabled !== false },
2625
+ audit: { auto: audit.auto === true, intervalDays: readIntervalDays(audit.interval_days) }
2626
+ };
2627
+ }
2628
+ }
2629
+ var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG;
2630
+ var init_fp_config = __esm(() => {
2631
+ init_package();
2632
+ init_fp_home();
2633
+ DEFAULT_CONFIG = {
2634
+ mode: "oss",
2635
+ daemon: { configured: false },
2636
+ collector: {
2637
+ sessions: false,
2638
+ hooks: true,
2639
+ hooksVerbosity: "decisions",
2640
+ redact: "minimal",
2641
+ environment: "local"
2642
+ },
2643
+ telemetry: { enabled: true },
2644
+ audit: { auto: false, intervalDays: DEFAULT_AUDIT_INTERVAL_DAYS }
2645
+ };
2646
+ });
2647
+
2648
+ // lib/telemetry-enabled.ts
2649
+ function isTelemetryEnabled() {
2650
+ if (process.env.FAILPROOFAI_TELEMETRY_DISABLED === "1")
2651
+ return false;
2652
+ try {
2653
+ return readConfig().telemetry.enabled;
2654
+ } catch {
2655
+ return true;
2656
+ }
2657
+ }
2658
+ var init_telemetry_enabled = __esm(() => {
2659
+ init_fp_config();
2660
+ });
2661
+
2294
2662
  // src/hooks/hook-telemetry.ts
2295
2663
  async function sendEvent(distinctId, event, properties) {
2296
2664
  const body = JSON.stringify({
@@ -2309,7 +2677,7 @@ async function sendEvent(distinctId, event, properties) {
2309
2677
  } catch {}
2310
2678
  }
2311
2679
  async function trackHookEvent(distinctId, event, properties) {
2312
- if (process.env.FAILPROOFAI_TELEMETRY_DISABLED === "1")
2680
+ if (!isTelemetryEnabled())
2313
2681
  return;
2314
2682
  const p = sendEvent(distinctId, event, properties);
2315
2683
  pending.add(p);
@@ -2324,6 +2692,7 @@ async function flushHookTelemetry() {
2324
2692
  var API_KEY, CAPTURE_URL = "https://us.i.posthog.com/capture/", pending;
2325
2693
  var init_hook_telemetry = __esm(() => {
2326
2694
  init_package();
2695
+ init_telemetry_enabled();
2327
2696
  API_KEY = POSTHOG_API_KEY;
2328
2697
  pending = new Set;
2329
2698
  });
@@ -2387,24 +2756,48 @@ function getFileBasedId() {
2387
2756
  } catch {}
2388
2757
  return id;
2389
2758
  }
2759
+ function publishInstanceId(id) {
2760
+ let target;
2761
+ try {
2762
+ target = telemetryIdFile();
2763
+ if (fs.readFileSync(target, "utf-8").trim() === id)
2764
+ return;
2765
+ } catch {
2766
+ try {
2767
+ target = telemetryIdFile();
2768
+ } catch {
2769
+ return;
2770
+ }
2771
+ }
2772
+ try {
2773
+ fs.mkdirSync(path.dirname(target), { recursive: true });
2774
+ const tmp = `${target}.${process.pid}.tmp`;
2775
+ fs.writeFileSync(tmp, id, { encoding: "utf-8", mode: 384 });
2776
+ fs.renameSync(tmp, target);
2777
+ } catch {}
2778
+ }
2390
2779
  function getInstanceId() {
2391
2780
  if (cachedId)
2392
2781
  return cachedId;
2393
2782
  const machineId = getPlatformMachineId();
2394
2783
  if (machineId) {
2395
2784
  cachedId = hashToId(machineId);
2785
+ publishInstanceId(cachedId);
2396
2786
  return cachedId;
2397
2787
  }
2398
2788
  const sysProps = getSystemPropertiesId();
2399
2789
  if (sysProps) {
2400
2790
  cachedId = hashToId(sysProps);
2791
+ publishInstanceId(cachedId);
2401
2792
  return cachedId;
2402
2793
  }
2403
2794
  cachedId = getFileBasedId();
2795
+ publishInstanceId(cachedId);
2404
2796
  return cachedId;
2405
2797
  }
2406
2798
  var NAMESPACE = "failproofai-telemetry-v1", ID_DIR, ID_FILE, cachedId;
2407
2799
  var init_telemetry_id = __esm(() => {
2800
+ init_fp_home();
2408
2801
  ID_DIR = path.join(os.homedir(), ".failproofai");
2409
2802
  ID_FILE = path.join(ID_DIR, "instance-id");
2410
2803
  });
@@ -3158,13 +3551,43 @@ function clearCustomHooks() {
3158
3551
  const g = globalThis;
3159
3552
  g[REGISTRY_KEY2] = [];
3160
3553
  }
3161
- var REGISTRY_KEY2 = "__failproofai_custom_hooks__";
3162
- var init_custom_hooks_registry = () => {};
3554
+ var REGISTRY_KEY2 = "__failproofai_custom_hooks__", customPolicies;
3555
+ var init_custom_hooks_registry = __esm(() => {
3556
+ customPolicies = {
3557
+ add(hook) {
3558
+ getRegistry2().push(hook);
3559
+ }
3560
+ };
3561
+ });
3163
3562
 
3164
3563
  // src/hooks/loader-utils.ts
3165
- import { readFile as readFile2, writeFile as writeFile2, unlink, access } from "fs/promises";
3166
- import { resolve as resolve3, dirname as dirname2, relative } from "path";
3564
+ import { readFile as readFile2, writeFile as writeFile2, unlink, access, mkdir, lstat } from "fs/promises";
3565
+ import { tmpdir } from "os";
3566
+ import { resolve as resolve4, dirname as dirname2, relative, join as join3 } from "path";
3167
3567
  import { pathToFileURL } from "url";
3568
+ import { createHash } from "crypto";
3569
+ function isTmpArtifact(name) {
3570
+ return TMP_ARTIFACT_RE.test(name);
3571
+ }
3572
+ async function sweepStaleTmpArtifacts(dir, now = Date.now()) {
3573
+ let removed = 0;
3574
+ try {
3575
+ const { readdir, stat } = await import("fs/promises");
3576
+ for (const name of await readdir(dir)) {
3577
+ if (!isTmpArtifact(name))
3578
+ continue;
3579
+ const path2 = resolve4(dir, name);
3580
+ try {
3581
+ const info = await stat(path2);
3582
+ if (now - info.mtimeMs < TMP_SWEEP_MIN_AGE_MS)
3583
+ continue;
3584
+ await unlink(path2);
3585
+ removed++;
3586
+ } catch {}
3587
+ }
3588
+ } catch {}
3589
+ return removed;
3590
+ }
3168
3591
  async function fileExists(path2) {
3169
3592
  try {
3170
3593
  await access(path2);
@@ -3176,14 +3599,14 @@ async function fileExists(path2) {
3176
3599
  async function findDistIndex() {
3177
3600
  const distPath = process.env.FAILPROOFAI_DIST_PATH;
3178
3601
  if (distPath) {
3179
- const candidate = resolve3(distPath, "index.js");
3602
+ const candidate = resolve4(distPath, "index.js");
3180
3603
  if (await fileExists(candidate))
3181
3604
  return candidate;
3182
3605
  }
3183
3606
  const candidates = [
3184
- resolve3(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
3185
- resolve3(process.cwd(), "dist", "index.js"),
3186
- resolve3(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
3607
+ resolve4(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
3608
+ resolve4(process.cwd(), "dist", "index.js"),
3609
+ resolve4(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
3187
3610
  ];
3188
3611
  for (const c of candidates) {
3189
3612
  if (await fileExists(c))
@@ -3192,16 +3615,24 @@ async function findDistIndex() {
3192
3615
  return null;
3193
3616
  }
3194
3617
  async function resolveLocalImport(fromDir, specifier) {
3195
- const base = resolve3(fromDir, specifier);
3196
- const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve3(base, "index.js")];
3618
+ const base = resolve4(fromDir, specifier);
3619
+ const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve4(base, "index.js")];
3197
3620
  for (const c of candidates) {
3198
3621
  if (await fileExists(c))
3199
3622
  return c;
3200
3623
  }
3201
3624
  return null;
3202
3625
  }
3203
- async function createEsmShim(distIndex, distUrl) {
3204
- const shimPath = distIndex + ".__failproofai_esm_shim__.mjs";
3626
+ async function writeShim(dir, name, code, exclusive) {
3627
+ const shimPath = join3(dir, name);
3628
+ await writeFile2(shimPath, code, {
3629
+ encoding: "utf-8",
3630
+ mode: 384,
3631
+ ...exclusive ? { flag: "wx" } : {}
3632
+ });
3633
+ return shimPath;
3634
+ }
3635
+ async function createEsmShim(_distIndex, distUrl, tmpSuffix = TMP_SUFFIX) {
3205
3636
  const shimCode = [
3206
3637
  `import _cjs from '${distUrl}';`,
3207
3638
  `export const customPolicies = _cjs.customPolicies;`,
@@ -3213,16 +3644,30 @@ async function createEsmShim(distIndex, distUrl) {
3213
3644
  `export default _cjs;`
3214
3645
  ].join(`
3215
3646
  `);
3216
- await writeFile2(shimPath, shimCode, "utf-8");
3647
+ const name = `failproofai-dist${tmpSuffix}.shim.mjs`;
3648
+ try {
3649
+ const dir = shimsDir();
3650
+ await mkdir(dir, { recursive: true, mode: 448 });
3651
+ const st = await lstat(dir);
3652
+ const uid = typeof process.getuid === "function" ? process.getuid() : null;
3653
+ if (!st.isDirectory() || st.isSymbolicLink() || uid !== null && st.uid !== uid || st.mode & 63) {
3654
+ throw new Error(`${dir} is not a private directory owned by this user`);
3655
+ }
3656
+ const shimPath2 = await writeShim(dir, name, shimCode, false);
3657
+ return { shimPath: shimPath2, shimUrl: pathToFileURL(shimPath2).href };
3658
+ } catch (err) {
3659
+ hookLogWarn(`policy shim directory unusable (${err instanceof Error ? err.message : String(err)}); falling back to ${tmpdir()}`);
3660
+ }
3661
+ const shimPath = await writeShim(tmpdir(), name, shimCode, true);
3217
3662
  return { shimPath, shimUrl: pathToFileURL(shimPath).href };
3218
3663
  }
3219
- async function rewriteFileTree(entryPath, distUrl, distIndex) {
3664
+ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SUFFIX, entrySha256) {
3220
3665
  const queue = [entryPath];
3221
3666
  const visited = new Set;
3222
3667
  const tmpFiles = [];
3223
3668
  let esmShimUrl = null;
3224
3669
  if (distIndex && distUrl) {
3225
- const shim = await createEsmShim(distIndex, distUrl);
3670
+ const shim = await createEsmShim(distIndex, distUrl, tmpSuffix);
3226
3671
  tmpFiles.push(shim.shimPath);
3227
3672
  esmShimUrl = shim.shimUrl;
3228
3673
  }
@@ -3231,7 +3676,17 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
3231
3676
  if (visited.has(filePath))
3232
3677
  continue;
3233
3678
  visited.add(filePath);
3234
- let code = await readFile2(filePath, "utf-8");
3679
+ let code;
3680
+ if (entrySha256 && filePath === entryPath) {
3681
+ const buf = await readFile2(filePath);
3682
+ const actual = createHash("sha256").update(buf).digest("hex");
3683
+ if (actual !== entrySha256) {
3684
+ throw new Error(`cloud-managed policy failed integrity re-verification at load: expected ${entrySha256}, got ${actual}`);
3685
+ }
3686
+ code = buf.toString("utf-8");
3687
+ } else {
3688
+ code = await readFile2(filePath, "utf-8");
3689
+ }
3235
3690
  if (esmShimUrl) {
3236
3691
  code = code.replace(/from\s+(['"])(?:claudeye|failproofai)\1/g, `from '${esmShimUrl}'`);
3237
3692
  }
@@ -3253,7 +3708,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
3253
3708
  if (!visited.has(resolved2) && !queue.includes(resolved2)) {
3254
3709
  queue.push(resolved2);
3255
3710
  }
3256
- let relPath = relative(dir, resolved2 + TMP_SUFFIX).split("\\").join("/");
3711
+ let relPath = relative(dir, resolved2 + tmpSuffix).split("\\").join("/");
3257
3712
  if (!relPath.startsWith("."))
3258
3713
  relPath = "./" + relPath;
3259
3714
  rewrites.set(specifier, relPath);
@@ -3266,7 +3721,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
3266
3721
  code = code.replace(new RegExp(`'${escaped}'`, "g"), `'${replacement}'`);
3267
3722
  code = code.replace(new RegExp(`"${escaped}"`, "g"), `"${replacement}"`);
3268
3723
  }
3269
- const tmpPath = filePath + TMP_SUFFIX;
3724
+ const tmpPath = filePath + tmpSuffix;
3270
3725
  await writeFile2(tmpPath, code, "utf-8");
3271
3726
  tmpFiles.push(tmpPath);
3272
3727
  }
@@ -3279,36 +3734,61 @@ async function cleanupTmpFiles(tmpFiles) {
3279
3734
  } catch {}
3280
3735
  }
3281
3736
  }
3282
- var TMP_SUFFIX = ".__failproofai_tmp__.mjs", LOCAL_IMPORT_RE, LOCAL_REQUIRE_RE;
3737
+ var TMP_SUFFIX = ".__failproofai_tmp__.mjs", TMP_ARTIFACT_RE, TMP_SWEEP_MIN_AGE_MS = 60000, LOCAL_IMPORT_RE, LOCAL_REQUIRE_RE;
3283
3738
  var init_loader_utils = __esm(() => {
3739
+ init_fp_home();
3740
+ init_hook_logger();
3741
+ TMP_ARTIFACT_RE = /\.__failproofai_tmp__\./;
3284
3742
  LOCAL_IMPORT_RE = /(?:import\s+(?:[\s\S]*?\s+from\s+)?|export\s+(?:[\s\S]*?\s+from\s+))(['"])(\.\.?\/[^'"]+)\1/g;
3285
3743
  LOCAL_REQUIRE_RE = /require\s*\(\s*(['"])(\.\.?\/[^'"]+)\1\s*\)/g;
3286
3744
  });
3287
3745
 
3288
3746
  // src/hooks/custom-hooks-loader.ts
3289
- import { resolve as resolve4, isAbsolute, basename } from "node:path";
3290
- import { existsSync as existsSync3, readdirSync } from "node:fs";
3747
+ import { resolve as resolve5, isAbsolute, basename } from "node:path";
3748
+ import { randomUUID } from "crypto";
3749
+ import { existsSync as existsSync4, readdirSync } from "node:fs";
3750
+ import { readFile as readFile3 } from "node:fs/promises";
3291
3751
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3292
- import { homedir as homedir4 } from "node:os";
3752
+ import { createHash as createHash2 } from "node:crypto";
3293
3753
  function discoverPolicyFiles(dir) {
3294
- if (!existsSync3(dir))
3754
+ if (!existsSync4(dir))
3295
3755
  return [];
3296
3756
  try {
3297
3757
  const entries = readdirSync(dir, { withFileTypes: true });
3298
- return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve4(dir, e.name));
3758
+ return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve5(dir, e.name));
3299
3759
  } catch {
3300
3760
  return [];
3301
3761
  }
3302
3762
  }
3303
3763
  function findSkippedPolicyFiles(dir) {
3304
- if (!existsSync3(dir))
3764
+ if (!existsSync4(dir))
3305
3765
  return [];
3306
3766
  try {
3307
- return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts")).map((e) => e.name).sort((a, b) => a.localeCompare(b));
3767
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts") && !isTmpArtifact(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
3308
3768
  } catch {
3309
3769
  return [];
3310
3770
  }
3311
3771
  }
3772
+ function moduleLoadTimeoutMs() {
3773
+ const override = Number(process.env.FAILPROOFAI_POLICY_LOAD_TIMEOUT_MS);
3774
+ return Number.isFinite(override) && override > 0 ? override : 1e4;
3775
+ }
3776
+ async function importWithDeadline(fileUrl) {
3777
+ const budget = moduleLoadTimeoutMs();
3778
+ let timer;
3779
+ try {
3780
+ await Promise.race([
3781
+ import(fileUrl),
3782
+ new Promise((_, reject) => {
3783
+ timer = setTimeout(() => reject(new Error(`timed out loading module after ${budget}ms`)), budget);
3784
+ timer.unref?.();
3785
+ })
3786
+ ]);
3787
+ } finally {
3788
+ if (timer)
3789
+ clearTimeout(timer);
3790
+ }
3791
+ }
3312
3792
  async function loadSingleFile(absPath, opts) {
3313
3793
  const g = globalThis;
3314
3794
  g[LOADING_KEY] = true;
@@ -3316,13 +3796,29 @@ async function loadSingleFile(absPath, opts) {
3316
3796
  try {
3317
3797
  const distIndex = await findDistIndex();
3318
3798
  const distUrl = distIndex ? pathToFileURL2(distIndex).href : null;
3319
- tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex);
3320
- const entryTmp = absPath + TMP_SUFFIX;
3321
- const fileUrl = `${pathToFileURL2(entryTmp).href}?v=${++loadSequence}`;
3322
- await import(fileUrl);
3799
+ const sequence = ++loadSequence;
3800
+ const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.${randomUUID()}.mjs`;
3801
+ tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex, tmpSuffix, opts?.verifyEntrySha);
3802
+ const fingerprint = await fingerprintTemporaryTree(tmpFiles, tmpSuffix);
3803
+ const cached = policyModuleCache.get(absPath);
3804
+ if (cached?.fingerprint === fingerprint) {
3805
+ for (const hook of cached.hooks)
3806
+ customPolicies.add({ ...hook });
3807
+ return;
3808
+ }
3809
+ const entryTmp = absPath + tmpSuffix;
3810
+ const fileUrl = pathToFileURL2(entryTmp).href;
3811
+ const hooksBefore = getCustomHooks().length;
3812
+ await importWithDeadline(fileUrl);
3813
+ if (policyModuleCache.size >= POLICY_MODULE_CACHE_MAX_ENTRIES)
3814
+ policyModuleCache.clear();
3815
+ policyModuleCache.set(absPath, {
3816
+ fingerprint,
3817
+ hooks: getCustomHooks().slice(hooksBefore).map((hook) => ({ ...hook }))
3818
+ });
3323
3819
  } catch (err) {
3324
3820
  const msg = err instanceof Error ? err.message : String(err);
3325
- const errorType = /Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : "runtime_error";
3821
+ const errorType = /Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : /timed out loading module/i.test(msg) ? "load_timeout" : "runtime_error";
3326
3822
  trackHookEvent(getInstanceId(), "custom_hooks_load_error", {
3327
3823
  error_type: errorType,
3328
3824
  is_convention: !!opts?.conventionScope,
@@ -3337,6 +3833,15 @@ async function loadSingleFile(absPath, opts) {
3337
3833
  await cleanupTmpFiles(tmpFiles);
3338
3834
  }
3339
3835
  }
3836
+ async function fingerprintTemporaryTree(tmpFiles, tmpSuffix) {
3837
+ const hash = createHash2("sha256");
3838
+ for (const tmpFile of [...tmpFiles].sort()) {
3839
+ hash.update(tmpFile.replaceAll(tmpSuffix, TMP_SUFFIX));
3840
+ const contents = await readFile3(tmpFile, "utf8");
3841
+ hash.update(contents.replaceAll(tmpSuffix, TMP_SUFFIX));
3842
+ }
3843
+ return hash.digest("hex");
3844
+ }
3340
3845
  function customPolicyId(file, name) {
3341
3846
  return `custom:${file}:${name}`;
3342
3847
  }
@@ -3356,15 +3861,37 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3356
3861
  const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
3357
3862
  const conventionEnabled = opts?.customPoliciesEnabled !== false;
3358
3863
  const loadedPaths = new Set;
3864
+ const cloudManagedByPath = new Map;
3865
+ for (const policy of opts?.cloudManagedPolicies ?? []) {
3866
+ const key = resolve5(policy.path);
3867
+ const existing = cloudManagedByPath.get(key);
3868
+ if (!existing) {
3869
+ cloudManagedByPath.set(key, policy);
3870
+ continue;
3871
+ }
3872
+ hookLogWarn(`cloud-managed policies ${existing.id} and ${policy.id} have identical source, so they share ` + `one artifact and load as one policy; enforcing it if either asks to enforce`);
3873
+ if (existing.effect !== "enforce" && policy.effect === "enforce") {
3874
+ cloudManagedByPath.set(key, policy);
3875
+ }
3876
+ }
3359
3877
  for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
3360
- const absPath = resolve4(projectRoot, customPoliciesPath);
3361
- if (existsSync3(absPath)) {
3878
+ const absPath = resolve5(projectRoot, customPoliciesPath);
3879
+ if (existsSync4(absPath)) {
3362
3880
  if (!loadedPaths.has(absPath)) {
3363
3881
  loadedPaths.add(absPath);
3364
3882
  const hooksBefore = getCustomHooks().length;
3365
- await loadSingleFile(absPath);
3883
+ await loadSingleFile(absPath, {
3884
+ verifyEntrySha: cloudManagedByPath.get(absPath)?.sha256
3885
+ });
3366
3886
  for (const hook of getCustomHooks().slice(hooksBefore)) {
3367
- hook.__policyId = customPolicyId(absPath, hook.name);
3887
+ const cloudManaged = cloudManagedByPath.get(absPath);
3888
+ const tagged = hook;
3889
+ if (cloudManaged) {
3890
+ tagged.__cloudManaged = cloudManaged;
3891
+ tagged.__policyId = `cloud:${cloudManaged.id}@${cloudManaged.version}:${hook.name}`;
3892
+ } else {
3893
+ tagged.__policyId = customPolicyId(absPath, hook.name);
3894
+ }
3368
3895
  }
3369
3896
  }
3370
3897
  } else {
@@ -3372,29 +3899,35 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3372
3899
  }
3373
3900
  }
3374
3901
  const hooksBeforeConvention = getCustomHooks().length;
3375
- const projectDir = resolve4(projectRoot, ".failproofai", "policies");
3376
- if (conventionEnabled)
3377
- warnSkippedPolicyFiles(projectDir, "project");
3902
+ const projectDir = resolve5(projectRoot, ".failproofai", "policies");
3903
+ const projectScope = projectDir === customPoliciesDir() ? "user" : "project";
3904
+ if (conventionEnabled) {
3905
+ sweepStaleTmpArtifacts(projectDir);
3906
+ warnSkippedPolicyFiles(projectDir, projectScope);
3907
+ }
3908
+ sweepStaleTmpArtifacts(shimsDir());
3378
3909
  const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
3379
3910
  for (const file of projectFiles) {
3380
3911
  loadedPaths.add(file);
3381
3912
  const hooksBefore = getCustomHooks().length;
3382
- await loadSingleFile(file, { conventionScope: "project" });
3913
+ await loadSingleFile(file, { conventionScope: projectScope });
3383
3914
  const newHooks = getCustomHooks().slice(hooksBefore);
3384
3915
  for (const hook of newHooks) {
3385
- hook.__policyId = conventionPolicyId("project", basename(file), hook.name);
3916
+ hook.__policyId = conventionPolicyId(projectScope, basename(file), hook.name);
3386
3917
  }
3387
3918
  if (newHooks.length > 0) {
3388
3919
  conventionSources.push({
3389
- scope: "project",
3920
+ scope: projectScope,
3390
3921
  file: basename(file),
3391
3922
  hookNames: newHooks.map((h) => h.name)
3392
3923
  });
3393
3924
  }
3394
3925
  }
3395
- const userDir = resolve4(homedir4(), ".failproofai", "policies");
3396
- if (conventionEnabled && userDir !== projectDir)
3926
+ const userDir = customPoliciesDir();
3927
+ if (conventionEnabled && userDir !== projectDir) {
3928
+ sweepStaleTmpArtifacts(userDir);
3397
3929
  warnSkippedPolicyFiles(userDir, "user");
3930
+ }
3398
3931
  const userFiles = conventionEnabled && userDir !== projectDir ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f)) : [];
3399
3932
  for (const file of userFiles) {
3400
3933
  loadedPaths.add(file);
@@ -3436,7 +3969,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3436
3969
  }
3437
3970
  return { hooks: allHooks, conventionSources };
3438
3971
  }
3439
- var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, LOADABLE_EXT_RE;
3972
+ var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE;
3440
3973
  var init_custom_hooks_loader = __esm(() => {
3441
3974
  init_hook_logger();
3442
3975
  init_custom_hooks_registry();
@@ -3444,44 +3977,45 @@ var init_custom_hooks_loader = __esm(() => {
3444
3977
  init_hooks_config();
3445
3978
  init_hook_telemetry();
3446
3979
  init_telemetry_id();
3980
+ init_fp_home();
3447
3981
  CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
3982
+ policyModuleCache = new Map;
3448
3983
  LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
3449
3984
  });
3450
3985
 
3451
3986
  // src/hooks/hook-activity-store.ts
3452
3987
  import {
3453
- readFileSync as readFileSync2,
3454
- writeFileSync as writeFileSync2,
3988
+ readFileSync as readFileSync3,
3989
+ writeFileSync as writeFileSync3,
3455
3990
  appendFileSync as appendFileSync2,
3456
3991
  renameSync as renameSync2,
3457
3992
  readdirSync as readdirSync2,
3458
- mkdirSync as mkdirSync3,
3459
- existsSync as existsSync4,
3460
- statSync as statSync4,
3993
+ mkdirSync as mkdirSync4,
3994
+ existsSync as existsSync5,
3995
+ statSync as statSync5,
3461
3996
  unlinkSync
3462
3997
  } from "node:fs";
3463
- import { join as join3 } from "node:path";
3464
- import { homedir as homedir5 } from "node:os";
3998
+ import { join as join4 } from "node:path";
3465
3999
  function ensureDir() {
3466
- if (!existsSync4(storeDir)) {
3467
- mkdirSync3(storeDir, { recursive: true });
4000
+ if (!existsSync5(storeDirValue())) {
4001
+ mkdirSync4(storeDirValue(), { recursive: true });
3468
4002
  }
3469
4003
  }
3470
4004
  function acquireLock() {
3471
4005
  ensureDir();
3472
- const lockPath = join3(storeDir, LOCK_FILE);
4006
+ const lockPath = join4(storeDirValue(), LOCK_FILE);
3473
4007
  const deadline = Date.now() + LOCK_STALE_MS;
3474
4008
  while (Date.now() < deadline) {
3475
4009
  try {
3476
- writeFileSync2(lockPath, String(process.pid), { flag: "wx" });
4010
+ writeFileSync3(lockPath, String(process.pid), { flag: "wx" });
3477
4011
  return;
3478
4012
  } catch (e) {
3479
4013
  if (e.code !== "EEXIST")
3480
4014
  return;
3481
4015
  try {
3482
- const s = statSync4(lockPath);
4016
+ const s = statSync5(lockPath);
3483
4017
  if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
3484
- writeFileSync2(lockPath, String(process.pid), "utf-8");
4018
+ writeFileSync3(lockPath, String(process.pid), "utf-8");
3485
4019
  return;
3486
4020
  }
3487
4021
  } catch {}
@@ -3490,15 +4024,15 @@ function acquireLock() {
3490
4024
  }
3491
4025
  function releaseLock() {
3492
4026
  try {
3493
- unlinkSync(join3(storeDir, LOCK_FILE));
4027
+ unlinkSync(join4(storeDirValue(), LOCK_FILE));
3494
4028
  } catch {}
3495
4029
  }
3496
4030
  function persistHookActivity(entry) {
3497
4031
  ensureDir();
3498
4032
  acquireLock();
3499
4033
  try {
3500
- const currentPath = join3(storeDir, CURRENT_FILE);
3501
- const countPath = join3(storeDir, COUNT_FILE);
4034
+ const currentPath = join4(storeDirValue(), CURRENT_FILE);
4035
+ const countPath = join4(storeDirValue(), COUNT_FILE);
3502
4036
  const lineCount = readCount(countPath);
3503
4037
  if (lineCount >= PAGE_SIZE) {
3504
4038
  try {
@@ -3518,13 +4052,13 @@ function persistHookActivity(entry) {
3518
4052
  }
3519
4053
  function rotate(currentPath, countPath) {
3520
4054
  const archiveName = `page-${Date.now()}-${rotateSeq++}.jsonl`;
3521
- const archivePath = join3(storeDir, archiveName);
4055
+ const archivePath = join4(storeDirValue(), archiveName);
3522
4056
  renameSync2(currentPath, archivePath);
3523
4057
  writeCount(countPath, 0);
3524
4058
  }
3525
4059
  function readCount(countPath) {
3526
4060
  try {
3527
- const n = parseInt(readFileSync2(countPath, "utf-8"), 10);
4061
+ const n = parseInt(readFileSync3(countPath, "utf-8"), 10);
3528
4062
  return isNaN(n) ? 0 : n;
3529
4063
  } catch {
3530
4064
  return 0;
@@ -3532,18 +4066,17 @@ function readCount(countPath) {
3532
4066
  }
3533
4067
  function writeCount(countPath, n) {
3534
4068
  try {
3535
- writeFileSync2(countPath, String(n), "utf-8");
4069
+ writeFileSync3(countPath, String(n), "utf-8");
3536
4070
  } catch {}
3537
4071
  }
3538
4072
  function readStoredStats() {
3539
4073
  try {
3540
- return JSON.parse(readFileSync2(join3(storeDir, STATS_FILE), "utf-8"));
4074
+ return JSON.parse(readFileSync3(join4(storeDirValue(), STATS_FILE), "utf-8"));
3541
4075
  } catch {
3542
4076
  return { totalEvents: 0, denyCount: 0, policyMap: {} };
3543
4077
  }
3544
4078
  }
3545
- function updateStats(entry) {
3546
- const s = readStoredStats();
4079
+ function foldEntry(s, entry) {
3547
4080
  s.totalEvents += 1;
3548
4081
  if (entry.decision === "deny")
3549
4082
  s.denyCount += 1;
@@ -3554,20 +4087,28 @@ function updateStats(entry) {
3554
4087
  } else if (entry.policyName) {
3555
4088
  s.policyMap[entry.policyName] = (s.policyMap[entry.policyName] ?? 0) + 1;
3556
4089
  }
3557
- const tmpPath = join3(storeDir, `stats.json.${process.pid}.tmp`);
4090
+ }
4091
+ function writeStoredStats(s) {
4092
+ const tmpPath = join4(storeDirValue(), `stats.json.${process.pid}.tmp`);
3558
4093
  try {
3559
- writeFileSync2(tmpPath, JSON.stringify(s), "utf-8");
3560
- renameSync2(tmpPath, join3(storeDir, STATS_FILE));
4094
+ writeFileSync3(tmpPath, JSON.stringify(s), "utf-8");
4095
+ renameSync2(tmpPath, join4(storeDirValue(), STATS_FILE));
4096
+ return true;
3561
4097
  } catch {
3562
4098
  try {
3563
4099
  unlinkSync(tmpPath);
3564
4100
  } catch {}
4101
+ return false;
3565
4102
  }
3566
4103
  }
3567
- var PAGE_SIZE = 25, DEFAULT_STORE_DIR, CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDir, rotateSeq = 0;
4104
+ function updateStats(entry) {
4105
+ const s = readStoredStats();
4106
+ foldEntry(s, entry);
4107
+ writeStoredStats(s);
4108
+ }
4109
+ var PAGE_SIZE = 25, defaultStoreDir = () => hookActivityDir(), CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDirOverride = null, storeDirValue = () => storeDirOverride ?? defaultStoreDir(), rotateSeq = 0;
3568
4110
  var init_hook_activity_store = __esm(() => {
3569
- DEFAULT_STORE_DIR = join3(homedir5(), ".failproofai", "cache", "hook-activity");
3570
- storeDir = DEFAULT_STORE_DIR;
4111
+ init_fp_home();
3571
4112
  });
3572
4113
 
3573
4114
  // src/hooks/resolve-cwd.ts
@@ -3616,10 +4157,10 @@ function runtimeCache(fn, revalidateSeconds, options) {
3616
4157
  }
3617
4158
 
3618
4159
  // lib/paths.ts
3619
- import { homedir as homedir6 } from "os";
3620
- import { join as join4 } from "path";
4160
+ import { homedir as homedir4 } from "os";
4161
+ import { join as join5 } from "path";
3621
4162
  function getDefaultClaudeProjectsPath() {
3622
- return join4(homedir6(), ".claude", "projects");
4163
+ return join5(homedir4(), ".claude", "projects");
3623
4164
  }
3624
4165
  function decodeFolderName(name) {
3625
4166
  if (/^[A-Za-z]--/.test(name)) {
@@ -3712,9 +4253,9 @@ __export(exports_codex_projects, {
3712
4253
  getCachedCodexSessionsByEncodedName: () => getCachedCodexSessionsByEncodedName,
3713
4254
  getCachedCodexProjects: () => getCachedCodexProjects
3714
4255
  });
3715
- import { open as open2, readdir } from "node:fs/promises";
3716
- import { homedir as homedir7 } from "node:os";
3717
- import { join as join5 } from "node:path";
4256
+ import { open, readdir } from "node:fs/promises";
4257
+ import { homedir as homedir5 } from "node:os";
4258
+ import { join as join6 } from "node:path";
3718
4259
  async function safeReaddir(dir) {
3719
4260
  try {
3720
4261
  return await readdir(dir, { withFileTypes: true });
@@ -3725,7 +4266,7 @@ async function safeReaddir(dir) {
3725
4266
  async function readFirstLine(filePath) {
3726
4267
  let fh = null;
3727
4268
  try {
3728
- fh = await open2(filePath, "r");
4269
+ fh = await open(filePath, "r");
3729
4270
  const buf = Buffer.alloc(FIRST_LINE_CHUNK_BYTES);
3730
4271
  const { bytesRead } = await fh.read(buf, 0, FIRST_LINE_CHUNK_BYTES, 0);
3731
4272
  if (bytesRead === 0)
@@ -3762,7 +4303,7 @@ async function listJsonlFiles(dir) {
3762
4303
  const entries = await safeReaddir(dir);
3763
4304
  if (!entries)
3764
4305
  return [];
3765
- return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => join5(dir, e.name));
4306
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => join6(dir, e.name));
3766
4307
  }
3767
4308
  async function scanCodexSessions() {
3768
4309
  const yearDirs = await safeReaddir(CODEX_SESSIONS_ROOT);
@@ -3772,19 +4313,19 @@ async function scanCodexSessions() {
3772
4313
  for (const y of yearDirs) {
3773
4314
  if (!y.isDirectory())
3774
4315
  continue;
3775
- const monthDirs = await safeReaddir(join5(CODEX_SESSIONS_ROOT, y.name));
4316
+ const monthDirs = await safeReaddir(join6(CODEX_SESSIONS_ROOT, y.name));
3776
4317
  if (!monthDirs)
3777
4318
  continue;
3778
4319
  for (const m of monthDirs) {
3779
4320
  if (!m.isDirectory())
3780
4321
  continue;
3781
- const dayDirs = await safeReaddir(join5(CODEX_SESSIONS_ROOT, y.name, m.name));
4322
+ const dayDirs = await safeReaddir(join6(CODEX_SESSIONS_ROOT, y.name, m.name));
3782
4323
  if (!dayDirs)
3783
4324
  continue;
3784
4325
  for (const d of dayDirs) {
3785
4326
  if (!d.isDirectory())
3786
4327
  continue;
3787
- const dayPath = join5(CODEX_SESSIONS_ROOT, y.name, m.name, d.name);
4328
+ const dayPath = join6(CODEX_SESSIONS_ROOT, y.name, m.name, d.name);
3788
4329
  filePaths.push(...await listJsonlFiles(dayPath));
3789
4330
  }
3790
4331
  }
@@ -3803,10 +4344,10 @@ async function scanCodexSessions() {
3803
4344
  return null;
3804
4345
  let fileMtime;
3805
4346
  try {
3806
- const fh = await open2(filePath, "r");
4347
+ const fh = await open(filePath, "r");
3807
4348
  try {
3808
- const stat2 = await fh.stat();
3809
- fileMtime = stat2.mtime;
4349
+ const stat = await fh.stat();
4350
+ fileMtime = stat.mtime;
3810
4351
  } finally {
3811
4352
  await fh.close().catch(() => {});
3812
4353
  }
@@ -3892,7 +4433,7 @@ var CODEX_SESSIONS_ROOT, SESSION_ID_RE, FIRST_LINE_CHUNK_BYTES, cachedScan, getC
3892
4433
  var init_codex_projects = __esm(() => {
3893
4434
  init_paths();
3894
4435
  init_logger();
3895
- CODEX_SESSIONS_ROOT = join5(homedir7(), ".codex", "sessions");
4436
+ CODEX_SESSIONS_ROOT = join6(homedir5(), ".codex", "sessions");
3896
4437
  SESSION_ID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
3897
4438
  FIRST_LINE_CHUNK_BYTES = 256 * 1024;
3898
4439
  cachedScan = runtimeCache(scanCodexSessions, 30);
@@ -3911,11 +4452,11 @@ __export(exports_copilot_projects, {
3911
4452
  getCachedCopilotSessionsByEncodedName: () => getCachedCopilotSessionsByEncodedName,
3912
4453
  getCachedCopilotProjects: () => getCachedCopilotProjects
3913
4454
  });
3914
- import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
3915
- import { homedir as homedir8 } from "node:os";
3916
- import { join as join6 } from "node:path";
4455
+ import { readdir as readdir2, readFile as readFile4, stat } from "node:fs/promises";
4456
+ import { homedir as homedir6 } from "node:os";
4457
+ import { join as join7 } from "node:path";
3917
4458
  function getCopilotSessionStateRoot() {
3918
- return join6(process.env.COPILOT_HOME || join6(homedir8(), ".copilot"), "session-state");
4459
+ return join7(process.env.COPILOT_HOME || join7(homedir6(), ".copilot"), "session-state");
3919
4460
  }
3920
4461
  async function safeReaddir2(dir) {
3921
4462
  try {
@@ -3932,7 +4473,7 @@ function parseCwdFromWorkspace(text) {
3932
4473
  }
3933
4474
  async function statMtime(path2) {
3934
4475
  try {
3935
- return (await stat2(path2)).mtime;
4476
+ return (await stat(path2)).mtime;
3936
4477
  } catch {
3937
4478
  return null;
3938
4479
  }
@@ -3944,13 +4485,13 @@ async function scanCopilotSessions() {
3944
4485
  return [];
3945
4486
  const candidates = entries.filter((e) => e.isDirectory()).map((e) => ({
3946
4487
  sessionId: e.name,
3947
- workspacePath: join6(root, e.name, "workspace.yaml"),
3948
- eventsPath: join6(root, e.name, "events.jsonl")
4488
+ workspacePath: join7(root, e.name, "workspace.yaml"),
4489
+ eventsPath: join7(root, e.name, "events.jsonl")
3949
4490
  }));
3950
4491
  const settled = await batchAll(candidates.map((c) => async () => {
3951
4492
  let workspaceText;
3952
4493
  try {
3953
- workspaceText = await readFile3(c.workspacePath, "utf-8");
4494
+ workspaceText = await readFile4(c.workspacePath, "utf-8");
3954
4495
  } catch {
3955
4496
  return null;
3956
4497
  }
@@ -4059,11 +4600,11 @@ __export(exports_cursor_projects, {
4059
4600
  getCachedCursorSessionsByEncodedName: () => getCachedCursorSessionsByEncodedName,
4060
4601
  getCachedCursorProjects: () => getCachedCursorProjects
4061
4602
  });
4062
- import { readdir as readdir3, readFile as readFile4, stat as stat3 } from "node:fs/promises";
4063
- import { homedir as homedir9 } from "node:os";
4064
- import { join as join7 } from "node:path";
4603
+ import { readdir as readdir3, readFile as readFile5, stat as stat2 } from "node:fs/promises";
4604
+ import { homedir as homedir7 } from "node:os";
4605
+ import { join as join8 } from "node:path";
4065
4606
  function getCursorHome() {
4066
- return process.env.CURSOR_HOME || join7(homedir9(), ".cursor");
4607
+ return process.env.CURSOR_HOME || join8(homedir7(), ".cursor");
4067
4608
  }
4068
4609
  async function safeReaddir3(dir) {
4069
4610
  try {
@@ -4074,7 +4615,7 @@ async function safeReaddir3(dir) {
4074
4615
  }
4075
4616
  async function statMtime2(path2) {
4076
4617
  try {
4077
- return (await stat3(path2)).mtime;
4618
+ return (await stat2(path2)).mtime;
4078
4619
  } catch {
4079
4620
  return null;
4080
4621
  }
@@ -4095,12 +4636,12 @@ function parseCwdFromMetaText(text) {
4095
4636
  }
4096
4637
  async function findFirstUsableMeta(dir, candidates) {
4097
4638
  for (const name of candidates) {
4098
- const path2 = join7(dir, name);
4639
+ const path2 = join8(dir, name);
4099
4640
  if (await statMtime2(path2) === null)
4100
4641
  continue;
4101
4642
  let text;
4102
4643
  try {
4103
- text = await readFile4(path2, "utf-8");
4644
+ text = await readFile5(path2, "utf-8");
4104
4645
  } catch {
4105
4646
  continue;
4106
4647
  }
@@ -4112,7 +4653,7 @@ async function findFirstUsableMeta(dir, candidates) {
4112
4653
  }
4113
4654
  async function findFirstExistingPath(dir, candidates) {
4114
4655
  for (const name of candidates) {
4115
- const path2 = join7(dir, name);
4656
+ const path2 = join8(dir, name);
4116
4657
  if (await statMtime2(path2) !== null)
4117
4658
  return path2;
4118
4659
  }
@@ -4122,7 +4663,7 @@ async function scanCursorSessions() {
4122
4663
  const home = getCursorHome();
4123
4664
  const newCandidates = [];
4124
4665
  const legacyCandidates = [];
4125
- const projectsRoot = join7(home, NEW_PROJECTS_DIR);
4666
+ const projectsRoot = join8(home, NEW_PROJECTS_DIR);
4126
4667
  const projectEntries = await safeReaddir3(projectsRoot);
4127
4668
  if (projectEntries) {
4128
4669
  for (const proj of projectEntries) {
@@ -4130,7 +4671,7 @@ async function scanCursorSessions() {
4130
4671
  continue;
4131
4672
  const decoded = decodeFolderName(proj.name);
4132
4673
  const cwd = decoded.startsWith("/") || /^[A-Za-z]:\//.test(decoded) ? decoded : `/${decoded}`;
4133
- const transcriptsRoot = join7(projectsRoot, proj.name, NEW_AGENT_TRANSCRIPTS_DIR);
4674
+ const transcriptsRoot = join8(projectsRoot, proj.name, NEW_AGENT_TRANSCRIPTS_DIR);
4134
4675
  const sessionDirs = await safeReaddir3(transcriptsRoot);
4135
4676
  if (!sessionDirs)
4136
4677
  continue;
@@ -4139,28 +4680,28 @@ async function scanCursorSessions() {
4139
4680
  continue;
4140
4681
  newCandidates.push({
4141
4682
  sessionId: sd.name,
4142
- dir: join7(transcriptsRoot, sd.name),
4683
+ dir: join8(transcriptsRoot, sd.name),
4143
4684
  cwd
4144
4685
  });
4145
4686
  }
4146
4687
  }
4147
4688
  }
4148
4689
  for (const sub of LEGACY_SESSION_ROOT_CANDIDATES) {
4149
- const root = join7(home, sub);
4690
+ const root = join8(home, sub);
4150
4691
  const entries = await safeReaddir3(root);
4151
4692
  if (!entries)
4152
4693
  continue;
4153
4694
  for (const e of entries) {
4154
4695
  if (!e.isDirectory())
4155
4696
  continue;
4156
- legacyCandidates.push({ sessionId: e.name, dir: join7(root, e.name) });
4697
+ legacyCandidates.push({ sessionId: e.name, dir: join8(root, e.name) });
4157
4698
  }
4158
4699
  }
4159
4700
  if (newCandidates.length === 0 && legacyCandidates.length === 0)
4160
4701
  return [];
4161
4702
  const settled = await batchAll([
4162
4703
  ...newCandidates.map((c) => async () => {
4163
- const transcriptPath = join7(c.dir, `${c.sessionId}.jsonl`);
4704
+ const transcriptPath = join8(c.dir, `${c.sessionId}.jsonl`);
4164
4705
  const transcriptMtime = await statMtime2(transcriptPath);
4165
4706
  if (!transcriptMtime)
4166
4707
  return null;
@@ -4432,11 +4973,11 @@ __export(exports_pi_projects, {
4432
4973
  getCachedPiSessionsByEncodedName: () => getCachedPiSessionsByEncodedName,
4433
4974
  getCachedPiProjects: () => getCachedPiProjects
4434
4975
  });
4435
- import { readdir as readdir4, readFile as readFile5, stat as stat4 } from "node:fs/promises";
4436
- import { homedir as homedir10 } from "node:os";
4437
- import { join as join8 } from "node:path";
4976
+ import { readdir as readdir4, readFile as readFile6, stat as stat3 } from "node:fs/promises";
4977
+ import { homedir as homedir8 } from "node:os";
4978
+ import { join as join9 } from "node:path";
4438
4979
  function getPiSessionsRoot() {
4439
- return process.env.PI_SESSIONS_DIR || join8(homedir10(), ".pi", "agent", "sessions");
4980
+ return process.env.PI_SESSIONS_DIR || join9(homedir8(), ".pi", "agent", "sessions");
4440
4981
  }
4441
4982
  async function safeReaddir4(dir) {
4442
4983
  try {
@@ -4447,7 +4988,7 @@ async function safeReaddir4(dir) {
4447
4988
  }
4448
4989
  async function statMtime3(path2) {
4449
4990
  try {
4450
- return (await stat4(path2)).mtime;
4991
+ return (await stat3(path2)).mtime;
4451
4992
  } catch {
4452
4993
  return null;
4453
4994
  }
@@ -4455,7 +4996,7 @@ async function statMtime3(path2) {
4455
4996
  async function readSessionCwd(filePath) {
4456
4997
  let text;
4457
4998
  try {
4458
- text = await readFile5(filePath, "utf-8");
4999
+ text = await readFile6(filePath, "utf-8");
4459
5000
  } catch {
4460
5001
  return null;
4461
5002
  }
@@ -4484,7 +5025,7 @@ async function scanPiSessions() {
4484
5025
  for (const cwdDir of cwdDirs) {
4485
5026
  if (!cwdDir.isDirectory())
4486
5027
  continue;
4487
- const cwdPath = join8(root, cwdDir.name);
5028
+ const cwdPath = join9(root, cwdDir.name);
4488
5029
  const sessionFiles = await safeReaddir4(cwdPath);
4489
5030
  if (!sessionFiles)
4490
5031
  continue;
@@ -4494,7 +5035,7 @@ async function scanPiSessions() {
4494
5035
  const m = SESSION_FILE_RE.exec(f.name);
4495
5036
  if (!m)
4496
5037
  continue;
4497
- candidates.push({ sessionId: m[1], filePath: join8(cwdPath, f.name) });
5038
+ candidates.push({ sessionId: m[1], filePath: join9(cwdPath, f.name) });
4498
5039
  }
4499
5040
  }
4500
5041
  if (candidates.length === 0)
@@ -4596,7 +5137,7 @@ var init_pi_projects = __esm(() => {
4596
5137
  });
4597
5138
 
4598
5139
  // lib/sqlite-reader.ts
4599
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
5140
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
4600
5141
  import initSqlJs from "sql.js/dist/sql-asm.js";
4601
5142
  async function tryNodeSqlite(dbPath) {
4602
5143
  try {
@@ -4623,7 +5164,7 @@ function loadSqlJs() {
4623
5164
  async function trySqlJs(dbPath) {
4624
5165
  try {
4625
5166
  const SQL = await loadSqlJs();
4626
- const db = new SQL.Database(readFileSync3(dbPath));
5167
+ const db = new SQL.Database(readFileSync4(dbPath));
4627
5168
  return {
4628
5169
  query(sql, params) {
4629
5170
  const stmt = db.prepare(sql);
@@ -4647,7 +5188,7 @@ async function trySqlJs(dbPath) {
4647
5188
  }
4648
5189
  }
4649
5190
  async function openSqliteReadonly(dbPath) {
4650
- if (!existsSync5(dbPath))
5191
+ if (!existsSync6(dbPath))
4651
5192
  return null;
4652
5193
  return await tryNodeSqlite(dbPath) ?? await trySqlJs(dbPath);
4653
5194
  }
@@ -4656,18 +5197,18 @@ var init_sqlite_reader = () => {};
4656
5197
 
4657
5198
  // lib/hermes-profiles.ts
4658
5199
  import { readdirSync as readdirSync3 } from "node:fs";
4659
- import { homedir as homedir11 } from "node:os";
4660
- import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve5 } from "node:path";
5200
+ import { homedir as homedir9 } from "node:os";
5201
+ import { basename as basename2, dirname as dirname3, join as join10, resolve as resolve6 } from "node:path";
4661
5202
  function hermesRoot() {
4662
5203
  const env = (process.env.HERMES_HOME || "").trim();
4663
5204
  if (env) {
4664
- const home = resolve5(env);
5205
+ const home = resolve6(env);
4665
5206
  const parent = dirname3(home);
4666
5207
  if (basename2(parent) === PROFILES_DIR)
4667
5208
  return dirname3(parent);
4668
5209
  return home;
4669
5210
  }
4670
- return join9(homedir11(), ".hermes");
5211
+ return join10(homedir9(), ".hermes");
4671
5212
  }
4672
5213
  function listHermesProfiles() {
4673
5214
  const root = hermesRoot();
@@ -4675,7 +5216,7 @@ function listHermesProfiles() {
4675
5216
  const seen = new Set([HERMES_DEFAULT_PROFILE]);
4676
5217
  let names;
4677
5218
  try {
4678
- names = readdirSync3(join9(root, PROFILES_DIR), { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name).sort();
5219
+ names = readdirSync3(join10(root, PROFILES_DIR), { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name).sort();
4679
5220
  } catch {
4680
5221
  return out;
4681
5222
  }
@@ -4683,7 +5224,7 @@ function listHermesProfiles() {
4683
5224
  if (seen.has(name))
4684
5225
  continue;
4685
5226
  seen.add(name);
4686
- out.push({ name, home: join9(root, PROFILES_DIR, name) });
5227
+ out.push({ name, home: join10(root, PROFILES_DIR, name) });
4687
5228
  }
4688
5229
  return out;
4689
5230
  }
@@ -4734,14 +5275,14 @@ function formatDuration(ms) {
4734
5275
  }
4735
5276
 
4736
5277
  // lib/hermes-sessions.ts
4737
- import { join as join10 } from "node:path";
5278
+ import { join as join11 } from "node:path";
4738
5279
  function hermesDbPaths() {
4739
5280
  const override = process.env.HERMES_DB_PATH;
4740
5281
  if (override)
4741
5282
  return [{ profile: HERMES_DEFAULT_PROFILE, dbPath: override }];
4742
5283
  return listHermesProfiles().map((p) => ({
4743
5284
  profile: p.name,
4744
- dbPath: join10(p.home, "state.db")
5285
+ dbPath: join11(p.home, "state.db")
4745
5286
  }));
4746
5287
  }
4747
5288
  function epochToMs(value) {
@@ -5037,12 +5578,12 @@ var init_hermes_projects = __esm(() => {
5037
5578
  });
5038
5579
 
5039
5580
  // lib/openclaw-sessions.ts
5040
- import { readFile as readFile6 } from "node:fs/promises";
5041
- import { readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
5042
- import { homedir as homedir12 } from "node:os";
5043
- import { join as join11 } from "node:path";
5581
+ import { readFile as readFile7 } from "node:fs/promises";
5582
+ import { readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
5583
+ import { homedir as homedir10 } from "node:os";
5584
+ import { join as join12 } from "node:path";
5044
5585
  function openclawHome() {
5045
- return process.env.OPENCLAW_HOME || join11(homedir12(), ".openclaw");
5586
+ return process.env.OPENCLAW_HOME || join12(homedir10(), ".openclaw");
5046
5587
  }
5047
5588
  function isPlainObject2(v) {
5048
5589
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -5157,13 +5698,13 @@ function openclawLinesToLogEntries(lines, source = "session") {
5157
5698
  }
5158
5699
  function listOpenClawAgents() {
5159
5700
  try {
5160
- return readdirSync4(join11(openclawHome(), "agents"), { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort();
5701
+ return readdirSync4(join12(openclawHome(), "agents"), { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).map((d) => d.name).sort();
5161
5702
  } catch {
5162
5703
  return [];
5163
5704
  }
5164
5705
  }
5165
5706
  function listOpenClawTranscripts() {
5166
- const agentsDir = join11(openclawHome(), "agents");
5707
+ const agentsDir = join12(openclawHome(), "agents");
5167
5708
  const out = [];
5168
5709
  let agentIds;
5169
5710
  try {
@@ -5172,7 +5713,7 @@ function listOpenClawTranscripts() {
5172
5713
  return out;
5173
5714
  }
5174
5715
  for (const agentId of agentIds) {
5175
- const sessionsDir = join11(agentsDir, agentId, "sessions");
5716
+ const sessionsDir = join12(agentsDir, agentId, "sessions");
5176
5717
  let files;
5177
5718
  try {
5178
5719
  files = readdirSync4(sessionsDir);
@@ -5185,9 +5726,9 @@ function listOpenClawTranscripts() {
5185
5726
  const sessionId = file.slice(0, -".jsonl".length);
5186
5727
  if (!OPENCLAW_SESSION_ID_RE.test(sessionId))
5187
5728
  continue;
5188
- const transcriptPath = join11(sessionsDir, file);
5729
+ const transcriptPath = join12(sessionsDir, file);
5189
5730
  try {
5190
- const st = statSync5(transcriptPath);
5731
+ const st = statSync6(transcriptPath);
5191
5732
  out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
5192
5733
  } catch {}
5193
5734
  }
@@ -5209,7 +5750,7 @@ async function getOpenClawSessionLog(sessionId) {
5209
5750
  return null;
5210
5751
  let content;
5211
5752
  try {
5212
- content = await readFile6(filePath, "utf-8");
5753
+ content = await readFile7(filePath, "utf-8");
5213
5754
  } catch {
5214
5755
  return null;
5215
5756
  }
@@ -5245,17 +5786,17 @@ __export(exports_openclaw_projects, {
5245
5786
  getCachedOpenClawSessions: () => getCachedOpenClawSessions,
5246
5787
  getCachedOpenClawProjects: () => getCachedOpenClawProjects
5247
5788
  });
5248
- import { readFileSync as readFileSync4 } from "node:fs";
5249
- import { join as join12 } from "node:path";
5789
+ import { readFileSync as readFileSync5 } from "node:fs";
5790
+ import { join as join13 } from "node:path";
5250
5791
  function str(v) {
5251
5792
  return typeof v === "string" && v.length > 0 ? v : undefined;
5252
5793
  }
5253
5794
  function readSessionsIndex(agentId) {
5254
5795
  const out = new Map;
5255
- const indexPath = join12(openclawHome(), "agents", agentId, "sessions", "sessions.json");
5796
+ const indexPath = join13(openclawHome(), "agents", agentId, "sessions", "sessions.json");
5256
5797
  let raw;
5257
5798
  try {
5258
- raw = JSON.parse(readFileSync4(indexPath, "utf-8"));
5799
+ raw = JSON.parse(readFileSync5(indexPath, "utf-8"));
5259
5800
  } catch {
5260
5801
  return out;
5261
5802
  }
@@ -5406,15 +5947,15 @@ var init_openclaw_projects = __esm(() => {
5406
5947
  });
5407
5948
 
5408
5949
  // lib/factory-sessions.ts
5409
- import { readFile as readFile7 } from "node:fs/promises";
5410
- import { readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
5411
- import { homedir as homedir13 } from "node:os";
5412
- import { join as join13 } from "node:path";
5950
+ import { readFile as readFile8 } from "node:fs/promises";
5951
+ import { readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
5952
+ import { homedir as homedir11 } from "node:os";
5953
+ import { join as join14 } from "node:path";
5413
5954
  function factoryHome() {
5414
- return process.env.FACTORY_HOME || join13(homedir13(), ".factory");
5955
+ return process.env.FACTORY_HOME || join14(homedir11(), ".factory");
5415
5956
  }
5416
5957
  function factorySessionsRoot() {
5417
- return join13(factoryHome(), "sessions");
5958
+ return join14(factoryHome(), "sessions");
5418
5959
  }
5419
5960
  function isPlainObject3(v) {
5420
5961
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -5560,7 +6101,7 @@ function listFactoryTranscripts() {
5560
6101
  for (const dir of projectDirs) {
5561
6102
  const projectName = dir.name;
5562
6103
  const cwd = decodeFolderName(projectName);
5563
- const projectPath = join13(root, projectName);
6104
+ const projectPath = join14(root, projectName);
5564
6105
  let files;
5565
6106
  try {
5566
6107
  files = readdirSync5(projectPath);
@@ -5573,9 +6114,9 @@ function listFactoryTranscripts() {
5573
6114
  const sessionId = file.slice(0, -".jsonl".length);
5574
6115
  if (!FACTORY_SESSION_ID_RE.test(sessionId))
5575
6116
  continue;
5576
- const transcriptPath = join13(projectPath, file);
6117
+ const transcriptPath = join14(projectPath, file);
5577
6118
  try {
5578
- const st = statSync6(transcriptPath);
6119
+ const st = statSync7(transcriptPath);
5579
6120
  out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
5580
6121
  } catch {}
5581
6122
  }
@@ -5597,7 +6138,7 @@ async function getFactorySessionLog(sessionId) {
5597
6138
  return null;
5598
6139
  let content;
5599
6140
  try {
5600
- content = await readFile7(filePath, "utf-8");
6141
+ content = await readFile8(filePath, "utf-8");
5601
6142
  } catch {
5602
6143
  return null;
5603
6144
  }
@@ -5699,13 +6240,13 @@ var init_factory_projects = __esm(() => {
5699
6240
  });
5700
6241
 
5701
6242
  // lib/devin-sessions.ts
5702
- import { homedir as homedir14 } from "node:os";
5703
- import { join as join14 } from "node:path";
6243
+ import { homedir as homedir12 } from "node:os";
6244
+ import { join as join15 } from "node:path";
5704
6245
  function devinHome() {
5705
- return process.env.DEVIN_HOME || join14(homedir14(), ".local", "share", "devin");
6246
+ return process.env.DEVIN_HOME || join15(homedir12(), ".local", "share", "devin");
5706
6247
  }
5707
6248
  function devinDbPath() {
5708
- return process.env.DEVIN_DB_PATH || join14(devinHome(), "cli", "sessions.db");
6249
+ return process.env.DEVIN_DB_PATH || join15(devinHome(), "cli", "sessions.db");
5709
6250
  }
5710
6251
  function devinEpochToMs(value) {
5711
6252
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -6001,18 +6542,18 @@ var init_devin_projects = __esm(() => {
6001
6542
  });
6002
6543
 
6003
6544
  // lib/antigravity-sessions.ts
6004
- import { readFile as readFile8 } from "node:fs/promises";
6005
- import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
6006
- import { homedir as homedir15 } from "node:os";
6007
- import { join as join15 } from "node:path";
6545
+ import { readFile as readFile9 } from "node:fs/promises";
6546
+ import { readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
6547
+ import { homedir as homedir13 } from "node:os";
6548
+ import { join as join16 } from "node:path";
6008
6549
  function antigravityHome() {
6009
- return process.env.ANTIGRAVITY_HOME || join15(homedir15(), ".gemini", "antigravity-cli");
6550
+ return process.env.ANTIGRAVITY_HOME || join16(homedir13(), ".gemini", "antigravity-cli");
6010
6551
  }
6011
6552
  function antigravityBrainRoot() {
6012
- return join15(antigravityHome(), "brain");
6553
+ return join16(antigravityHome(), "brain");
6013
6554
  }
6014
6555
  function antigravityTranscriptRelPath() {
6015
- return join15(".system_generated", "logs", "transcript_full.jsonl");
6556
+ return join16(".system_generated", "logs", "transcript_full.jsonl");
6016
6557
  }
6017
6558
  function isPlainObject5(v) {
6018
6559
  return !!v && typeof v === "object" && !Array.isArray(v);
@@ -6135,9 +6676,9 @@ function listAntigravityTranscripts() {
6135
6676
  const conversationId = dir.name;
6136
6677
  if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
6137
6678
  continue;
6138
- const transcriptPath = join15(root, conversationId, rel);
6679
+ const transcriptPath = join16(root, conversationId, rel);
6139
6680
  try {
6140
- const st = statSync7(transcriptPath);
6681
+ const st = statSync8(transcriptPath);
6141
6682
  if (!st.isFile())
6142
6683
  continue;
6143
6684
  out.push({ conversationId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
@@ -6148,9 +6689,9 @@ function listAntigravityTranscripts() {
6148
6689
  function findAntigravityTranscript(conversationId) {
6149
6690
  if (!ANTIGRAVITY_CONVERSATION_ID_RE.test(conversationId))
6150
6691
  return null;
6151
- const transcriptPath = join15(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
6692
+ const transcriptPath = join16(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
6152
6693
  try {
6153
- if (statSync7(transcriptPath).isFile())
6694
+ if (statSync8(transcriptPath).isFile())
6154
6695
  return transcriptPath;
6155
6696
  } catch {}
6156
6697
  return null;
@@ -6161,7 +6702,7 @@ async function getAntigravitySessionLog(conversationId) {
6161
6702
  return null;
6162
6703
  let content;
6163
6704
  try {
6164
- content = await readFile8(filePath, "utf-8");
6705
+ content = await readFile9(filePath, "utf-8");
6165
6706
  } catch {
6166
6707
  return null;
6167
6708
  }
@@ -6188,9 +6729,9 @@ __export(exports_antigravity_projects, {
6188
6729
  getAntigravityIndex: () => getAntigravityIndex,
6189
6730
  antigravitySummariesDbPath: () => antigravitySummariesDbPath
6190
6731
  });
6191
- import { join as join16 } from "node:path";
6732
+ import { join as join17 } from "node:path";
6192
6733
  function antigravitySummariesDbPath() {
6193
- return join16(antigravityHome(), "conversation_summaries.db");
6734
+ return join17(antigravityHome(), "conversation_summaries.db");
6194
6735
  }
6195
6736
  function parseWorkspaceCwd(raw) {
6196
6737
  if (!raw)
@@ -6333,16 +6874,16 @@ var init_antigravity_projects = __esm(() => {
6333
6874
  });
6334
6875
 
6335
6876
  // lib/goose-sessions.ts
6336
- import { homedir as homedir16 } from "node:os";
6337
- import { join as join17 } from "node:path";
6877
+ import { homedir as homedir14 } from "node:os";
6878
+ import { join as join18 } from "node:path";
6338
6879
  function gooseHome() {
6339
6880
  if (process.env.GOOSE_HOME)
6340
6881
  return process.env.GOOSE_HOME;
6341
6882
  const xdg = process.env.XDG_DATA_HOME;
6342
- return join17(xdg && xdg.length > 0 ? xdg : join17(homedir16(), ".local", "share"), "goose");
6883
+ return join18(xdg && xdg.length > 0 ? xdg : join18(homedir14(), ".local", "share"), "goose");
6343
6884
  }
6344
6885
  function gooseDbPath() {
6345
- return process.env.GOOSE_DB_PATH || join17(gooseHome(), "sessions", "sessions.db");
6886
+ return process.env.GOOSE_DB_PATH || join18(gooseHome(), "sessions", "sessions.db");
6346
6887
  }
6347
6888
  function gooseTimestampToMs(value) {
6348
6889
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -6609,11 +7150,11 @@ var init_goose_projects = __esm(() => {
6609
7150
  });
6610
7151
 
6611
7152
  // lib/projects.ts
6612
- import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
6613
- import { join as join18, resolve as resolve6, sep } from "node:path";
7153
+ import { readdir as readdir5, stat as stat4 } from "node:fs/promises";
7154
+ import { join as join19, resolve as resolve7, sep } from "node:path";
6614
7155
  async function getMtime(path2, label) {
6615
7156
  try {
6616
- return (await stat5(path2)).mtime;
7157
+ return (await stat4(path2)).mtime;
6617
7158
  } catch (error) {
6618
7159
  logWarn(`Failed to stat ${label}:`, error);
6619
7160
  return new Date(0);
@@ -6621,7 +7162,7 @@ async function getMtime(path2, label) {
6621
7162
  }
6622
7163
  async function safeReaddir5(dirPath) {
6623
7164
  try {
6624
- const s = await stat5(dirPath);
7165
+ const s = await stat4(dirPath);
6625
7166
  if (!s.isDirectory())
6626
7167
  return null;
6627
7168
  return await readdir5(dirPath, { withFileTypes: true });
@@ -6636,7 +7177,7 @@ async function getClaudeProjectFolders() {
6636
7177
  if (!entries)
6637
7178
  return [];
6638
7179
  const settled = await batchAll(entries.filter((entry) => entry.isDirectory()).map((entry) => async () => {
6639
- const folderPath = join18(projectsPath, entry.name);
7180
+ const folderPath = join19(projectsPath, entry.name);
6640
7181
  const mtime = await getMtime(folderPath, entry.name);
6641
7182
  return {
6642
7183
  name: entry.name,
@@ -6762,8 +7303,8 @@ function resolveProjectPath(projectName) {
6762
7303
  throw new RangeError("Empty project name");
6763
7304
  if (/^[/\\]/.test(projectName))
6764
7305
  throw new RangeError("Absolute project name");
6765
- const projectsPath = resolve6(getClaudeProjectsPath());
6766
- const candidate = resolve6(join18(projectsPath, projectName));
7306
+ const projectsPath = resolve7(getClaudeProjectsPath());
7307
+ const candidate = resolve7(join19(projectsPath, projectName));
6767
7308
  if (!candidate.startsWith(projectsPath + sep)) {
6768
7309
  throw new RangeError("Project path escapes root");
6769
7310
  }
@@ -6781,7 +7322,7 @@ async function getSessionFiles(projectPath) {
6781
7322
  return [];
6782
7323
  const jsonlEntries = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl") && extractSessionId2(entry.name));
6783
7324
  const settled = await batchAll(jsonlEntries.map((entry) => async () => {
6784
- const filePath = join18(projectPath, entry.name);
7325
+ const filePath = join19(projectPath, entry.name);
6785
7326
  const mtime = await getMtime(filePath, entry.name);
6786
7327
  return {
6787
7328
  name: entry.name,
@@ -6810,13 +7351,13 @@ var init_projects = __esm(() => {
6810
7351
 
6811
7352
  // lib/resolve-subagent-path.ts
6812
7353
  import { access as access2 } from "fs/promises";
6813
- import { join as join19, relative as relative2 } from "path";
7354
+ import { join as join20, relative as relative2 } from "path";
6814
7355
  async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId) {
6815
7356
  const fileName = `agent-${agentId}.jsonl`;
6816
7357
  const candidatePaths = [
6817
- join19(projectsPath, projectName, fileName),
6818
- join19(projectsPath, projectName, sessionId, fileName),
6819
- join19(projectsPath, projectName, sessionId, "subagents", fileName)
7358
+ join20(projectsPath, projectName, fileName),
7359
+ join20(projectsPath, projectName, sessionId, fileName),
7360
+ join20(projectsPath, projectName, sessionId, "subagents", fileName)
6820
7361
  ];
6821
7362
  for (const candidatePath of candidatePaths) {
6822
7363
  if (relative2(projectsPath, candidatePath).startsWith("..")) {
@@ -6837,8 +7378,8 @@ async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId
6837
7378
  var init_resolve_subagent_path = () => {};
6838
7379
 
6839
7380
  // lib/log-entries.ts
6840
- import { readFile as readFile9 } from "fs/promises";
6841
- import { join as join20 } from "path";
7381
+ import { readFile as readFile10 } from "fs/promises";
7382
+ import { join as join21 } from "path";
6842
7383
  function formatTimestamp(date) {
6843
7384
  const base = formatDate(date);
6844
7385
  const ms = date.getMilliseconds().toString().padStart(3, "0");
@@ -7032,8 +7573,8 @@ async function parseFileContent(fileContent, source) {
7032
7573
  async function parseSessionLog(projectName, sessionId) {
7033
7574
  const projectDir = resolveProjectPath(projectName);
7034
7575
  const projectsPath = getClaudeProjectsPath();
7035
- const filePath = join20(projectDir, `${sessionId}.jsonl`);
7036
- const fileContent = await readFile9(filePath, "utf-8");
7576
+ const filePath = join21(projectDir, `${sessionId}.jsonl`);
7577
+ const fileContent = await readFile10(filePath, "utf-8");
7037
7578
  const { entries: sessionEntries, rawLines: sessionRawLines, subagentIds } = await parseFileContent(fileContent, "session");
7038
7579
  if (subagentIds.length === 0) {
7039
7580
  return { entries: sessionEntries, rawLines: sessionRawLines, subagentIds: [] };
@@ -7043,7 +7584,7 @@ async function parseSessionLog(projectName, sessionId) {
7043
7584
  const agentPath = await resolveSubagentPath(projectsPath, projectName, sessionId, agentId);
7044
7585
  if (!agentPath)
7045
7586
  return null;
7046
- const agentContent = await readFile9(agentPath, "utf-8");
7587
+ const agentContent = await readFile10(agentPath, "utf-8");
7047
7588
  const { entries, rawLines } = await parseFileContent(agentContent, agentSource);
7048
7589
  return { entries, rawLines };
7049
7590
  }), SUBAGENT_CONCURRENCY);
@@ -7069,32 +7610,32 @@ var init_log_entries = __esm(() => {
7069
7610
  });
7070
7611
 
7071
7612
  // lib/codex-sessions.ts
7072
- import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync8 } from "node:fs";
7073
- import { readFile as readFile10 } from "node:fs/promises";
7074
- import { dirname as dirname4, join as join21 } from "node:path";
7075
- import { homedir as homedir17 } from "node:os";
7613
+ import { readFileSync as readFileSync6, readdirSync as readdirSync7, existsSync as existsSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync9 } from "node:fs";
7614
+ import { readFile as readFile11 } from "node:fs/promises";
7615
+ import { dirname as dirname4, join as join22 } from "node:path";
7616
+ import { homedir as homedir15 } from "node:os";
7076
7617
  function readCache() {
7077
7618
  try {
7078
- if (!existsSync6(CACHE_PATH))
7619
+ if (!existsSync7(CACHE_PATH))
7079
7620
  return {};
7080
- return JSON.parse(readFileSync5(CACHE_PATH, "utf-8"));
7621
+ return JSON.parse(readFileSync6(CACHE_PATH, "utf-8"));
7081
7622
  } catch {
7082
7623
  return {};
7083
7624
  }
7084
7625
  }
7085
7626
  function writeCacheEntry(sessionId, path2) {
7086
7627
  try {
7087
- mkdirSync4(dirname4(CACHE_PATH), { recursive: true });
7628
+ mkdirSync5(dirname4(CACHE_PATH), { recursive: true });
7088
7629
  const cache = readCache();
7089
7630
  cache[sessionId] = path2;
7090
- writeFileSync3(CACHE_PATH, JSON.stringify(cache), "utf-8");
7631
+ writeFileSync4(CACHE_PATH, JSON.stringify(cache), "utf-8");
7091
7632
  } catch {}
7092
7633
  }
7093
7634
  function dirSearch(dir, sessionId) {
7094
7635
  try {
7095
7636
  for (const f of readdirSync7(dir, { withFileTypes: true })) {
7096
7637
  if (f.isFile() && f.name.includes(sessionId) && f.name.endsWith(".jsonl")) {
7097
- return join21(dir, f.name);
7638
+ return join22(dir, f.name);
7098
7639
  }
7099
7640
  }
7100
7641
  } catch {}
@@ -7103,16 +7644,16 @@ function dirSearch(dir, sessionId) {
7103
7644
  function findCodexTranscript(sessionId) {
7104
7645
  const cache = readCache();
7105
7646
  const cached = cache[sessionId];
7106
- if (cached && existsSync6(cached))
7647
+ if (cached && existsSync7(cached))
7107
7648
  return cached;
7108
- const root = join21(homedir17(), ".codex", "sessions");
7649
+ const root = join22(homedir15(), ".codex", "sessions");
7109
7650
  const today = new Date;
7110
7651
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
7111
7652
  const datedDirs = [today, yesterday].map((d) => {
7112
7653
  const y = String(d.getUTCFullYear());
7113
7654
  const m = String(d.getUTCMonth() + 1).padStart(2, "0");
7114
7655
  const day = String(d.getUTCDate()).padStart(2, "0");
7115
- return join21(root, y, m, day);
7656
+ return join22(root, y, m, day);
7116
7657
  });
7117
7658
  for (const dir of datedDirs) {
7118
7659
  const hit = dirSearch(dir, sessionId);
@@ -7125,13 +7666,13 @@ function findCodexTranscript(sessionId) {
7125
7666
  for (const y of readdirSync7(root, { withFileTypes: true })) {
7126
7667
  if (!y.isDirectory())
7127
7668
  continue;
7128
- for (const m of readdirSync7(join21(root, y.name), { withFileTypes: true })) {
7669
+ for (const m of readdirSync7(join22(root, y.name), { withFileTypes: true })) {
7129
7670
  if (!m.isDirectory())
7130
7671
  continue;
7131
- for (const d of readdirSync7(join21(root, y.name, m.name), { withFileTypes: true })) {
7672
+ for (const d of readdirSync7(join22(root, y.name, m.name), { withFileTypes: true })) {
7132
7673
  if (!d.isDirectory())
7133
7674
  continue;
7134
- const hit = dirSearch(join21(root, y.name, m.name, d.name), sessionId);
7675
+ const hit = dirSearch(join22(root, y.name, m.name, d.name), sessionId);
7135
7676
  if (hit) {
7136
7677
  writeCacheEntry(sessionId, hit);
7137
7678
  return hit;
@@ -7344,19 +7885,20 @@ async function getCodexSessionLog(sessionId) {
7344
7885
  const filePath = findCodexTranscript(sessionId);
7345
7886
  if (!filePath)
7346
7887
  return null;
7347
- const fileContent = await readFile10(filePath, "utf-8");
7888
+ const fileContent = await readFile11(filePath, "utf-8");
7348
7889
  const { entries, rawLines, cwd } = await parseCodexLog(fileContent, "session");
7349
7890
  return { entries, rawLines, cwd, filePath };
7350
7891
  }
7351
7892
  var CACHE_PATH, getCachedCodexSessionLog;
7352
7893
  var init_codex_sessions = __esm(() => {
7353
7894
  init_log_entries();
7354
- CACHE_PATH = join21(homedir17(), ".failproofai", "cache", "codex-session-paths.json");
7895
+ init_fp_home();
7896
+ CACHE_PATH = codexSessionPathsFile();
7355
7897
  getCachedCodexSessionLog = runtimeCache((sessionId) => getCodexSessionLog(sessionId), 60, { maxSize: 50 });
7356
7898
  });
7357
7899
 
7358
7900
  // src/hooks/resolve-permission-mode.ts
7359
- import { readFileSync as readFileSync6 } from "node:fs";
7901
+ import { readFileSync as readFileSync7 } from "node:fs";
7360
7902
  function resolvePermissionMode(integration, parsed, sessionId) {
7361
7903
  if (integration === "claude") {
7362
7904
  return parsed.permission_mode ?? "default";
@@ -7371,7 +7913,7 @@ function resolveCodexMode(sessionId) {
7371
7913
  const path2 = findCodexTranscript(sessionId);
7372
7914
  if (!path2)
7373
7915
  return;
7374
- for (const line of readFileSync6(path2, "utf-8").split(`
7916
+ for (const line of readFileSync7(path2, "utf-8").split(`
7375
7917
  `)) {
7376
7918
  if (!line.includes("turn_context"))
7377
7919
  continue;
@@ -7396,21 +7938,21 @@ var init_resolve_permission_mode = __esm(() => {
7396
7938
  });
7397
7939
 
7398
7940
  // lib/copilot-sessions.ts
7399
- import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync9 } from "node:fs";
7400
- import { readFile as readFile11 } from "node:fs/promises";
7401
- import { join as join22, resolve as resolve7, sep as sep2 } from "node:path";
7402
- import { homedir as homedir18 } from "node:os";
7941
+ import { readFileSync as readFileSync8, readdirSync as readdirSync8, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
7942
+ import { readFile as readFile12 } from "node:fs/promises";
7943
+ import { join as join23, resolve as resolve8, sep as sep2 } from "node:path";
7944
+ import { homedir as homedir16 } from "node:os";
7403
7945
  function getCopilotHome() {
7404
- return process.env.COPILOT_HOME || join22(homedir18(), ".copilot");
7946
+ return process.env.COPILOT_HOME || join23(homedir16(), ".copilot");
7405
7947
  }
7406
7948
  function getCopilotSessionStateRoot2() {
7407
- return join22(getCopilotHome(), "session-state");
7949
+ return join23(getCopilotHome(), "session-state");
7408
7950
  }
7409
7951
  function getCopilotSessionDir(sessionId) {
7410
7952
  if (!sessionId)
7411
7953
  return null;
7412
- const root = resolve7(getCopilotSessionStateRoot2());
7413
- const candidate = resolve7(root, sessionId);
7954
+ const root = resolve8(getCopilotSessionStateRoot2());
7955
+ const candidate = resolve8(root, sessionId);
7414
7956
  if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
7415
7957
  return null;
7416
7958
  return candidate;
@@ -7419,23 +7961,23 @@ function resolveSessionFile(sessionId, filename) {
7419
7961
  const dir = getCopilotSessionDir(sessionId);
7420
7962
  if (!dir)
7421
7963
  return null;
7422
- return join22(dir, filename);
7964
+ return join23(dir, filename);
7423
7965
  }
7424
7966
  function findCopilotTranscript(sessionId) {
7425
7967
  const candidate = resolveSessionFile(sessionId, "events.jsonl");
7426
7968
  if (!candidate)
7427
7969
  return null;
7428
- return existsSync7(candidate) ? candidate : null;
7970
+ return existsSync8(candidate) ? candidate : null;
7429
7971
  }
7430
7972
  function findCopilotWorkspace(sessionId) {
7431
7973
  const candidate = resolveSessionFile(sessionId, "workspace.yaml");
7432
7974
  if (!candidate)
7433
7975
  return null;
7434
- return existsSync7(candidate) ? candidate : null;
7976
+ return existsSync8(candidate) ? candidate : null;
7435
7977
  }
7436
7978
  function readYamlScalar(path2, key) {
7437
7979
  try {
7438
- const text = readFileSync7(path2, "utf-8");
7980
+ const text = readFileSync8(path2, "utf-8");
7439
7981
  const re = new RegExp(`^${key}\\s*:\\s*(.+?)\\s*$`, "m");
7440
7982
  const m = text.match(re);
7441
7983
  if (!m)
@@ -7598,7 +8140,7 @@ async function getCopilotSessionLog(sessionId) {
7598
8140
  return null;
7599
8141
  let fileContent;
7600
8142
  try {
7601
- fileContent = await readFile11(filePath, "utf-8");
8143
+ fileContent = await readFile12(filePath, "utf-8");
7602
8144
  } catch {
7603
8145
  return null;
7604
8146
  }
@@ -7613,18 +8155,18 @@ var init_copilot_sessions = __esm(() => {
7613
8155
  });
7614
8156
 
7615
8157
  // lib/cursor-sessions.ts
7616
- import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
7617
- import { readFile as readFile12 } from "node:fs/promises";
7618
- import { basename as basename3, join as join23, resolve as resolve8, sep as sep3 } from "node:path";
7619
- import { homedir as homedir19 } from "node:os";
8158
+ import { readFileSync as readFileSync9, readdirSync as readdirSync9, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
8159
+ import { readFile as readFile13 } from "node:fs/promises";
8160
+ import { basename as basename3, join as join24, resolve as resolve9, sep as sep3 } from "node:path";
8161
+ import { homedir as homedir17 } from "node:os";
7620
8162
  function getCursorHome2() {
7621
- return process.env.CURSOR_HOME || join23(homedir19(), ".cursor");
8163
+ return process.env.CURSOR_HOME || join24(homedir17(), ".cursor");
7622
8164
  }
7623
8165
  function getCursorSessionDir(sessionId) {
7624
8166
  if (!sessionId)
7625
8167
  return null;
7626
- const home = resolve8(getCursorHome2());
7627
- const projectsRoot = resolve8(home, NEW_PROJECTS_DIR2);
8168
+ const home = resolve9(getCursorHome2());
8169
+ const projectsRoot = resolve9(home, NEW_PROJECTS_DIR2);
7628
8170
  let projectEntries = [];
7629
8171
  try {
7630
8172
  projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
@@ -7632,19 +8174,19 @@ function getCursorSessionDir(sessionId) {
7632
8174
  for (const entry of projectEntries) {
7633
8175
  if (!entry.isDirectory())
7634
8176
  continue;
7635
- const candidate = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
7636
- const transcriptRoot = resolve8(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
8177
+ const candidate = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
8178
+ const transcriptRoot = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
7637
8179
  if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
7638
8180
  continue;
7639
- if (existsSync8(candidate))
8181
+ if (existsSync9(candidate))
7640
8182
  return candidate;
7641
8183
  }
7642
8184
  for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
7643
- const root = resolve8(home, sub);
7644
- const candidate = resolve8(root, sessionId);
8185
+ const root = resolve9(home, sub);
8186
+ const candidate = resolve9(root, sessionId);
7645
8187
  if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
7646
8188
  continue;
7647
- if (existsSync8(candidate))
8189
+ if (existsSync9(candidate))
7648
8190
  return candidate;
7649
8191
  }
7650
8192
  return null;
@@ -7653,12 +8195,12 @@ function findCursorTranscript(sessionId) {
7653
8195
  const dir = getCursorSessionDir(sessionId);
7654
8196
  if (!dir)
7655
8197
  return null;
7656
- const newCandidate = join23(dir, `${basename3(dir)}.jsonl`);
7657
- if (existsSync8(newCandidate))
8198
+ const newCandidate = join24(dir, `${basename3(dir)}.jsonl`);
8199
+ if (existsSync9(newCandidate))
7658
8200
  return newCandidate;
7659
8201
  for (const name of LEGACY_TRANSCRIPT_FILE_CANDIDATES2) {
7660
- const candidate = join23(dir, name);
7661
- if (existsSync8(candidate))
8202
+ const candidate = join24(dir, name);
8203
+ if (existsSync9(candidate))
7662
8204
  return candidate;
7663
8205
  }
7664
8206
  return null;
@@ -7853,7 +8395,7 @@ async function getCursorSessionLog(sessionId) {
7853
8395
  return null;
7854
8396
  let fileContent;
7855
8397
  try {
7856
- fileContent = await readFile12(filePath, "utf-8");
8398
+ fileContent = await readFile13(filePath, "utf-8");
7857
8399
  } catch {
7858
8400
  return null;
7859
8401
  }
@@ -7869,12 +8411,12 @@ var init_cursor_sessions = __esm(() => {
7869
8411
  });
7870
8412
 
7871
8413
  // lib/pi-sessions.ts
7872
- import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
7873
- import { readFile as readFile13 } from "node:fs/promises";
7874
- import { join as join24, resolve as resolve9, sep as sep4 } from "node:path";
7875
- import { homedir as homedir20 } from "node:os";
8414
+ import { readFileSync as readFileSync10, readdirSync as readdirSync10, existsSync as existsSync10, statSync as statSync12 } from "node:fs";
8415
+ import { readFile as readFile14 } from "node:fs/promises";
8416
+ import { join as join25, resolve as resolve10, sep as sep4 } from "node:path";
8417
+ import { homedir as homedir18 } from "node:os";
7876
8418
  function getPiSessionStateRoot() {
7877
- return process.env.PI_SESSIONS_DIR || join24(homedir20(), ".pi", "agent", "sessions");
8419
+ return process.env.PI_SESSIONS_DIR || join25(homedir18(), ".pi", "agent", "sessions");
7878
8420
  }
7879
8421
  function isSafeSessionId(sessionId) {
7880
8422
  return UUID_RE.test(sessionId);
@@ -7882,7 +8424,7 @@ function isSafeSessionId(sessionId) {
7882
8424
  function findPiTranscript(sessionId) {
7883
8425
  if (!isSafeSessionId(sessionId))
7884
8426
  return null;
7885
- const root = resolve9(getPiSessionStateRoot());
8427
+ const root = resolve10(getPiSessionStateRoot());
7886
8428
  let cwdDirs;
7887
8429
  try {
7888
8430
  cwdDirs = readdirSync10(root);
@@ -7890,7 +8432,7 @@ function findPiTranscript(sessionId) {
7890
8432
  return null;
7891
8433
  }
7892
8434
  for (const cwdDir of cwdDirs) {
7893
- const cwdPath = resolve9(root, cwdDir);
8435
+ const cwdPath = resolve10(root, cwdDir);
7894
8436
  if (!cwdPath.startsWith(`${root}${sep4}`))
7895
8437
  continue;
7896
8438
  let files;
@@ -7903,10 +8445,10 @@ function findPiTranscript(sessionId) {
7903
8445
  const m = SESSION_FILE_RE2.exec(f);
7904
8446
  if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
7905
8447
  continue;
7906
- const candidate = resolve9(cwdPath, f);
8448
+ const candidate = resolve10(cwdPath, f);
7907
8449
  if (!candidate.startsWith(`${cwdPath}${sep4}`))
7908
8450
  continue;
7909
- if (existsSync9(candidate))
8451
+ if (existsSync10(candidate))
7910
8452
  return candidate;
7911
8453
  }
7912
8454
  }
@@ -7924,7 +8466,7 @@ function extractMessageText(content) {
7924
8466
 
7925
8467
  `);
7926
8468
  }
7927
- function buildAssistantContent(content) {
8469
+ function buildAssistantContent(content, onToolUse) {
7928
8470
  if (!Array.isArray(content))
7929
8471
  return [];
7930
8472
  const blocks = [];
@@ -7935,6 +8477,14 @@ function buildAssistantContent(content) {
7935
8477
  if (block?.type === "thinking" && typeof block.thinking === "string" && block.thinking.length > 0) {
7936
8478
  blocks.push({ type: "text", text: `[thinking] ${block.thinking}` });
7937
8479
  }
8480
+ if (block?.type === "toolCall") {
8481
+ const id = typeof block.id === "string" && block.id.length > 0 ? block.id : `${typeof block.name === "string" ? block.name : "tool"}-${blocks.length}`;
8482
+ const name = typeof block.name === "string" ? block.name : "tool";
8483
+ const input = block.arguments && typeof block.arguments === "object" && !Array.isArray(block.arguments) ? block.arguments : {};
8484
+ const toolUse = { type: "tool_use", id, name, input };
8485
+ blocks.push(toolUse);
8486
+ onToolUse?.(toolUse);
8487
+ }
7938
8488
  }
7939
8489
  return blocks;
7940
8490
  }
@@ -7945,6 +8495,8 @@ async function parsePiLog(fileContent, source = "session") {
7945
8495
  const rawLines = [];
7946
8496
  let cwd;
7947
8497
  let seenSessionStart = false;
8498
+ const toolUseById = new Map;
8499
+ const toolUseStartMs = new Map;
7948
8500
  for (let i = 0;i < lines.length; i++) {
7949
8501
  if (i > 0 && i % 200 === 0)
7950
8502
  await new Promise((r) => setImmediate(r));
@@ -7992,7 +8544,10 @@ async function parsePiLog(fileContent, source = "session") {
7992
8544
  continue;
7993
8545
  }
7994
8546
  if (role === "assistant") {
7995
- const blocks = buildAssistantContent(content);
8547
+ const blocks = buildAssistantContent(content, (block) => {
8548
+ toolUseById.set(block.id, block);
8549
+ toolUseStartMs.set(block.id, date.getTime());
8550
+ });
7996
8551
  if (blocks.length === 0) {
7997
8552
  entries.push({
7998
8553
  type: "system",
@@ -8008,6 +8563,22 @@ async function parsePiLog(fileContent, source = "session") {
8008
8563
  });
8009
8564
  continue;
8010
8565
  }
8566
+ if (role === "toolResult") {
8567
+ const callId = raw.message.toolCallId;
8568
+ const block = typeof callId === "string" ? toolUseById.get(callId) : undefined;
8569
+ if (block) {
8570
+ const startMs = typeof callId === "string" && toolUseStartMs.get(callId) || date.getTime();
8571
+ const durationMs = Math.max(0, date.getTime() - startMs);
8572
+ block.result = {
8573
+ timestamp,
8574
+ timestampFormatted: formatTimestamp(date),
8575
+ content: extractMessageText(content),
8576
+ durationMs,
8577
+ durationFormatted: formatDuration(durationMs)
8578
+ };
8579
+ continue;
8580
+ }
8581
+ }
8011
8582
  entries.push({
8012
8583
  type: "system",
8013
8584
  ...baseEntry(rawCopy, timestamp, date, source),
@@ -8032,7 +8603,7 @@ async function getPiSessionLog(sessionId) {
8032
8603
  return null;
8033
8604
  let fileContent;
8034
8605
  try {
8035
- fileContent = await readFile13(filePath, "utf-8");
8606
+ fileContent = await readFile14(filePath, "utf-8");
8036
8607
  } catch {
8037
8608
  return null;
8038
8609
  }
@@ -8101,6 +8672,214 @@ var init_resolve_transcript_path = __esm(() => {
8101
8672
  init_factory_sessions();
8102
8673
  init_antigravity_sessions();
8103
8674
  });
8675
+ // src/hooks/cloud-managed-policies.ts
8676
+ import { createHash as createHash3 } from "node:crypto";
8677
+ import { existsSync as existsSync11, readFileSync as readFileSync11, realpathSync, rmSync } from "node:fs";
8678
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve11 } from "node:path";
8679
+ function cloudManagedPolicyRoot() {
8680
+ return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
8681
+ }
8682
+ function parseManifest(value) {
8683
+ if (!value || typeof value !== "object")
8684
+ throw new Error("active manifest is not an object");
8685
+ const raw = value;
8686
+ if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
8687
+ throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
8688
+ }
8689
+ const deployment = raw.deployment ?? raw.generation;
8690
+ if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
8691
+ throw new Error("active manifest deployment is invalid");
8692
+ }
8693
+ if (!Array.isArray(raw.policies))
8694
+ throw new Error("active manifest policies is not an array");
8695
+ const policies = raw.policies.map((p) => {
8696
+ const entry = p;
8697
+ return { ...entry, version: entry.version ?? entry.revision };
8698
+ });
8699
+ return { schemaVersion: raw.schemaVersion, deployment, policies };
8700
+ }
8701
+ function resolveManagedPath(root, candidate) {
8702
+ if (!candidate || isAbsolute2(candidate))
8703
+ throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
8704
+ const absolute = resolve11(root, candidate);
8705
+ const lexicalRelative = relative3(root, absolute);
8706
+ if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
8707
+ throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
8708
+ }
8709
+ const realRoot = realpathSync(root);
8710
+ const realFile = realpathSync(absolute);
8711
+ const physicalRelative = relative3(realRoot, realFile);
8712
+ if (physicalRelative.startsWith("..") || isAbsolute2(physicalRelative)) {
8713
+ throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
8714
+ }
8715
+ return realFile;
8716
+ }
8717
+ function readActiveCloudManagedPolicies() {
8718
+ const root = cloudManagedPolicyRoot();
8719
+ const activePath = resolve11(root, "active.json");
8720
+ if (!existsSync11(activePath))
8721
+ return [];
8722
+ let raw;
8723
+ try {
8724
+ raw = JSON.parse(readFileSync11(activePath, "utf8"));
8725
+ } catch (err) {
8726
+ throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
8727
+ }
8728
+ const manifest = parseManifest(raw);
8729
+ const seen = new Set;
8730
+ return manifest.policies.map((policy) => {
8731
+ if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
8732
+ throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
8733
+ }
8734
+ if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
8735
+ throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
8736
+ }
8737
+ if (!SHA256_RE.test(policy.sha256)) {
8738
+ throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
8739
+ }
8740
+ if (seen.has(policy.id))
8741
+ throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
8742
+ seen.add(policy.id);
8743
+ const path2 = resolveManagedPath(root, policy.path);
8744
+ const bytes = readFileSync11(path2);
8745
+ const actual = createHash3("sha256").update(bytes).digest("hex");
8746
+ if (actual !== policy.sha256) {
8747
+ throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
8748
+ }
8749
+ if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
8750
+ throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
8751
+ }
8752
+ return {
8753
+ id: policy.id,
8754
+ version: policy.version,
8755
+ effect: policy.effect ?? "enforce",
8756
+ sha256: policy.sha256,
8757
+ path: path2,
8758
+ deployment: manifest.deployment
8759
+ };
8760
+ });
8761
+ }
8762
+ var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
8763
+ var init_cloud_managed_policies = __esm(() => {
8764
+ init_fp_home();
8765
+ ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
8766
+ SHA256_RE = /^[a-f0-9]{64}$/;
8767
+ POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
8768
+ });
8769
+
8770
+ // lib/atomic-write.ts
8771
+ var init_atomic_write = () => {};
8772
+
8773
+ // src/hooks/session-pause.ts
8774
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync11, readFileSync as readFileSync12, rmSync as rmSync2 } from "node:fs";
8775
+ import { createHash as createHash4 } from "node:crypto";
8776
+ import { join as join26, resolve as resolve12 } from "node:path";
8777
+ function pauseStateDir() {
8778
+ const override = process.env.FAILPROOFAI_STATE_DIR;
8779
+ if (override)
8780
+ return resolve12(override, "sessions");
8781
+ return sessionPauseDir();
8782
+ }
8783
+ function pauseFilePath(sessionId) {
8784
+ const digest = createHash4("sha256").update(sessionId).digest("hex").slice(0, 32);
8785
+ return join26(pauseStateDir(), `${digest}.json`);
8786
+ }
8787
+ function parseStored(raw) {
8788
+ if (!raw || typeof raw !== "object")
8789
+ return null;
8790
+ const p = raw;
8791
+ if (p.schemaVersion !== SCHEMA_VERSION)
8792
+ return null;
8793
+ if (typeof p.sessionId !== "string" || p.sessionId.length === 0)
8794
+ return null;
8795
+ if (!Number.isFinite(p.pausedAt) || !Number.isFinite(p.expiresAt))
8796
+ return null;
8797
+ const pausedAt = p.pausedAt;
8798
+ const firstPausedAt = Number.isFinite(p.firstPausedAt) ? p.firstPausedAt : pausedAt;
8799
+ return {
8800
+ schemaVersion: SCHEMA_VERSION,
8801
+ sessionId: p.sessionId,
8802
+ pausedAt,
8803
+ firstPausedAt,
8804
+ expiresAt: Math.min(p.expiresAt, firstPausedAt + PAUSE_CEILING_MS),
8805
+ setBy: typeof p.setBy === "string" ? p.setBy : "unknown",
8806
+ ...typeof p.cwd === "string" ? { cwd: p.cwd } : {}
8807
+ };
8808
+ }
8809
+ function readActivePause(sessionId, now = Date.now()) {
8810
+ if (!sessionId)
8811
+ return null;
8812
+ try {
8813
+ const path2 = pauseFilePath(sessionId);
8814
+ if (!existsSync12(path2))
8815
+ return null;
8816
+ const stored = parseStored(JSON.parse(readFileSync12(path2, "utf8")));
8817
+ if (!stored)
8818
+ return null;
8819
+ if (stored.expiresAt <= now)
8820
+ return null;
8821
+ const { schemaVersion: _ignored, ...pause } = stored;
8822
+ return pause;
8823
+ } catch {
8824
+ return null;
8825
+ }
8826
+ }
8827
+ var SCHEMA_VERSION = 1, PAUSE_DEFAULT_MS, PAUSE_CEILING_MS;
8828
+ var init_session_pause = __esm(() => {
8829
+ init_atomic_write();
8830
+ init_hook_activity_store();
8831
+ init_fp_home();
8832
+ PAUSE_DEFAULT_MS = 30 * 60000;
8833
+ PAUSE_CEILING_MS = 8 * 60 * 60000;
8834
+ });
8835
+
8836
+ // src/hooks/daemon-download.ts
8837
+ var init_daemon_download = __esm(() => {
8838
+ init_package();
8839
+ init_package();
8840
+ init_fp_home();
8841
+ });
8842
+
8843
+ // src/hooks/daemon-service.ts
8844
+ var init_daemon_service = __esm(() => {
8845
+ init_hook_logger();
8846
+ init_daemon_download();
8847
+ init_fp_home();
8848
+ init_package();
8849
+ init_fp_config();
8850
+ });
8851
+
8852
+ // src/hooks/fp-reset.ts
8853
+ import {
8854
+ copyFileSync,
8855
+ existsSync as existsSync13,
8856
+ mkdirSync as mkdirSync7,
8857
+ readFileSync as readFileSync13,
8858
+ readdirSync as readdirSync12,
8859
+ renameSync as renameSync3,
8860
+ rmSync as rmSync3,
8861
+ rmdirSync,
8862
+ statSync as statSync13,
8863
+ writeFileSync as writeFileSync5
8864
+ } from "node:fs";
8865
+ function layoutWarningForHook() {
8866
+ const state = detectLayout();
8867
+ if (state.kind === "current" || state.kind === "absent")
8868
+ return null;
8869
+ if (state.kind === "future") {
8870
+ return `[failproofai] this directory was written by a newer version (layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` + `Upgrade failproofai.`;
8871
+ }
8872
+ if (existsSync13(globalPolicyConfigFile()))
8873
+ return null;
8874
+ return `[failproofai] setup predates this version — global policies are NOT being enforced. ` + `Run \`failproofai config\` to re-create it.`;
8875
+ }
8876
+ var init_fp_reset = __esm(() => {
8877
+ init_hook_activity_store();
8878
+ init_fp_home();
8879
+ init_fp_config();
8880
+ init_daemon_service();
8881
+ });
8882
+
8104
8883
  // src/hooks/handler.ts
8105
8884
  function canonicalizeEventType(raw, cli) {
8106
8885
  if (cli === "codex") {
@@ -8135,8 +8914,29 @@ function canonicalizeEventType(raw, cli) {
8135
8914
  }
8136
8915
  return raw;
8137
8916
  }
8917
+ async function runObserved(hook, ctx, hookName, eventType, cli) {
8918
+ try {
8919
+ return await Promise.race([
8920
+ hook.fn(ctx),
8921
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
8922
+ ]);
8923
+ } catch (err) {
8924
+ const msg = err instanceof Error ? err.message : String(err);
8925
+ trackHookEvent(getInstanceId(), "custom_hook_error", {
8926
+ hook_name: hookName,
8927
+ error_type: msg === "timeout" ? "timeout" : "exception",
8928
+ event_type: eventType,
8929
+ cli,
8930
+ is_observe_mode: true
8931
+ });
8932
+ return { decision: "allow" };
8933
+ }
8934
+ }
8138
8935
  async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts) {
8139
8936
  const startTime = performance.now();
8937
+ const layoutWarning = layoutWarningForHook();
8938
+ if (layoutWarning)
8939
+ hookLogWarn(layoutWarning);
8140
8940
  try {
8141
8941
  let parsed = {};
8142
8942
  if (stdinPayload) {
@@ -8176,6 +8976,10 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8176
8976
  let config;
8177
8977
  let customHooksList = [];
8178
8978
  let conventionHookNames = new Set;
8979
+ let activePause = null;
8980
+ const policyAttribution = new Map;
8981
+ let cloudDeployment;
8982
+ const observedResults = [];
8179
8983
  if (opts?.forceDecision) {
8180
8984
  config = { enabledPolicies: [] };
8181
8985
  clearPolicies();
@@ -8184,23 +8988,59 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8184
8988
  } else {
8185
8989
  config = readMergedHooksConfig(session.cwd);
8186
8990
  clearPolicies();
8187
- registerBuiltinPolicies(config.enabledPolicies);
8188
- const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
8991
+ activePause = readActivePause(session.sessionId);
8992
+ registerBuiltinPolicies(activePause ? [] : config.enabledPolicies);
8993
+ let cloudManagedPolicies = [];
8994
+ try {
8995
+ cloudManagedPolicies = readActiveCloudManagedPolicies();
8996
+ } catch (err) {
8997
+ const msg = err instanceof Error ? err.message : String(err);
8998
+ hookLogWarn(`[failproofai] cloud-managed policies could NOT be loaded and are not being enforced: ${msg}. ` + `Local policies are unaffected. Run \`failproofai config --status\` to check this machine's enrolment.`);
8999
+ trackHookEvent(getInstanceId(), "cloud_managed_load_error", {
9000
+ event_type: eventType,
9001
+ cli
9002
+ });
9003
+ }
9004
+ cloudDeployment = cloudManagedPolicies[0]?.deployment;
9005
+ const configuredCustomPaths = config.customPoliciesPaths ?? config.customPoliciesPath;
9006
+ const allExplicitPaths = cloudManagedPolicies.length === 0 ? configuredCustomPaths : [
9007
+ ...typeof configuredCustomPaths === "string" ? [configuredCustomPaths] : configuredCustomPaths ?? [],
9008
+ ...cloudManagedPolicies.map((policy) => policy.path)
9009
+ ];
9010
+ const loadResult = await loadAllCustomHooks(allExplicitPaths, {
8189
9011
  sessionCwd: session.cwd,
8190
- customPoliciesEnabled: config.customPoliciesEnabled
9012
+ customPoliciesEnabled: config.customPoliciesEnabled,
9013
+ ...cloudManagedPolicies.length > 0 ? { cloudManagedPolicies } : {}
8191
9014
  });
8192
9015
  customHooksList = loadResult.hooks;
8193
9016
  const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
8194
9017
  conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8195
9018
  for (const hook of customHooksList) {
8196
- const policyId = hook.__policyId;
8197
- if (policyId && disabledCustomPolicies.has(policyId))
9019
+ const taggedHook = hook;
9020
+ const policyId = taggedHook.__policyId;
9021
+ const cloudManaged = taggedHook.__cloudManaged;
9022
+ if (!cloudManaged && policyId && disabledCustomPolicies.has(policyId))
9023
+ continue;
9024
+ if (!cloudManaged && activePause)
8198
9025
  continue;
8199
9026
  const hookName = hook.name;
8200
9027
  const conventionScope = hook.__conventionScope;
8201
9028
  const isConvention = !!conventionScope;
8202
- const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
9029
+ const prefix = cloudManaged ? `cloud/${cloudManaged.id}@${cloudManaged.version}` : isConvention ? `.failproofai-${conventionScope}` : "custom";
9030
+ const observeOnly = cloudManaged?.effect === "observe";
8203
9031
  const fn = async (ctx) => {
9032
+ if (observeOnly) {
9033
+ const shadow = await runObserved(hook, ctx, hookName, eventType, cli);
9034
+ if (shadow.decision !== "allow") {
9035
+ observedResults.push({
9036
+ policyId: cloudManaged.id,
9037
+ version: cloudManaged.version,
9038
+ decision: shadow.decision,
9039
+ reason: shadow.reason ?? null
9040
+ });
9041
+ }
9042
+ return { decision: "allow" };
9043
+ }
8204
9044
  try {
8205
9045
  const result2 = await Promise.race([
8206
9046
  hook.fn(ctx),
@@ -8222,7 +9062,12 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8222
9062
  return { decision: "allow" };
8223
9063
  }
8224
9064
  };
8225
- registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
9065
+ const registeredName = `${prefix}/${hookName}`;
9066
+ policyAttribution.set(registeredName, {
9067
+ source: cloudManaged ? "cloud" : isConvention ? "convention" : "custom",
9068
+ ...cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudVersion: cloudManaged.version } : {}
9069
+ });
9070
+ registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1);
8226
9071
  }
8227
9072
  if (customHooksList.length > 0) {
8228
9073
  trackHookEvent(getInstanceId(), "custom_hooks_loaded", {
@@ -8263,7 +9108,18 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8263
9108
  transcriptPath: session.transcriptPath,
8264
9109
  cwd: session.cwd,
8265
9110
  permissionMode: session.permissionMode,
8266
- hookEventName: session.hookEventName
9111
+ hookEventName: session.hookEventName,
9112
+ ...result.policyName ? (() => {
9113
+ const attribution = policyAttribution.get(result.policyName);
9114
+ return {
9115
+ policySource: attribution?.source ?? "builtin",
9116
+ ...attribution?.cloudPolicyId ? { cloudPolicyId: attribution.cloudPolicyId } : {},
9117
+ ...attribution?.cloudVersion !== undefined ? { cloudVersion: attribution.cloudVersion } : {}
9118
+ };
9119
+ })() : {},
9120
+ ...cloudDeployment !== undefined ? { cloudDeployment } : {},
9121
+ ...observedResults.length > 0 ? { observed: observedResults } : {},
9122
+ ...activePause ? { pausedBy: activePause.setBy, pauseExpiresAt: activePause.expiresAt } : {}
8267
9123
  };
8268
9124
  try {
8269
9125
  persistHookActivity(activityEntry);
@@ -8315,6 +9171,9 @@ var init_handler = __esm(() => {
8315
9171
  init_resolve_transcript_path();
8316
9172
  init_telemetry_id();
8317
9173
  init_hook_logger();
9174
+ init_cloud_managed_policies();
9175
+ init_session_pause();
9176
+ init_fp_reset();
8318
9177
  });
8319
9178
 
8320
9179
  // src/hooks/worker-server.ts
@@ -8322,13 +9181,13 @@ var exports_worker_server = {};
8322
9181
  __export(exports_worker_server, {
8323
9182
  startWorkerServer: () => startWorkerServer
8324
9183
  });
8325
- import { createServer } from "node:net";
8326
- import { existsSync as existsSync10, unlinkSync as unlinkSync2 } from "node:fs";
9184
+ import { createConnection, createServer } from "node:net";
9185
+ import { existsSync as existsSync14, unlinkSync as unlinkSync2 } from "node:fs";
8327
9186
  function isWorkerHookRequest(msg) {
8328
9187
  if (!msg || typeof msg !== "object")
8329
9188
  return false;
8330
9189
  const m = msg;
8331
- return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || typeof m.cwd === "string");
9190
+ return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || m.cwd === null || typeof m.cwd === "string");
8332
9191
  }
8333
9192
  function encodeFrame(value) {
8334
9193
  const body = Buffer.from(JSON.stringify(value), "utf8");
@@ -8337,9 +9196,20 @@ function encodeFrame(value) {
8337
9196
  return Buffer.concat([header, body]);
8338
9197
  }
8339
9198
  function enqueue(task) {
8340
- processingChain = processingChain.then(task).catch(() => {});
9199
+ processingChain = processingChain.then(() => new Promise((settle) => {
9200
+ const timer = setTimeout(() => {
9201
+ hookLogWarn(`worker: a request did not settle within ${TASK_DEADLINE_MS}ms; ` + `exiting so the supervisor can respawn a clean worker`);
9202
+ process.exit(WEDGED_EXIT_CODE);
9203
+ }, TASK_DEADLINE_MS);
9204
+ timer.unref?.();
9205
+ const done = () => {
9206
+ clearTimeout(timer);
9207
+ settle();
9208
+ };
9209
+ task().then(done, done);
9210
+ })).catch(() => {});
8341
9211
  }
8342
- function handleConnection(socket) {
9212
+ function handleConnection(socket, shutdown) {
8343
9213
  let recvBuf = Buffer.alloc(0);
8344
9214
  let declaredLen = null;
8345
9215
  socket.on("data", (chunk) => {
@@ -8367,6 +9237,10 @@ function handleConnection(socket) {
8367
9237
  socket.write(encodeFrame({ type: "error", message: "malformed request frame" }));
8368
9238
  continue;
8369
9239
  }
9240
+ if (message && typeof message === "object" && message.type === "shutdown") {
9241
+ socket.end(encodeFrame({ type: "shutdownAccepted" }), shutdown);
9242
+ return;
9243
+ }
8370
9244
  if (!isWorkerHookRequest(message)) {
8371
9245
  socket.write(encodeFrame({ type: "error", message: "unrecognized request shape" }));
8372
9246
  continue;
@@ -8376,7 +9250,7 @@ function handleConnection(socket) {
8376
9250
  try {
8377
9251
  const result = await evaluateHookEvent(request.hookEvent, request.cli, request.stdin, {
8378
9252
  awaitTelemetryFlush: false,
8379
- fallbackCwd: request.cwd
9253
+ fallbackCwd: request.cwd ?? undefined
8380
9254
  });
8381
9255
  socket.write(encodeFrame({
8382
9256
  type: "hookResult",
@@ -8394,17 +9268,67 @@ function handleConnection(socket) {
8394
9268
  });
8395
9269
  socket.on("error", () => {});
8396
9270
  }
8397
- function startWorkerServer(socketPath) {
8398
- if (existsSync10(socketPath)) {
8399
- try {
8400
- unlinkSync2(socketPath);
8401
- } catch {}
8402
- }
8403
- const server = createServer(handleConnection);
8404
- server.listen(socketPath);
9271
+ function requestExistingWorkerShutdown(socketPath) {
9272
+ return new Promise((resolvePromise, reject) => {
9273
+ const socket = createConnection({ path: socketPath });
9274
+ let recvBuf = Buffer.alloc(0);
9275
+ let settled = false;
9276
+ const timer = setTimeout(() => finish(new Error("existing worker did not acknowledge shutdown")), STALE_WORKER_SHUTDOWN_TIMEOUT_MS);
9277
+ timer.unref?.();
9278
+ const finish = (result) => {
9279
+ if (settled)
9280
+ return;
9281
+ settled = true;
9282
+ clearTimeout(timer);
9283
+ socket.destroy();
9284
+ if (result instanceof Error)
9285
+ reject(result);
9286
+ else
9287
+ resolvePromise(result);
9288
+ };
9289
+ socket.on("connect", () => socket.write(encodeFrame({ type: "shutdown" })));
9290
+ socket.on("data", (chunk) => {
9291
+ recvBuf = Buffer.concat([recvBuf, chunk]);
9292
+ if (recvBuf.length < 4)
9293
+ return;
9294
+ const len = recvBuf.readUInt32BE(0);
9295
+ if (len > MAX_FRAME_LEN || recvBuf.length < 4 + len)
9296
+ return;
9297
+ try {
9298
+ const response = JSON.parse(recvBuf.subarray(4, 4 + len).toString("utf8"));
9299
+ if (response.type === "shutdownAccepted")
9300
+ finish("stopped");
9301
+ else
9302
+ finish(new Error("existing socket belongs to a process that does not support shutdown"));
9303
+ } catch {
9304
+ finish(new Error("existing worker returned a malformed shutdown response"));
9305
+ }
9306
+ });
9307
+ socket.on("error", (err) => {
9308
+ if (err.code === "ECONNREFUSED" || err.code === "ENOENT")
9309
+ finish("stale");
9310
+ else
9311
+ finish(err);
9312
+ });
9313
+ });
9314
+ }
9315
+ function startWorkerServer(socketPath, shutdown) {
9316
+ const server = createServer((socket) => handleConnection(socket, shutdown ?? (() => server.close())));
9317
+ const listen = () => {
9318
+ if (existsSync14(socketPath)) {
9319
+ try {
9320
+ unlinkSync2(socketPath);
9321
+ } catch {}
9322
+ }
9323
+ server.listen(socketPath);
9324
+ };
9325
+ if (!existsSync14(socketPath))
9326
+ listen();
9327
+ else
9328
+ requestExistingWorkerShutdown(socketPath).then(listen, (err) => server.emit("error", err));
8405
9329
  return server;
8406
9330
  }
8407
- var MAX_FRAME_LEN, processingChain;
9331
+ var MAX_FRAME_LEN, TASK_DEADLINE_MS = 60000, WEDGED_EXIT_CODE = 75, processingChain, STALE_WORKER_SHUTDOWN_TIMEOUT_MS = 2000;
8408
9332
  var init_worker_server = __esm(() => {
8409
9333
  init_handler();
8410
9334
  init_hook_logger();
@@ -8413,14 +9337,14 @@ var init_worker_server = __esm(() => {
8413
9337
  });
8414
9338
 
8415
9339
  // bin/failproofai-worker.mjs
8416
- import { realpathSync } from "node:fs";
8417
- import { dirname as dirname5, resolve as resolve10 } from "node:path";
9340
+ import { realpathSync as realpathSync2 } from "node:fs";
9341
+ import { dirname as dirname5, resolve as resolve13 } from "node:path";
8418
9342
  import { fileURLToPath } from "node:url";
8419
9343
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
8420
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve10(dirname5(realpathSync(fileURLToPath(import.meta.url))), "..");
9344
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve13(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..");
8421
9345
  }
8422
9346
  if (!process.env.FAILPROOFAI_DIST_PATH) {
8423
- process.env.FAILPROOFAI_DIST_PATH = resolve10(dirname5(realpathSync(fileURLToPath(import.meta.url))), "..", "dist");
9347
+ process.env.FAILPROOFAI_DIST_PATH = resolve13(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
8424
9348
  }
8425
9349
  var socketPath = process.env.FAILPROOFAI_WORKER_SOCKET;
8426
9350
  if (!socketPath) {
@@ -8428,7 +9352,7 @@ if (!socketPath) {
8428
9352
  process.exit(1);
8429
9353
  }
8430
9354
  var { startWorkerServer: startWorkerServer2 } = await Promise.resolve().then(() => (init_worker_server(), exports_worker_server));
8431
- var server = startWorkerServer2(socketPath);
9355
+ var server = startWorkerServer2(socketPath, shutdown);
8432
9356
  server.on("error", (err) => {
8433
9357
  console.error(`[failproofai-worker] server error: ${err.message}`);
8434
9358
  process.exit(1);