failproofai 1.0.0-beta.1 → 1.0.0-beta.11

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 (358) 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 +2 -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]__06hexd0._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
  107. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  109. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
  112. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
  119. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
  120. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
  121. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  122. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
  123. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
  124. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  125. package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
  129. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1gwr8l-._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__1rdruw4._.js} +2 -2
  133. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
  135. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  136. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1yw368c._.js} +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__20ie7me._.js +3 -0
  139. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/ssr/_0n4bc3i._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  146. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
  150. package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  153. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  155. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  156. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
  157. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  158. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
  159. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  160. package/.next/standalone/.next/server/middleware.js +2 -2
  161. package/.next/standalone/.next/server/pages/404.html +1 -1
  162. package/.next/standalone/.next/server/pages/500.html +1 -1
  163. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  164. package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
  165. package/.next/standalone/.next/static/chunks/{2ovmv4esqalpb.js → 0-m1vctj2jny6.js} +1 -1
  166. package/.next/standalone/.next/static/chunks/{1b3a1scouf40o.js → 04y6igmx5olt6.js} +1 -1
  167. package/.next/standalone/.next/static/chunks/{35584kn409ad1.js → 06abmgw7cvecc.js} +22 -22
  168. package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
  169. package/.next/standalone/.next/static/chunks/0on899o88y54u.js +1 -0
  170. package/.next/standalone/.next/static/chunks/0p_y4ndalc68e.js +6 -0
  171. package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
  172. package/.next/standalone/.next/static/chunks/0ug1y_6-uwu5t.js +1 -0
  173. package/.next/standalone/.next/static/chunks/13vrxdsdihqd_.css +2 -0
  174. package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
  175. package/.next/standalone/.next/static/chunks/19lrnqxn7ni7i.js +1 -0
  176. package/.next/standalone/.next/static/chunks/{30is-6r00sbd7.js → 1a9qlmo5s678i.js} +1 -1
  177. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  178. package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
  179. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  180. package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
  181. package/.next/standalone/.next/static/chunks/{07ix3lb4gib0x.js → 37nu_nos28w1x.js} +1 -1
  182. package/.next/standalone/.next/static/chunks/{191njgoik03fm.js → 43ylh_2r15k-w.js} +1 -1
  183. package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
  184. package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
  185. package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
  186. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  187. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  188. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  189. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  190. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  191. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  192. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  193. package/.next/standalone/app/audit/audit-styles.css +6 -149
  194. package/.next/standalone/app/components/pause-notices.tsx +111 -0
  195. package/.next/standalone/app/policies/hooks-client.tsx +81 -6
  196. package/.next/standalone/app/settings/page.tsx +31 -0
  197. package/.next/standalone/app/settings/settings-client.tsx +488 -0
  198. package/.next/standalone/components/navbar.tsx +2 -0
  199. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  200. package/.next/standalone/lib/auth/auth-store.ts +4 -4
  201. package/.next/standalone/lib/codex-sessions.ts +2 -1
  202. package/.next/standalone/lib/dashboard-host.ts +101 -0
  203. package/.next/standalone/lib/install-check.ts +22 -7
  204. package/.next/standalone/lib/pi-sessions.ts +99 -14
  205. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  206. package/.next/standalone/lib/telemetry-id.ts +52 -1
  207. package/.next/standalone/lib/telemetry.ts +8 -4
  208. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  209. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  210. package/.next/standalone/node_modules/react/package.json +3 -3
  211. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  212. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  213. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  214. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  215. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  216. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  217. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  218. package/.next/standalone/package.json +23 -10
  219. package/.next/standalone/proxy.ts +111 -1
  220. package/.next/standalone/server.js +1 -1
  221. package/bin/failproofai.mjs +580 -81
  222. package/dist/cli.mjs +12974 -7447
  223. package/dist/worker.mjs +2020 -272
  224. package/lib/auth/api-server-client.ts +2 -3
  225. package/lib/auth/auth-store.ts +4 -4
  226. package/lib/codex-sessions.ts +2 -1
  227. package/lib/dashboard-host.ts +101 -0
  228. package/lib/install-check.ts +22 -7
  229. package/lib/pi-sessions.ts +99 -14
  230. package/lib/telemetry-enabled.ts +54 -0
  231. package/lib/telemetry-id.ts +52 -1
  232. package/lib/telemetry.ts +8 -4
  233. package/package.json +23 -10
  234. package/scripts/build-daemon-packages.mjs +247 -0
  235. package/scripts/daemon-platforms.mjs +42 -0
  236. package/scripts/install-telemetry.mjs +8 -1
  237. package/scripts/launch.ts +25 -3
  238. package/scripts/parse-script-args.ts +27 -1
  239. package/scripts/prune-standalone.mjs +26 -0
  240. package/scripts/publish-aliases.mjs +62 -41
  241. package/scripts/repro-npm-install.sh +155 -0
  242. package/scripts/translate-docs/readme-translator.ts +163 -4
  243. package/scripts/validate-mdx.ts +153 -13
  244. package/src/audit/audit-lock.ts +285 -0
  245. package/src/audit/audit-schedule.ts +111 -0
  246. package/src/audit/cache.ts +18 -4
  247. package/src/audit/cli-adapters/goose.ts +2 -1
  248. package/src/audit/cli-adapters/hermes.ts +28 -10
  249. package/src/audit/cli.ts +191 -35
  250. package/src/audit/dashboard-cache.ts +2 -1
  251. package/src/hooks/backfill-cli.ts +247 -0
  252. package/src/hooks/builtin-policies.ts +390 -2
  253. package/src/hooks/cloud-connection.ts +354 -0
  254. package/src/hooks/cloud-enrollment-cli.ts +325 -0
  255. package/src/hooks/cloud-enrollment.ts +243 -0
  256. package/src/hooks/cloud-introspect.ts +147 -0
  257. package/src/hooks/cloud-managed-policies.ts +169 -0
  258. package/src/hooks/collector-config.ts +234 -0
  259. package/src/hooks/configure-wizard.ts +930 -158
  260. package/src/hooks/custom-hooks-loader.ts +191 -33
  261. package/src/hooks/daemon-client.ts +144 -17
  262. package/src/hooks/daemon-download.ts +264 -30
  263. package/src/hooks/daemon-service.ts +1030 -72
  264. package/src/hooks/first-run-gate.ts +50 -0
  265. package/src/hooks/fp-config.ts +497 -0
  266. package/src/hooks/fp-home.ts +375 -0
  267. package/src/hooks/fp-reset.ts +464 -0
  268. package/src/hooks/handler.ts +185 -6
  269. package/src/hooks/hook-activity-store.ts +85 -20
  270. package/src/hooks/hook-logger.ts +2 -1
  271. package/src/hooks/hook-telemetry.ts +5 -1
  272. package/src/hooks/hooks-config.ts +4 -3
  273. package/src/hooks/loader-utils.ts +171 -8
  274. package/src/hooks/manager.ts +5 -4
  275. package/src/hooks/onboarding-attempt.ts +179 -0
  276. package/src/hooks/onboarding-lock.ts +119 -0
  277. package/src/hooks/policy-types.ts +28 -0
  278. package/src/hooks/session-pause-cli.ts +159 -0
  279. package/src/hooks/session-pause.ts +268 -0
  280. package/src/hooks/setup-state.ts +256 -0
  281. package/src/hooks/tui.ts +119 -4
  282. package/src/hooks/uninstall-cli.ts +349 -0
  283. package/src/hooks/worker-server.ts +65 -3
  284. package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
  286. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
  287. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  289. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  290. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  291. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  292. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  293. package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
  294. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
  295. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  296. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  297. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  298. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  299. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  300. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
  301. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  302. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
  303. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ig795d._.js +0 -3
  304. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1yo1gyz._.js +0 -3
  305. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  306. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  307. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_14q3-7b.js +0 -3
  308. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  309. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  310. package/.next/standalone/.next/static/chunks/0fdx7ikab08-g.js +0 -6
  311. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  312. package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
  313. package/.next/standalone/.next/static/chunks/1d71o1_tav721.js +0 -1
  314. package/.next/standalone/.next/static/chunks/24gs8ip6_spcb.js +0 -1
  315. package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
  316. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
  317. package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
  318. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  319. package/.next/standalone/Cargo.lock +0 -428
  320. package/.next/standalone/Cargo.toml +0 -9
  321. package/.next/standalone/crates/PROTOCOL.md +0 -122
  322. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
  323. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  324. package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
  325. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
  326. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  327. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  328. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  329. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  330. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  331. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  332. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  333. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  334. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  335. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  336. package/.next/standalone/integration-suite/Dockerfile +0 -47
  337. package/.next/standalone/integration-suite/README.md +0 -129
  338. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  339. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  340. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  341. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  342. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  343. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  344. package/.next/standalone/integration-suite/report.js +0 -120
  345. package/.next/standalone/integration-suite/run.sh +0 -159
  346. package/.next/standalone/openclaw-plugin/index.js +0 -223
  347. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  348. package/.next/standalone/openclaw-plugin/package.json +0 -12
  349. package/.next/standalone/osv-scanner.toml +0 -17
  350. package/.next/standalone/pi-extension/index.ts +0 -524
  351. package/.next/standalone/pi-extension/package.json +0 -12
  352. package/.next/standalone/postcss.config.mjs +0 -8
  353. package/.next/standalone/readme-arch-hq.gif +0 -0
  354. package/.next/standalone/rust-toolchain.toml +0 -4
  355. package/src/auth/cli.ts +0 -371
  356. /package/.next/standalone/.next/static/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_buildManifest.js +0 -0
  357. /package/.next/standalone/.next/static/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_clientMiddlewareManifest.js +0 -0
  358. /package/.next/standalone/.next/static/{avJMC2AXvrg13Pbm4QH8F → BZDo0aAHzDwh5ntxgIgpc}/_ssgManifest.js +0 -0
