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
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { resolve, join } from "node:path";
5
5
  import { statSync } from "node:fs";
6
- import { readFile, writeFile, stat, open } from "node:fs/promises";
6
+ import { readFile, writeFile } from "node:fs/promises";
7
7
  import { execSync, execFileSync } from "node:child_process";
8
8
  import { homedir } from "node:os";
9
9
  import type { BuiltinPolicyDefinition, PolicyContext, PolicyResult, PolicyParamsSchema } from "./policy-types";
@@ -164,12 +164,201 @@ const ENV_FILE_PATH_RE = /(?:^|[\\/])\.env(?:\.|$)/;
164
164
  const ENV_CMD_RE = /\.env(?:\b|\s|$|\.)/;
165
165
 
166
166
  // blockSudo
167
- const SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
168
167
  const PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
169
168
  const RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
170
169
 
171
170
  // blockCurlPipeSh
172
171
  const CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
172
+ /**
173
+ * `failproofai config --pause` in any of its reachable spellings — direct, via
174
+ * `npx`/`bunx`/`pnpm dlx`, by absolute path, or through the `configure`/`setup`
175
+ * aliases the CLI normalizes. Only `--pause` matches: `--resume` and `--status`
176
+ * restore or merely report enforcement, so an agent running either is harmless.
177
+ *
178
+ * Two details carry the whole policy, and an earlier version got both wrong:
179
+ *
180
+ * `failproofai[^\s]*` rather than `\bfailproofai\b`. A word boundary stops at
181
+ * the character after the name, so every ordinary way of pinning or pathing the
182
+ * binary walked straight through: `npx failproofai@latest`, `npx -y
183
+ * failproofai@0.0.16`, `bunx failproofai@latest`, and
184
+ * `node /usr/lib/node_modules/failproofai/bin/failproofai.mjs`. The suffix has
185
+ * to be absorbed by the same token.
186
+ *
187
+ * `\s+--pause` rather than `\s--pause`. With a single `\s`, two spaces before
188
+ * the flag — which no shell cares about and any agent may emit — did not match.
189
+ *
190
+ * This still raises the bar rather than closing the class: base64, a wrapper
191
+ * script, an alias or a shell variable all still reach the same state. Closing
192
+ * it properly means a pause cannot originate from a tool call at all.
193
+ *
194
+ * Matched against BOTH the raw command and its shell-unescaped form (see
195
+ * `stripShellQuoting`), because a shell reconstructs the binary name from
196
+ * fragments a regex over the raw string cannot see.
197
+ *
198
+ * ANCHORED ON COMMAND POSITION, which is the third thing an earlier version got
199
+ * wrong and the most expensive: with no anchor, the pattern fired on any string
200
+ * that merely CONTAINED the invocation, so `grep -rn "failproofai config
201
+ * --pause" docs/`, `git commit -m "docs: explain failproofai config --pause"`,
202
+ * `gh pr create --body "...failproofai config --pause"` and `git log --grep`
203
+ * were all denied. This policy is `defaultEnabled`, and this repo's own
204
+ * CHANGELOG and `docs/built-in-policies.mdx` contain that literal string, so
205
+ * the first thing it did on a real machine was block someone reading the
206
+ * documentation for it. Its sibling `FAILPROOFAI_CLI_RE` was anchored from the
207
+ * start; this one was not.
208
+ *
209
+ * The distinction that matters is structural, not textual: in a real
210
+ * invocation the binary sits where the shell will look for a COMMAND — first in
211
+ * a segment, or behind a runner (`npx`, `pnpm dlx`, `node`, `sh -c`) and its
212
+ * flags. In every false positive above it sits where an ARGUMENT goes.
213
+ */
214
+
215
+ /**
216
+ * Split a command into the pieces a shell would treat as separate commands.
217
+ *
218
+ * Includes `(`, `)` and backtick alongside the ordinary operators so that
219
+ * command substitution — `echo $(failproofai config --pause)` — puts the inner
220
+ * invocation in command position of its own segment rather than hiding it as an
221
+ * argument of `echo`.
222
+ */
223
+ const SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
224
+
225
+ /**
226
+ * Tokens that stand in front of the real binary without being it: package
227
+ * runners, interpreters, and the `exec`-alikes. Compared by basename, so
228
+ * `/usr/bin/env` and `env` behave identically.
229
+ */
230
+ const COMMAND_PREFIX_TOKENS = new Set([
231
+ "npx", "bunx", "pnpx", "npm", "pnpm", "yarn", "dlx", "exec", "run",
232
+ "node", "bun", "deno", "env", "command", "builtin", "nohup", "setsid",
233
+ "time", "timeout", "nice", "stdbuf", "xargs", "sudo", "doas",
234
+ "sh", "bash", "zsh", "dash", "ksh", "fish", "ash",
235
+ ]);
236
+
237
+ /** The binary itself, however it is pinned or pathed: `failproofai`,
238
+ * `failproofai@latest`, `/usr/local/bin/failproofai`,
239
+ * `./node_modules/.bin/failproofai`, `.../failproofai/bin/failproofai.mjs`. */
240
+ const SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
241
+
242
+ /** `config`, and the two aliases the entrypoint normalizes to it. */
243
+ const CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
244
+
245
+ /** `--pause`, with or without an `=value` tail. */
246
+ const PAUSE_FLAG_RE = /^--pause(?:=|$)/;
247
+
248
+ /** An `FOO=bar` prefix assignment, which a shell consumes before the command. */
249
+ const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
250
+
251
+ /**
252
+ * A bare operand belonging to the runner in front of it — `timeout 30 …`,
253
+ * `nice -n 5 …`. Skipped so the runner's argument is not mistaken for the
254
+ * command. Safe to skip unconditionally: no binary is named `30` or `5m`.
255
+ */
256
+ const RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
257
+
258
+ /**
259
+ * True when `command` runs `failproofai config --pause` in command position.
260
+ *
261
+ * Residual false positive, deliberately accepted: a quoted argument that itself
262
+ * contains a shell operator AND the whole invocation — `git commit -m "x;
263
+ * failproofai config --pause"` — survives `stripShellQuoting` as two segments
264
+ * and denies. That is a far narrower miss than matching every mention, and it
265
+ * errs toward refusing rather than toward silently suspending enforcement.
266
+ */
267
+ function namesSelfPause(command: string): boolean {
268
+ for (const segment of command.split(SEGMENT_SEPARATORS)) {
269
+ const tokens = segment.split(/\s+/).filter(Boolean);
270
+ let i = 0;
271
+ // Walk off everything a shell resolves before it settles on the command:
272
+ // prefix assignments, redirections, runner flags, and the runners.
273
+ while (i < tokens.length) {
274
+ const token = tokens[i];
275
+ const isSkippable =
276
+ ENV_ASSIGNMENT_RE.test(token) ||
277
+ token.startsWith("-") ||
278
+ token.startsWith(">") ||
279
+ token.startsWith("<") ||
280
+ RUNNER_OPERAND_RE.test(token) ||
281
+ COMMAND_PREFIX_TOKENS.has(token.slice(token.lastIndexOf("/") + 1));
282
+ if (!isSkippable) break;
283
+ i++;
284
+ }
285
+ if (i >= tokens.length) continue;
286
+ if (!SELF_BINARY_TOKEN_RE.test(tokens[i])) continue;
287
+
288
+ // The binary IS the command here. Now require `config … --pause` in the
289
+ // argument order the CLI actually accepts.
290
+ const args = tokens.slice(i + 1);
291
+ const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
292
+ if (configAt === -1) continue;
293
+ if (args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a))) return true;
294
+ }
295
+ return false;
296
+ }
297
+
298
+ /**
299
+ * Decode one ANSI-C `$'...'` body (the text between the quotes) to the bytes a
300
+ * shell would produce. Covers the escape forms that can reconstruct an ASCII
301
+ * identifier: `\xHH`, octal `\NNN`, `\uHHHH`, `\UHHHHHHHH`, and the named
302
+ * controls. An out-of-range or malformed sequence is left as-is rather than
303
+ * throwing — this runs on adversarial input and must never crash the hook.
304
+ */
305
+ function decodeAnsiC(body: string): string {
306
+ return body.replace(
307
+ /\\(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,
308
+ (_, seq: string) => {
309
+ try {
310
+ if (seq[0] === "x") return String.fromCharCode(parseInt(seq.slice(1), 16));
311
+ if (seq[0] === "u" || seq[0] === "U") return String.fromCodePoint(parseInt(seq.slice(1), 16));
312
+ if (/^[0-7]+$/.test(seq)) return String.fromCharCode(parseInt(seq, 8));
313
+ } catch {
314
+ return seq;
315
+ }
316
+ const named: Record<string, string> = {
317
+ a: "\x07", b: "\b", e: "\x1b", f: "\f", n: "\n",
318
+ r: "\r", t: "\t", v: "\v", "\\": "\\", "'": "'", '"': '"', "?": "?",
319
+ };
320
+ return named[seq] ?? seq;
321
+ },
322
+ );
323
+ }
324
+
325
+ /**
326
+ * Collapse the quoting a POSIX shell resolves LEXICALLY — before it execs a
327
+ * command — so a literal matcher sees roughly what will actually run.
328
+ *
329
+ * Two adversarial red-team rounds defeated the pattern above by reassembling
330
+ * the binary name from fragments the regex could not see: first backslash and
331
+ * quote splitting (`fail\proofai`, `fail"proof"ai`, `f\a\i\l\p\r\o\o\f\a\i`),
332
+ * then ANSI-C quoting (`$'fail\x70roofai'`, octal `\160`, unicode `p`).
333
+ * Each executes the real pause. This now normalizes all three lexical forms:
334
+ * ANSI-C `$'...'` bodies are decoded first (the generic backslash pass below
335
+ * would otherwise turn `\x70` into `x70`), then surrounding single/double
336
+ * quotes and lone backslash-escapes are removed.
337
+ *
338
+ * The boundary is principled, not arbitrary: everything handled here is pure
339
+ * shell LEXING. What remains — a variable (`f=failproofai; $f …`), command
340
+ * substitution (`$(printf failproofai)`), `eval`, an alias, a base64 pipe —
341
+ * requires the shell to EXECUTE something to reconstruct the name, which no
342
+ * PreToolUse hook inspecting a command string can follow. That class is the one
343
+ * the header comment above calls out, and the only real closure is to make the
344
+ * pause ACTION itself refuse to originate from a tool call — deferred with the
345
+ * rest of the daemon-side redesign.
346
+ */
347
+ function stripShellQuoting(command: string): string {
348
+ // Line continuation FIRST. A shell deletes a backslash-newline pair entirely
349
+ // and rejoins the fragments, so `fail\<newline>proofai` runs the real binary.
350
+ // The generic `\(.)` strip below cannot catch it — JS `.` excludes newline —
351
+ // so it has to be removed before that pass, or the fragments never join.
352
+ // (A third red-team round rode exactly this in.) `\r?\n` also covers a
353
+ // CRLF-joined line.
354
+ const joined = command.replace(/\\\r?\n/g, "");
355
+ // `$'...'` next, honoring `\'` inside the body, so its own escapes decode
356
+ // before the generic backslash-strip can mangle them.
357
+ const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body: string) =>
358
+ decodeAnsiC(body),
359
+ );
360
+ return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
361
+ }
173
362
  const PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