@@ -0,0 +1,464 @@
1
+ /**
2
+ * What happens when this CLI meets a home directory written by a different
3
+ * layout.
4
+ *
5
+ * The decision was wipe-and-re-setup rather than migrate. A migration has to
6
+ * be right on every path or it half-moves a home, and a half-moved home fails
7
+ * in the worst available way: the daemon writes where the dashboard does not
8
+ * read, and an absent directory is indistinguishable from an idle one. A reset
9
+ * is one destructive operation that is either done or not done.
10
+ *
11
+ * ## Where the deletion happens, and where it deliberately does not
12
+ *
13
+ * Only a real CLI command resets. A HOOK never deletes anything, because a
14
+ * hook runs unattended, once per tool call, with an agent waiting on stdout —
15
+ * removing a user's audit history and cursors from inside one would be a
16
+ * destructive act nobody asked for and nobody saw.
17
+ *
18
+ * But a hook must not stay silent either. On a stale layout the merged config
19
+ * resolves to nothing, so every builtin policy quietly stops firing: the
20
+ * machine looks protected and is not. That is the single worst outcome here,
21
+ * so the hook path says so on stderr, every time, until setup is re-run.
22
+ *
23
+ * ## Why the hook does not simply deny
24
+ *
25
+ * Failing closed is this product's instinct, and it is the wrong instinct
26
+ * here. A stale layout is the result of an upgrade the user did not ask to be
27
+ * interrupted by, and this branch has already demonstrated — on this very
28
+ * machine — that a blanket deny takes `UserPromptSubmit` with it and locks the
29
+ * user out of their agent entirely, with no way back except hand-editing JSON.
30
+ * A loud warning that survives until setup runs is the proportionate answer.
31
+ */
32
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync } from "node:fs";
33
+ import { resolve } from "node:path";
34
+ import {
35
+ LAYOUT_VERSION,
36
+ customPoliciesDir,
37
+ failproofaiHome,
38
+ hookActivityDir,
39
+ legacy,
40
+ policiesDir,
41
+ resettablePaths,
42
+ } from "./fp-home";
43
+ import { detectLayout, readConfig, updateConfig, writeVersionFile, type LayoutState } from "./fp-config";
44
+ import {
45
+ daemonServiceStatus,
46
+ daemonStatusCommand,
47
+ daemonVersionSkew,
48
+ isDaemonSupportedPlatform,
49
+ probeDaemonEndToEnd,
50
+ } from "./daemon-service";
51
+
52
+ export interface ResetOutcome {
53
+ /** Paths that existed and were removed. */
54
+ removed: string[];
55
+ /** Basenames of user-authored policy files moved into `custom-policies/`. */
56
+ migrated: string[];
57
+ /** Basenames of decision-log pages carried into layout 2's `hook-activity/`. */
58
+ activity: string[];
59
+ /** The layout that was found before the reset. */
60
+ from: number;
61
+ }
62
+
63
+ /** Script files a person could have written by hand. `.d.ts` is a type stub. */
64
+ const USER_SOURCE_RE = /\.(js|mjs|ts)$/;
65
+
66
+ /**
67
+ * Move hand-written policies from layout 1's `policies/` into layout 2's
68
+ * `policies/custom-policies/`, where the loader actually reads them.
69
+ *
70
+ * Layout 1 documented `~/.failproofai/policies/` as the user scope for personal
71
+ * convention policies, and layout 2 moved the directory the loader opens down a
72
+ * level (`customPoliciesDir()`). Left alone, those files survive the reset —
73
+ * that part is handled by keeping the parent out of `resettablePaths()` — but
74
+ * nothing would ever load them again, which is the same silent enforcement gap
75
+ * by a slower route.
76
+ *
77
+ * Every loadable extension moves, not just the `*policies.{js,mjs,ts}` names
78
+ * the convention loads: a file that misses the naming convention is still
79
+ * source somebody wrote, and `findSkippedPolicyFiles` exists precisely to tell
80
+ * them so. Leaving it behind to be reported against an empty directory helps
81
+ * nobody.
82
+ *
83
+ * A destination that already exists is never overwritten — the source is left
84
+ * in place instead. The parent directory is no longer deleted, so "left in
85
+ * place" means "still there to look at", which is the right outcome for the
86
+ * only case that reaches it (a half-finished earlier migration).
87
+ */
88
+ export function migrateConventionPolicies(): string[] {
89
+ const from = policiesDir();
90
+ if (!existsSync(from)) return [];
91
+ const moved: string[] = [];
92
+ try {
93
+ const entries = readdirSync(from, { withFileTypes: true }).filter(
94
+ (e) => e.isFile() && USER_SOURCE_RE.test(e.name) && !e.name.endsWith(".d.ts"),
95
+ );
96
+ if (entries.length === 0) return [];
97
+
98
+ const to = customPoliciesDir();
99
+ mkdirSync(to, { recursive: true });
100
+ for (const entry of entries) {
101
+ const target = resolve(to, entry.name);
102
+ if (existsSync(target)) continue;
103
+ try {
104
+ renameSync(resolve(from, entry.name), target);
105
+ moved.push(entry.name);
106
+ } catch {
107
+ // One unmovable file must not strand the rest. It stays where it is,
108
+ // and the reset no longer deletes that directory.
109
+ }
110
+ }
111
+ } catch {
112
+ // A policies directory we cannot read is not worth aborting a reset over.
113
+ }
114
+ return moved.sort((a, b) => a.localeCompare(b));
115
+ }
116
+
117
+ /**
118
+ * Carry layout 1's decision log into layout 2, WITHOUT re-shipping it.
119
+ *
120
+ * `cache/hook-activity` was deleted along with the rest of `cache/`, so an
121
+ * upgrade silently discarded every decision the machine had ever recorded —
122
+ * the data the dashboard's activity tab exists to show, and on a connected
123
+ * machine the data an operator had already been billed the collection of.
124
+ *
125
+ * # Why this MOVES rather than copies
126
+ *
127
+ * The collector keys its cursors on `(device, inode)` — deliberately, because
128
+ * the store rotates by RENAMING `current.jsonl`, and a path-keyed cursor would
129
+ * both re-ship the rotated file and carry its offset onto the fresh one.
130
+ * `rename()` preserves the inode, so a moved page is still the file its cursor
131
+ * belongs to and resumes at the right offset. A copy gives every page a NEW
132
+ * inode, which reads as "never seen before" and re-ships all of it.
133
+ *
134
+ * `head_fingerprint` is what makes that safe rather than lucky: the cursor
135
+ * verifies the file's first bytes whenever a resumed cursor's path has changed,
136
+ * which is exactly this situation. It was added for inode REUSE; a migration is
137
+ * the same question asked the other way round.
138
+ *
139
+ * The fallback for `EXDEV` (a `cache/` on a different filesystem — rare, since
140
+ * both live under one home, but possible with bind mounts) is a copy, accepting
141
+ * that those pages re-ship. Ingest dedups on a content hash and collapses them
142
+ * at merge, so the cost is bandwidth, not duplicate rows.
143
+ *
144
+ * # What is deliberately NOT carried
145
+ *
146
+ * `current.count` and `stats.json` are derived state, and two of each cannot be
147
+ * merged without inventing a number. They are dropped and the store rebuilds
148
+ * them. The legacy `current.jsonl` is moved under a PAGE name rather than onto
149
+ * the destination's own `current.jsonl`, which may already exist and may be
150
+ * mid-write — a rotated page is exactly what the store would have made of it.
151
+ */
152
+ export function migrateHookActivity(): string[] {
153
+ const from = legacy.hookActivityDir();
154
+ if (!existsSync(from)) return [];
155
+ const moved: string[] = [];
156
+ try {
157
+ const entries = readdirSync(from, { withFileTypes: true }).filter(
158
+ (e) => e.isFile() && e.name.endsWith(".jsonl"),
159
+ );
160
+ if (entries.length === 0) return [];
161
+
162
+ const to = hookActivityDir();
163
+ mkdirSync(to, { recursive: true });
164
+
165
+ // One timestamp for the whole migration, with a per-file counter, so the
166
+ // names are stable, ordered, and cannot collide with each other.
167
+ const stamp = Date.now();
168
+ let seq = 0;
169
+
170
+ for (const entry of entries) {
171
+ const source = resolve(from, entry.name);
172
+ // `current.jsonl` becomes a page: the destination has its own, and the
173
+ // store's reader treats pages and current identically.
174
+ let name = entry.name === "current.jsonl" ? `page-${stamp}-${seq++}.jsonl` : entry.name;
175
+ let target = resolve(to, name);
176
+ // Never overwrite. A same-named page in the destination is a different
177
+ // file with different records, and losing either is worse than a rename.
178
+ while (existsSync(target)) {
179
+ name = `page-${stamp}-${seq++}.jsonl`;
180
+ target = resolve(to, name);
181
+ }
182
+ try {
183
+ renameSync(source, target);
184
+ moved.push(name);
185
+ } catch (err) {
186
+ // EXDEV: a rename across filesystems. Copy instead and accept the
187
+ // re-ship; a page left behind would be data lost.
188
+ if ((err as NodeJS.ErrnoException)?.code === "EXDEV") {
189
+ try {
190
+ copyFileSync(source, target);
191
+ moved.push(name);
192
+ } catch {
193
+ // Unreadable or undeletable — leave it where it is. `cache/` is no
194
+ // longer deleted wholesale, so "left behind" means "still there".
195
+ }
196
+ }
197
+ }
198
+ }
199
+ } catch {
200
+ // An activity directory we cannot read is not worth aborting a reset over.
201
+ }
202
+ return moved.sort((a, b) => a.localeCompare(b));
203
+ }
204
+
205
+ /**
206
+ * Delete every path layout 1 or layout 2 could have written, then stamp
207
+ * VERSION so the next run reads as current.
208
+ *
209
+ * Enumerated rather than "remove the home directory": a reset must never take
210
+ * out something a future layout adds that this list has not been taught about,
211
+ * and `bin/` and `run/` are excluded on purpose — a downloaded daemon binary is
212
+ * large, version-pinned and re-verified on use, and `run/` holds sockets
213
+ * belonging to a process that may be alive right now.
214
+ */
215
+ export function resetHome(from: number): ResetOutcome {
216
+ // BEFORE the deletions, so a file that is mid-move is never one the reset
217
+ // then walks over.
218
+ const migrated = migrateConventionPolicies();
219
+ const activity = migrateHookActivity();
220
+ const removed: string[] = [];
221
+ for (const path of resettablePaths()) {
222
+ if (!existsSync(path)) continue;
223
+ try {
224
+ rmSync(path, { recursive: true, force: true });
225
+ removed.push(path);
226
+ } catch {
227
+ // A path we cannot remove is not worth aborting the reset over; the
228
+ // VERSION stamp below is what stops this repeating, and a leftover
229
+ // directory from an old layout is inert once nothing reads it.
230
+ }
231
+ }
232
+ writeVersionFile();
233
+ return { removed, migrated, activity, from };
234
+ }
235
+
236
+ export interface LayoutCheck {
237
+ state: LayoutState;
238
+ /** Lines to print. Empty when there is nothing to say. */
239
+ lines: string[];
240
+ /** True when the caller should stop rather than continue. */
241
+ fatal: boolean;
242
+ /**
243
+ * True when this call actually reset the home.
244
+ *
245
+ * The caller must force setup when it is set. A reset removes the global
246
+ * policy config, but `isConfigured()` is a union that also counts the agent
247
+ * CLIs' settings files — which the reset deliberately leaves alone — so the
248
+ * machine still reads as configured, the wizard is skipped, and
249
+ * `markLauncherSeen()` back-fills the marker so every later run skips it too.
250
+ * The user is left with hooks firing on every tool call against no policies
251
+ * at all, and nothing ever says so again.
252
+ */
253
+ didReset: boolean;
254
+ }
255
+
256
+ /**
257
+ * The interactive path: reset a stale home, refuse a future one.
258
+ *
259
+ * A future layout is refused rather than reset because the two failures are
260
+ * not the same. An older home can be rebuilt by re-running setup; a home
261
+ * written by a NEWER CLI holds data this build cannot read but a simple
262
+ * upgrade could, and deleting it would destroy something recoverable.
263
+ */
264
+ export async function checkLayoutForCli(): Promise<LayoutCheck> {
265
+ const state = detectLayout();
266
+
267
+ if (state.kind === "future") {
268
+ return {
269
+ state,
270
+ fatal: true,
271
+ didReset: false,
272
+ lines: [
273
+ `This machine's failproofai directory was written by a newer version`,
274
+ `(layout ${state.found}; this build speaks ${LAYOUT_VERSION}).`,
275
+ ``,
276
+ `Upgrade rather than reset — the data is fine, this build just cannot read it:`,
277
+ ` npm install -g failproofai@latest`,
278
+ ],
279
+ };
280
+ }
281
+
282
+ if (state.kind === "stale") {
283
+ const { removed, migrated, activity } = resetHome(state.found);
284
+ return {
285
+ state,
286
+ fatal: false,
287
+ didReset: true,
288
+ lines: [
289
+ `failproofai reorganised ${failproofaiHome()} in this version.`,
290
+ // "activity history" was in this sentence while the reset was deleting
291
+ // it. It is carried over now, so the message says what was KEPT as well
292
+ // as what went — a user who reads "removed" and nothing else has no way
293
+ // to know their decision log survived.
294
+ `Removed ${removed.length} item(s) from the old layout — policy config and`,
295
+ `audit cache. Your daemon binary and decision history were kept.`,
296
+ // Named individually rather than counted. These are files a person
297
+ // wrote; "moved 3 items" is not something you can check at a glance,
298
+ // and the whole point of saying it is that they can.
299
+ ...(migrated.length > 0
300
+ ? [
301
+ ``,
302
+ `Kept your own policy file(s) and moved them to where this version`,
303
+ `loads them (${customPoliciesDir()}):`,
304
+ ...migrated.map((name) => ` ${name}`),
305
+ ]
306
+ : []),
307
+ // Counted, not named. Unlike policy files these are machine-written
308
+ // pages with generated names — a list of them tells the reader nothing
309
+ // they could act on, where the COUNT answers the only question they
310
+ // have: did my history survive.
311
+ ...(activity.length > 0
312
+ ? [
313
+ ``,
314
+ `Carried ${activity.length} page(s) of decision history into ${hookActivityDir()}.`,
315
+ ]
316
+ : []),
317
+ ``,
318
+ `Run \`failproofai config\` to set up again.`,
319
+ ],
320
+ };
321
+ }
322
+
323
+ if (state.kind === "absent") writeVersionFile();
324
+ return {
325
+ state,
326
+ fatal: false,
327
+ didReset: false,
328
+ lines: [...(await healDaemonFlag()), ...staleDaemonHint()],
329
+ };
330
+ }
331
+
332
+ /**
333
+ * Clear `daemonConfigured` when the service it points at is provably gone.
334
+ *
335
+ * This exists because the combination it repairs bricked a real machine during
336
+ * development. `daemonConfigured` makes every hook route through failproofaid
337
+ * and FAIL CLOSED when it cannot be reached — so removing the service without
338
+ * first clearing the flag denies every tool call on the box, including
339
+ * `UserPromptSubmit`, which locks the user out of their agent entirely. There
340
+ * was no command to undo it: the only recovery was hand-editing JSON.
341
+ *
342
+ * Deliberately keyed on "not-installed", never on "stopped". A stopped service
343
+ * is usually a restart in progress, and clearing the flag there would silently
344
+ * downgrade a healthy machine to the in-process path — trading a loud, correct
345
+ * failure for a quiet, wrong one.
346
+ */
347
+ async function healDaemonFlag(): Promise<string[]> {
348
+ try {
349
+ const cfg = readConfig();
350
+ if (!cfg.daemon.configured) return [];
351
+ if (!isDaemonSupportedPlatform()) return [];
352
+
353
+ const status = daemonServiceStatus();
354
+ if (status === "not-installed") {
355
+ updateConfig({ daemon: { configured: false } });
356
+ return [
357
+ `failproofaid is no longer installed, but this machine was still configured`,
358
+ `to require it — which denies every tool call. Cleared that flag; policies`,
359
+ `now evaluate in-process. Run \`failproofai config\` to reinstall the daemon.`,
360
+ ``,
361
+ ];
362
+ }
363
+
364
+ // Installed, and systemd has refused to start it: one of the paths the unit
365
+ // is gated on is gone. This is what `npm rm -g failproofai` leaves behind —
366
+ // npm runs no uninstall script, so the unit survives the package that
367
+ // supplies its worker, and every tool call on the machine then denies with
368
+ // nothing to point at.
369
+ //
370
+ // Treated like "not-installed" rather than like "stopped" because systemd
371
+ // has already made the call and will keep making it at every boot. That is
372
+ // the distinction `condition-failed` exists to carry; see its definition.
373
+ if (status === "condition-failed") {
374
+ updateConfig({ daemon: { configured: false } });
375
+ return [
376
+ `failproofaid is installed but cannot start — a file its service requires is`,
377
+ `gone (most often because failproofai was removed with \`npm rm -g\`, which`,
378
+ `deletes the worker but leaves the service behind). This machine was`,
379
+ `configured to require the daemon, which denies every tool call, so that flag`,
380
+ `is cleared; policies now evaluate in-process.`,
381
+ ``,
382
+ `Run \`failproofai uninstall\` to remove the leftover service, or`,
383
+ `\`failproofai config\` to rebuild it. \`${daemonStatusCommand()}\` names the missing path.`,
384
+ ``,
385
+ ];
386
+ }
387
+
388
+ // Installed and RUNNING is not the same as working, and the difference is
389
+ // a total lockout. `ExecStart` bakes in `process.execPath`, so an
390
+ // `nvm uninstall 20` months later leaves a unit systemd still calls active
391
+ // whose worker dies on every spawn. Nothing else catches it: the install
392
+ // probe cannot run retroactively, and the not-installed branch above never
393
+ // fires because the unit is very much installed.
394
+ //
395
+ // Clearing the flag is the whole repair, and it is deliberately NOT
396
+ // accompanied by an uninstall: this runs unprompted at the top of whatever
397
+ // command the user typed, and tearing down a root-owned service from there
398
+ // is not a decision to make on their behalf. Removing and reinstalling the
399
+ // unit is the wizard's job, where a person is present — see the
400
+ // `daemonAlreadyHealthy` probe in `configure-wizard.ts`.
401
+ if (status === "running" && !(await probeDaemonEndToEnd())) {
402
+ updateConfig({ daemon: { configured: false } });
403
+ return [
404
+ `failproofaid is running but cannot evaluate policies — its worker process`,
405
+ `will not start (most often because the Node install its service was built`,
406
+ `against is gone). This machine was configured to require it, which denies`,
407
+ `every tool call, so that flag is cleared; policies now evaluate in-process.`,
408
+ `Run \`failproofai config\` to rebuild the service.`,
409
+ ``,
410
+ ];
411
+ }
412
+ return [];
413
+ } catch {
414
+ // Never let a self-heal attempt break the command the user actually typed.
415
+ return [];
416
+ }
417
+ }
418
+
419
+ /**
420
+ * One line when the daemon is older than the CLI.
421
+ *
422
+ * Deliberately NOT on the hook path. A stale daemon still enforces every policy
423
+ * correctly — it is slower to notice an upgrade, not broken — so a warning once
424
+ * per tool call would be noise about something that is working. CLI commands
425
+ * are where a person is present to act on it.
426
+ */
427
+ function staleDaemonHint(): string[] {
428
+ try {
429
+ const skew = daemonVersionSkew();
430
+ if (!skew) return [];
431
+ return [
432
+ `[failproofai] daemon is ${skew.installed}, CLI is ${skew.expected} — ` +
433
+ `run \`failproofai config\` to update it.`,
434
+ ``,
435
+ ];
436
+ } catch {
437
+ return [];
438
+ }
439
+ }
440
+
441
+ /**
442
+ * The hook path: never delete, never deny, but never stay quiet either.
443
+ *
444
+ * Returns a single stderr line when the layout is not current. Hooks run once
445
+ * per tool call, so this repeats — deliberately. A warning that appears once
446
+ * and then stops is a warning nobody sees, and the state it describes (a
447
+ * machine whose global policies have silently stopped firing) persists until
448
+ * somebody acts on it.
449
+ */
450
+ export function layoutWarningForHook(): string | null {
451
+ const state = detectLayout();
452
+ if (state.kind === "current" || state.kind === "absent") return null;
453
+ if (state.kind === "future") {
454
+ return (
455
+ `[failproofai] this directory was written by a newer version ` +
456
+ `(layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` +
457
+ `Upgrade failproofai.`
458
+ );
459
+ }
460
+ return (
461
+ `[failproofai] setup predates this version — global policies are NOT being enforced. ` +
462
+ `Run \`failproofai config\` to re-create it.`
463
+ );
464
+ }