174
363
 
175
364
  // blockForcePush
@@ -559,10 +748,163 @@ function blockEnvFiles(ctx: PolicyContext): PolicyResult {
559
748
  return allow();
560
749
  }
561
750
 
751
+ /**
752
+ * True when a segment runs an elevation binary IN COMMAND POSITION.
753
+ *
754
+ * Anchored structurally rather than by token, for the reason `namesSelfPause`
755
+ * is: `SUDO_RE` required the literal token `sudo` at a command boundary, so
756
+ * **`/usr/bin/sudo rm -rf /` was ALLOWED** — a direct invocation, no
757
+ * obfuscation, one absolute path away from root on a `defaultEnabled` guard.
758
+ * Every other form the shell resolves before settling on the binary — prefix
759
+ * assignments, redirections, runners and their flags — is walked off first, and
760
+ * the comparison is on the BASENAME so a path cannot hide the name.
761
+ *
762
+ * `doas` is included because it is the same capability under a different name;
763
+ * a machine with `doas` installed and only `sudo` blocked is not blocked.
764
+ *
765
+ * This raises the bar; it does not close the class. `bash -c "sudo …"`, a
766
+ * variable (`S=sudo; $S …`) and base64-through-a-pipe all still reach root,
767
+ * because static inspection of one command string cannot follow them. Closing
768
+ * that properly needs enforcement below the shell, not a better regex.
769
+ */
770
+ /**
771
+ * Split a command into per-segment TOKEN LISTS the way a shell reads it.
772
+ *
773
+ * Distinct from `shellSegments` below, which returns raw segment STRINGS and is
774
+ * what the deletion policies want. Named apart deliberately: defining a second
775
+ * `shellSegments` here shadowed that one, silently broke every `rm -rf` check,
776
+ * and disabled this matcher too — caught only because `tsc` reports duplicate
777
+ * implementations.
778
+ *
779
+ * Segmentation happens on the RAW string and skips separators inside quotes,
780
+ * which is the whole point: stripping quoting FIRST turns `\|` — a literal pipe
781
+ * in a grep alternation — into a real separator, so `grep "a\|sudo b"` parses
782
+ * as a command called `sudo` and gets denied. That is a common enough pattern
783
+ * that over-blocking it is worse than the evasion it would catch.
784
+ *
785
+ * Each token is unquoted individually afterwards, which still defeats `\sudo`
786
+ * and `"sudo"` without ever letting quote removal change where the boundaries
787
+ * are. Quoted tokens are returned separately so a caller can decide whether
788
+ * their CONTENTS deserve a second look — see `EVAL_FLAG_RE`.
789
+ */
790
+ function quoteAwareSegments(command: string): Array<string[]> {
791
+ const segments: Array<string[]> = [];
792
+ let tokens: string[] = [];
793
+ let token = "";
794
+ let quote: '"' | "'" | null = null;
795
+ const endToken = () => {
796
+ if (token) tokens.push(token);
797
+ token = "";
798
+ };
799
+ const endSegment = () => {
800
+ endToken();
801
+ if (tokens.length) segments.push(tokens);
802
+ tokens = [];
803
+ };
804
+ for (let i = 0; i < command.length; i++) {
805
+ const c = command[i];
806
+ if (quote) {
807
+ // Inside single quotes a backslash is literal; inside double quotes it escapes.
808
+ if (c === "\\" && quote === '"' && i + 1 < command.length) {
809
+ token += command[++i];
810
+ continue;
811
+ }
812
+ if (c === quote) {
813
+ quote = null;
814
+ continue;
815
+ }
816
+ token += c;
817
+ continue;
818
+ }
819
+ if (c === "\\" && i + 1 < command.length) {
820
+ // An escaped character is literal — `\sudo` is still sudo, and `\|` is a
821
+ // pipe CHARACTER rather than a separator.
822
+ token += command[++i];
823
+ continue;
824
+ }
825
+ if (c === '"' || c === "'") {
826
+ quote = c;
827
+ continue;
828
+ }
829
+ if (/\s/.test(c)) {
830
+ endToken();
831
+ continue;
832
+ }
833
+ if (/[;&|\n\r(){}`]/.test(c)) {
834
+ endSegment();
835
+ continue;
836
+ }
837
+ token += c;
838
+ }
839
+ endSegment();
840
+ return segments;
841
+ }
842
+
843
+ /** `-c` and friends: the flags whose ARGUMENT is a command, not data. */
844
+ const EVAL_FLAG_RE = /^-{1,2}c$/;
845
+
846
+ /**
847
+ * True when a command runs an elevation binary IN COMMAND POSITION.
848
+ *
849
+ * Anchored structurally rather than by token, for the reason `namesSelfPause`
850
+ * is: `SUDO_RE` required the literal token `sudo` at a command boundary, so
851
+ * **`/usr/bin/sudo rm -rf /` was ALLOWED** — a direct invocation, no
852
+ * obfuscation, one absolute path away from root on a `defaultEnabled` guard.
853
+ * Every other form the shell resolves before settling on the binary — prefix
854
+ * assignments, redirections, runners and their flags — is walked off first, and
855
+ * the comparison is on the BASENAME so a path cannot hide the name.
856
+ *
857
+ * `doas` is included because it is the same capability under a different name;
858
+ * a machine with `doas` installed and only `sudo` blocked is not blocked.
859
+ *
860
+ * A quoted argument is re-examined ONLY when the command that owns it is a
861
+ * shell runner invoked with an eval flag (`bash -c "sudo …"`). That distinction
862
+ * is load-bearing: `bash -c "sudo x"` and `grep "a|sudo x"` are identical from
863
+ * the outside, and the only thing separating an execution from a search string
864
+ * is whether the receiving binary evaluates its argument.
865
+ *
866
+ * This raises the bar; it does not close the class. A variable (`S=sudo; $S …`),
867
+ * base64 through a pipe, and a wrapper script on disk all still reach root,
868
+ * because static inspection of one command string cannot follow them. Closing
869
+ * that properly needs enforcement below the shell, not a better matcher.
870
+ */
871
+ function namesElevation(command: string, depth = 0): boolean {
872
+ for (const tokens of quoteAwareSegments(command)) {
873
+ let i = 0;
874
+ while (i < tokens.length) {
875
+ const token = tokens[i];
876
+ const base = token.slice(token.lastIndexOf("/") + 1);
877
+ // `sudo`/`doas` are themselves in COMMAND_PREFIX_TOKENS (they legitimately
878
+ // stand in front of another binary), so they must be tested BEFORE the
879
+ // skip list or the walk would step straight over the thing being looked for.
880
+ if (base === "sudo" || base === "doas") return true;
881
+ const isSkippable =
882
+ ENV_ASSIGNMENT_RE.test(token) ||
883
+ token.startsWith("-") ||
884
+ token.startsWith(">") ||
885
+ token.startsWith("<") ||
886
+ RUNNER_OPERAND_RE.test(token) ||
887
+ COMMAND_PREFIX_TOKENS.has(base);
888
+ if (!isSkippable) break;
889
+ // `sh -c "…"` / `bash -c "…"`: the next token is a COMMAND. Bounded
890
+ // recursion so a nested `bash -c "bash -c …"` cannot spin.
891
+ if (EVAL_FLAG_RE.test(token) && depth < 3 && i + 1 < tokens.length) {
892
+ if (namesElevation(tokens[i + 1], depth + 1)) return true;
893
+ }
894
+ i++;
895
+ }
896
+ }
897
+ return false;
898
+ }
899
+
562
900
  function blockSudo(ctx: PolicyContext): PolicyResult {
563
901
  if (ctx.toolName !== "Bash") return allow();
564
902
  const cmd = getCommand(ctx).trimStart();
565
- if (SUDO_RE.test(cmd) || cmd.startsWith("sudo ")) {
903
+ // ONE pass. `shellSegments` unquotes each token individually, so `\sudo` and
904
+ // `"sudo"` are still caught — without a whole-string strip, which is what
905
+ // turned an escaped pipe in `grep "a\|sudo b"` into a segment separator and
906
+ // denied an ordinary search.
907
+ if (namesElevation(cmd)) {
566
908
  // Check allowPatterns — match against parsed tokens, not raw string
567
909
  const allowPatterns = ((ctx.params?.allowPatterns ?? []) as string[]);
568
910
  if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p))) return allow();
@@ -579,6 +921,42 @@ function blockSudo(ctx: PolicyContext): PolicyResult {
579
921
  return allow();
580
922
  }
581
923
 
924
+ /**
925
+ * A pause the agent can issue is not a guardrail.
926
+ *
927
+ * `failproofai config --pause` is a human affordance: you watched the agent get
928
+ * blocked, you judged the block wrong, you suspended enforcement for a bit. An
929
+ * agent that can run it turns every other policy advisory — it need only shell
930
+ * out once to switch them all off, and the pause even survives into later turns.
931
+ *
932
+ * This raises the bar rather than closing the class: an agent can still reach
933
+ * the same state obfuscated (base64, a wrapper script, an alias). Closing it
934
+ * properly means the pause cannot originate from a tool call at all — a
935
+ * dashboard action, or a daemon that accepts it only from a TTY. Until then,
936
+ * the honest description of this policy is "stops the obvious attempt".
937
+ *
938
+ * It is deliberately NOT redundant with `block-failproofai-commands`, which
939
+ * blocks the CLI far more broadly. That one anchors on a command boundary
940
+ * (`FAILPROOFAI_CLI_RE`), so `npx -y failproofai config --pause` does not match
941
+ * it — and being broad, it is a policy people plausibly switch off so agents
942
+ * can run `failproofai audit`. Neither gap should leave pausing reachable, so
943
+ * this stays narrow, matches the runner forms, and survives that one being off.
944
+ */
945
+ function blockSelfPause(ctx: PolicyContext): PolicyResult {
946
+ if (ctx.toolName !== "Bash") return allow();
947
+ const cmd = getCommand(ctx);
948
+ // The raw command AND its shell-unescaped form: a shell strips quotes and
949
+ // backslashes before running the binary, so `fail\proofai config --pause`
950
+ // reaches the pause CLI even though the literal name is broken.
951
+ if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
952
+ return deny(
953
+ "Pausing failproofai enforcement is a human action, not an agent one. " +
954
+ "If a policy is blocking legitimate work, say so and let the operator decide.",
955
+ );
956
+ }
957
+ return allow();
958
+ }
959
+
582
960
  function blockCurlPipeSh(ctx: PolicyContext): PolicyResult {
583
961
  if (ctx.toolName !== "Bash") return allow();
584
962
  const cmd = getCommand(ctx);
@@ -1752,6 +2130,16 @@ export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = [
1752
2130
  },
1753
2131
  } satisfies PolicyParamsSchema,
1754
2132
  },
2133
+ {
2134
+ name: "block-self-pause",
2135
+ displayTitle: "Tried to pause failproofai enforcement",
2136
+ impact: "An agent that can pause enforcement can switch off every other policy.",
2137
+ description: "Block agents from pausing failproofai enforcement",
2138
+ fn: blockSelfPause,
2139
+ match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
2140
+ defaultEnabled: true,
2141
+ category: "Dangerous Commands",
2142
+ },
1755
2143
  {
1756
2144
  name: "block-sudo",
1757
2145
  displayTitle: "Tried to run a command with sudo",