failproofai 0.0.13 → 0.0.14-beta.0

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 (283) hide show
  1. package/.next/standalone/.factory/hooks.json +112 -0
  2. package/.next/standalone/.next/BUILD_ID +1 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  22. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  23. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  24. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  26. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  27. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/audit/run/route.js +3 -3
  29. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +2 -1
  36. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/index.html +1 -1
  41. package/.next/standalone/.next/server/app/index.rsc +15 -15
  42. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  43. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  44. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  46. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  51. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  52. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -3
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  65. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +3 -0
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +3 -0
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +3 -0
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jllheg._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +3 -0
  75. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xuaoik._.js +3 -3
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d9qen3._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +3 -0
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/{_1z062h5._.js → _1vslkoe._.js} +2 -2
  93. package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +24 -12
  96. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cz9as._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +2 -2
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +3 -0
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07iom4f._.js → [root-of-the-server]__07qfsfa._.js} +2 -2
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ge8e7w._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h12me5._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +3 -0
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11rtg6s._.js +1 -1
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +12 -9
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +4 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1d8omgc._.js +1 -1
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dky4g0._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__212nf49._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  124. package/.next/standalone/.next/server/chunks/ssr/_0_9_ytf._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  126. package/.next/standalone/.next/server/chunks/ssr/{_0ul5rf_._.js → _1_ozgif._.js} +2 -2
  127. package/.next/standalone/.next/server/chunks/ssr/_1p0-leb._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  129. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  131. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/lib_antigravity-projects_ts_0_6zf11._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/lib_devin-projects_ts_0a64qdy._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/lib_factory-projects_ts_1uyaahq._.js +3 -0
  135. package/.next/standalone/.next/server/chunks/ssr/lib_goose-projects_ts_0zkscri._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_13p7zob.js → node_modules_html-to-image_es_index_0g5r64e.js} +1 -1
  138. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0-uvagv.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_03c7gi5.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_09p-8om.js +1 -1
  141. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0v-kfiu.js +1 -1
  142. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0xrgzyz.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1806lsc.js +1 -1
  144. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1j6dd-e.js +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1sa65r-.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  148. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  149. package/.next/standalone/.next/server/pages/404.html +1 -1
  150. package/.next/standalone/.next/server/pages/500.html +1 -1
  151. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  152. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  153. package/.next/standalone/.next/static/chunks/{23c7v2svx90dp.js → 003uz088wbmz7.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{0vc0eak148vo6.js → 0405htbxay9vd.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/0iinhn4417nk1.js +1 -0
  156. package/.next/standalone/.next/static/chunks/1374x9bv_s_vg.js +6 -0
  157. package/.next/standalone/.next/static/chunks/{30ysv-_8jj052.js → 17sdztgbcc0fi.js} +1 -1
  158. package/.next/standalone/.next/static/chunks/{2cejn75gug3j8.js → 2m_tbl39fcedn.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/{36wdtkgdf0zrd.js → 2o3ql0n5owqsw.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/{0wtagd8a5gwq-.js → 2ufuwhv03yvtb.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/35-90m9py64d4.js +1 -0
  162. package/.next/standalone/.next/static/chunks/3r--yyjkp9_7b.css +2 -0
  163. package/.next/standalone/.next/static/chunks/43weu8f59tlix.js +1 -0
  164. package/.next/standalone/.next/static/chunks/{05apzuand0c22.js → 454dyizqik6t9.js} +2 -2
  165. package/.next/standalone/app/api/audit/run/route.ts +2 -0
  166. package/.next/standalone/app/audit/_components/empty-state.tsx +1 -1
  167. package/.next/standalone/app/globals.css +25 -1
  168. package/.next/standalone/app/policies/hooks-client.tsx +2 -4
  169. package/.next/standalone/app/project/[name]/page.tsx +30 -10
  170. package/.next/standalone/app/project/[name]/session/[sessionId]/page.tsx +71 -21
  171. package/.next/standalone/assets/logos/antigravity.svg +1 -0
  172. package/.next/standalone/assets/logos/devin.svg +1 -0
  173. package/.next/standalone/assets/logos/factory-dark.png +0 -0
  174. package/.next/standalone/assets/logos/factory-light.png +0 -0
  175. package/.next/standalone/assets/logos/goose-dark.svg +1 -0
  176. package/.next/standalone/assets/logos/goose-light.svg +1 -0
  177. package/.next/standalone/assets/logos/openclaw.svg +22 -0
  178. package/.next/standalone/components/navbar.tsx +5 -11
  179. package/.next/standalone/components/reach-developers.tsx +1 -1
  180. package/.next/standalone/lib/antigravity-projects.ts +224 -0
  181. package/.next/standalone/lib/antigravity-sessions.ts +283 -0
  182. package/.next/standalone/lib/cli-registry.ts +26 -6
  183. package/.next/standalone/lib/devin-projects.ts +141 -0
  184. package/.next/standalone/lib/devin-sessions.ts +343 -0
  185. package/.next/standalone/lib/download-session.ts +56 -5
  186. package/.next/standalone/lib/factory-projects.ts +109 -0
  187. package/.next/standalone/lib/factory-sessions.ts +312 -0
  188. package/.next/standalone/lib/goose-projects.ts +148 -0
  189. package/.next/standalone/lib/goose-sessions.ts +296 -0
  190. package/.next/standalone/lib/openclaw-projects.ts +182 -0
  191. package/.next/standalone/lib/openclaw-sessions.ts +284 -0
  192. package/.next/standalone/lib/projects.ts +33 -9
  193. package/.next/standalone/lib/telemetry-sanitize.ts +43 -0
  194. package/.next/standalone/openclaw-plugin/index.js +223 -0
  195. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +6 -0
  196. package/.next/standalone/openclaw-plugin/package.json +12 -0
  197. package/.next/standalone/package.json +2 -1
  198. package/.next/standalone/server.js +1 -1
  199. package/README.md +105 -57
  200. package/bin/failproofai.mjs +87 -29
  201. package/dist/cli.mjs +12803 -9809
  202. package/lib/antigravity-projects.ts +224 -0
  203. package/lib/antigravity-sessions.ts +283 -0
  204. package/lib/cli-registry.ts +26 -6
  205. package/lib/devin-projects.ts +141 -0
  206. package/lib/devin-sessions.ts +343 -0
  207. package/lib/download-session.ts +56 -5
  208. package/lib/factory-projects.ts +109 -0
  209. package/lib/factory-sessions.ts +312 -0
  210. package/lib/goose-projects.ts +148 -0
  211. package/lib/goose-sessions.ts +296 -0
  212. package/lib/openclaw-projects.ts +182 -0
  213. package/lib/openclaw-sessions.ts +284 -0
  214. package/lib/projects.ts +33 -9
  215. package/lib/telemetry-sanitize.ts +43 -0
  216. package/openclaw-plugin/index.js +223 -0
  217. package/openclaw-plugin/openclaw.plugin.json +6 -0
  218. package/openclaw-plugin/package.json +12 -0
  219. package/package.json +2 -1
  220. package/scripts/launch.ts +4 -8
  221. package/scripts/postinstall.mjs +3 -4
  222. package/scripts/sync-agent-cli-harnesses-prompt.md +7 -10
  223. package/scripts/translate-docs/cli.ts +73 -52
  224. package/scripts/translate-docs/config.ts +57 -13
  225. package/scripts/translate-docs/mdx-translator.ts +51 -31
  226. package/scripts/translate-docs/mintlify-nav.ts +91 -10
  227. package/scripts/translate-docs/types.ts +3 -0
  228. package/src/audit/cli-adapters/antigravity.ts +87 -0
  229. package/src/audit/cli-adapters/devin.ts +53 -0
  230. package/src/audit/cli-adapters/factory.ts +75 -0
  231. package/src/audit/cli-adapters/goose.ts +53 -0
  232. package/src/audit/cli-adapters/index.ts +30 -6
  233. package/src/audit/cli-adapters/openclaw.ts +56 -0
  234. package/src/audit/cli.ts +44 -1
  235. package/src/audit/report.ts +1 -1
  236. package/src/audit/types.ts +1 -1
  237. package/src/hooks/builtin-policies.ts +5 -5
  238. package/src/hooks/configure-wizard.ts +356 -0
  239. package/src/hooks/handler.ts +283 -218
  240. package/src/hooks/hook-telemetry.ts +43 -3
  241. package/src/hooks/install-prompt.ts +92 -173
  242. package/src/hooks/integrations.ts +739 -138
  243. package/src/hooks/manager.ts +46 -2
  244. package/src/hooks/policy-evaluator.ts +301 -121
  245. package/src/hooks/policy-presets.ts +71 -0
  246. package/src/hooks/resolve-cwd.ts +1 -1
  247. package/src/hooks/resolve-permission-mode.ts +1 -5
  248. package/src/hooks/resolve-transcript-path.ts +25 -7
  249. package/src/hooks/tool-name-canonicalize.ts +36 -3
  250. package/src/hooks/tui.ts +578 -0
  251. package/src/hooks/types.ts +447 -109
  252. package/.next/standalone/.gemini/settings.json +0 -147
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cag8qd._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cefjd9._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ekj92q._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12v-lma._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__136y39g._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13h8pzr._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hgv_75._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04bqvos._.js +0 -3
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dqgs96._.js +0 -3
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0q5rfxg._.js +0 -3
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qukut7._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__108slqn._.js +0 -3
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ql-mch._.js +0 -3
  266. package/.next/standalone/.next/server/chunks/ssr/_11v0g3z._.js +0 -3
  267. package/.next/standalone/.next/server/chunks/ssr/lib_gemini-projects_ts_1c7bgx-._.js +0 -3
  268. package/.next/standalone/.next/static/chunks/0fa7j157kg9_d.js +0 -1
  269. package/.next/standalone/.next/static/chunks/0nrah-qv6smeg.js +0 -6
  270. package/.next/standalone/.next/static/chunks/1c8i78p7t1i-5.js +0 -1
  271. package/.next/standalone/.next/static/chunks/25qr0e6ez0ei0.js +0 -1
  272. package/.next/standalone/.next/static/chunks/25s52un3k_wht.css +0 -2
  273. package/.next/standalone/assets/logos/gemini-dark.svg +0 -13
  274. package/.next/standalone/assets/logos/gemini-light.svg +0 -13
  275. package/.next/standalone/lib/gemini-projects.ts +0 -243
  276. package/.next/standalone/lib/gemini-sessions.ts +0 -365
  277. package/lib/gemini-projects.ts +0 -243
  278. package/lib/gemini-sessions.ts +0 -365
  279. package/src/audit/cli-adapters/gemini.ts +0 -51
  280. package/src/hooks/first-run-nudge.ts +0 -146
  281. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_buildManifest.js +0 -0
  282. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_clientMiddlewareManifest.js +0 -0
  283. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → MNJWFb2jLRrxRKGFNK6Hy}/_ssgManifest.js +0 -0
@@ -86,6 +86,14 @@ export function hooksInstalledInSettings(scope: HookScope, cwd?: string): boolea
86
86
  return claudeCode.hooksInstalledInSettings(scope, cwd);
87
87
  }
88
88
 
89
+ export interface InstallHooksOptions {
90
+ /** Replace the enabled set at this scope instead of unioning (default: additive). */
91
+ replace?: boolean;
92
+ /** Suppress this module's installation logging (for callers that render their
93
+ * own UI, like the configure wizard). Errors still surface via console.error. */
94
+ quiet?: boolean;
95
+ }
96
+
89
97
  /**
90
98
  * Install hooks into Claude Code settings.
91
99
  *
@@ -104,6 +112,38 @@ export async function installHooks(
104
112
  customPoliciesPath?: string,
105
113
  removeCustomHooks = false,
106
114
  cli?: IntegrationType[],
115
+ options: InstallHooksOptions = {},
116
+ ): Promise<void> {
117
+ const { replace = false, quiet = false } = options;
118
+ if (!quiet) {
119
+ return installHooksImpl(
120
+ policyNames, scope, cwd, includeBeta, source, customPoliciesPath, removeCustomHooks, cli, replace,
121
+ );
122
+ }
123
+ // Quiet mode: this module logs exclusively via console.log, so muting it for
124
+ // the duration of the call silences installation output at its owner rather
125
+ // than at every call site. console.error (real failures) still flows.
126
+ const origLog = console.log;
127
+ console.log = () => {};
128
+ try {
129
+ return await installHooksImpl(
130
+ policyNames, scope, cwd, includeBeta, source, customPoliciesPath, removeCustomHooks, cli, replace,
131
+ );
132
+ } finally {
133
+ console.log = origLog;
134
+ }
135
+ }
136
+
137
+ async function installHooksImpl(
138
+ policyNames?: string[],
139
+ scope: HookScope = "user",
140
+ cwd?: string,
141
+ includeBeta = false,
142
+ source?: string,
143
+ customPoliciesPath?: string,
144
+ removeCustomHooks = false,
145
+ cli?: IntegrationType[],
146
+ replace = false,
107
147
  ): Promise<void> {
108
148
  // Validate user input first before any system checks
109
149
  if (policyNames !== undefined && policyNames.length > 0) {
@@ -159,8 +199,12 @@ export async function installHooks(
159
199
  } else {
160
200
  incoming = policyNames;
161
201
  }
162
- // Additive: union with whatever was already enabled, deduplicated.
163
- selectedPolicies = [...new Set([...previousConfig.enabledPolicies, ...incoming])];
202
+ // Default is additive (union with whatever was already enabled). The
203
+ // configure wizard passes replace=true so the chosen set becomes the full
204
+ // enabled set at this scope (unticking a policy actually removes it).
205
+ selectedPolicies = replace
206
+ ? [...new Set(incoming)]
207
+ : [...new Set([...previousConfig.enabledPolicies, ...incoming])];
164
208
  } else {
165
209
  // Interactive — pre-load current config if it exists
166
210
  const preSelected = previousConfig.enabledPolicies.length > 0 ? previousConfig.enabledPolicies : undefined;
@@ -164,8 +164,7 @@ export async function evaluatePolicies(
164
164
  // — that shape is only honored on tool events. The only force-retry
165
165
  // channel for Stop/SubagentStop is `{followup_message}` on stdout
166
166
  // (exit 0); Cursor auto-submits the text as the next user message
167
- // (capped at `loop_limit`, default 5). Mirrors the Copilot Stop
168
- // branch at line ~279 and the Gemini AfterAgent branch at line ~188.
167
+ // (capped at `loop_limit`, default 5). Mirrors the Copilot Stop branch.
169
168
  // Without this branch, the 5 `require-*-before-stop` builtins were
170
169
  // observation-only on Cursor — the deny was logged but the agent
171
170
  // stopped cleanly. Ref: https://cursor.com/docs/hooks
@@ -202,7 +201,7 @@ export async function evaluatePolicies(
202
201
  // same flat shape — except Stop, where we emit the MANDATORY ACTION
203
202
  // wording so the shim can re-inject it as a system-prompt suffix on
204
203
  // the next before_agent_start (Pi cannot veto agent_end directly).
205
- // Mirrors the Cursor/Gemini/Copilot/OpenCode Stop branches above.
204
+ // Mirrors the Cursor/Copilot/OpenCode Stop branches above.
206
205
  if (session?.cli === "pi") {
207
206
  if (eventType === "Stop") {
208
207
  const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`;
@@ -229,33 +228,6 @@ export async function evaluatePolicies(
229
228
  };
230
229
  }
231
230
 
232
- // Gemini CLI: flat `{decision: "deny", reason}` for non-Stop events
233
- // (preferred per Gemini's "Golden Rule" — exit 0 with structured JSON).
234
- // For Stop (AfterAgent), use `{decision: "block", reason}` to force-retry,
235
- // mirroring Claude's exit-2-from-Stop "do this before stopping" semantics.
236
- // Ref: https://geminicli.com/docs/hooks/
237
- if (session?.cli === "gemini") {
238
- if (eventType === "Stop") {
239
- const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`;
240
- return {
241
- exitCode: 0,
242
- stdout: JSON.stringify({ decision: "block", reason: reasonText }),
243
- stderr: "",
244
- policyName: policy.name,
245
- reason,
246
- decision: "deny",
247
- };
248
- }
249
- return {
250
- exitCode: 0,
251
- stdout: JSON.stringify({ decision: "deny", reason: blockedMessage }),
252
- stderr: "",
253
- policyName: policy.name,
254
- reason,
255
- decision: "deny",
256
- };
257
- }
258
-
259
231
  // Hermes: the block contract is `{"decision":"block","reason"}` on stdout;
260
232
  // Hermes IGNORES exit codes, so the JSON is the only channel. This one
261
233
  // return covers every Hermes event we install (PreToolUse / PostToolUse /
@@ -275,6 +247,36 @@ export async function evaluatePolicies(
275
247
  };
276
248
  }
277
249
 
250
+ // OpenClaw: the shipped openclaw-plugin parses a flat {permission, reason}
251
+ // verdict and maps it per plugin-hook — before_tool_call → {block:true,
252
+ // blockReason}; before_agent_run → {outcome:"block", reason};
253
+ // before_agent_finalize (Stop) → {action:"revise", reason}. For Stop we
254
+ // emit the MANDATORY ACTION wording so the revise loop carries the
255
+ // directive. Observation hooks (after_tool_call / session_* /
256
+ // subagent_ended / before_compaction) ignore stdout, so the flat deny is
257
+ // logged but cannot veto — a documented limitation. Mirrors the Pi branch.
258
+ if (session?.cli === "openclaw") {
259
+ if (eventType === "Stop") {
260
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`;
261
+ return {
262
+ exitCode: 0,
263
+ stdout: JSON.stringify({ permission: "deny", reason: reasonText }),
264
+ stderr: "",
265
+ policyName: policy.name,
266
+ reason,
267
+ decision: "deny",
268
+ };
269
+ }
270
+ return {
271
+ exitCode: 0,
272
+ stdout: JSON.stringify({ permission: "deny", reason: blockedMessage }),
273
+ stderr: "",
274
+ policyName: policy.name,
275
+ reason,
276
+ decision: "deny",
277
+ };
278
+ }
279
+
278
280
  // OpenCode: `session.idle` is a notification-only bus event — by the
279
281
  // time the plugin handler fires, OpenCode has already gone idle and
280
282
  // throwing from the handler does not force-retry. The only working
@@ -283,8 +285,8 @@ export async function evaluatePolicies(
283
285
  // shim already routes `hookSpecificOutput.additionalContext` through
284
286
  // that path (see buildOpenCodePluginShim's applyDecision), so we emit
285
287
  // the deny reason as additionalContext instead of exit-2. Mirrors the
286
- // Cursor `followup_message` (line ~157) and Copilot `{decision:"block"}`
287
- // (line ~299) Stop branches. SubagentStop is widened in for forward
288
+ // Cursor `followup_message` and Copilot `{decision:"block"}` Stop
289
+ // branches. SubagentStop is widened in for forward
288
290
  // compat — OpenCode doesn't yet expose subagent boundaries to plugins.
289
291
  if (session?.cli === "opencode") {
290
292
  if (eventType === "Stop" || eventType === "SubagentStop") {
@@ -303,6 +305,105 @@ export async function evaluatePolicies(
303
305
  // for tool events.
304
306
  }
305
307
 
308
+ // Factory droid: droid drives tool blocking off EXIT CODE 2 (it ignores a
309
+ // JSON `{decision:…}` on tool events — verified live against droid
310
+ // v0.171.0: `Hook returned exit code 2, throwing ToolExecutionControlError`).
311
+ // The one exception is `Stop`, where droid does NOT honor exit-2
312
+ // force-retry; there it reads `{decision:"block", reason}` on stdout at
313
+ // exit 0 ("if decision is block, Droid does not stop"). So: Stop → JSON
314
+ // block; every other event (PreToolUse / PostToolUse / UserPromptSubmit /
315
+ // SubagentStop / …) → exit 2 + the blocked message on stderr.
316
+ if (session?.cli === "factory") {
317
+ if (eventType === "Stop") {
318
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`;
319
+ return {
320
+ exitCode: 0,
321
+ stdout: JSON.stringify({ decision: "block", reason: reasonText }),
322
+ stderr: "",
323
+ policyName: policy.name,
324
+ reason,
325
+ decision: "deny",
326
+ };
327
+ }
328
+ return {
329
+ exitCode: 2,
330
+ stdout: "",
331
+ stderr: blockedMessage + "\n",
332
+ policyName: policy.name,
333
+ reason,
334
+ decision: "deny",
335
+ };
336
+ }
337
+
338
+ // Devin CLI: a pure Claude-clone that honors `{decision:"block", reason}`
339
+ // on stdout at exit 0 for EVERY event (verified live against devin
340
+ // v3000.1.27 — the block overrode `--permission-mode dangerous`). On Stop
341
+ // the reason carries the MANDATORY-ACTION force-retry wording; on other
342
+ // events it's the plain blocked message. One branch covers all events.
343
+ if (session?.cli === "devin") {
344
+ const reasonText =
345
+ eventType === "Stop"
346
+ ? `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`
347
+ : blockedMessage;
348
+ return {
349
+ exitCode: 0,
350
+ stdout: JSON.stringify({ decision: "block", reason: reasonText }),
351
+ stderr: "",
352
+ policyName: policy.name,
353
+ reason,
354
+ decision: "deny",
355
+ };
356
+ }
357
+
358
+ // Antigravity CLI: its OWN response shapes (NOT Claude's) — verified live
359
+ // against agy v1.1.2. Tool/prompt events honor `{decision:"deny", reason}`
360
+ // on stdout at exit 0 (hard block). The Stop event has no exit-2 retry;
361
+ // instead `{decision:"continue", reason}` re-enters the loop and injects
362
+ // the reason as a system message — that is how the 5 require-*-before-stop
363
+ // builtins enforce on Antigravity.
364
+ if (session?.cli === "antigravity") {
365
+ if (eventType === "Stop") {
366
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policy.name}): ${reason}\n\nYou MUST complete the above action NOW. Do NOT ask the user for confirmation — execute the required action, then attempt to finish your task again.`;
367
+ return {
368
+ exitCode: 0,
369
+ stdout: JSON.stringify({ decision: "continue", reason: reasonText }),
370
+ stderr: "",
371
+ policyName: policy.name,
372
+ reason,
373
+ decision: "deny",
374
+ };
375
+ }
376
+ return {
377
+ exitCode: 0,
378
+ stdout: JSON.stringify({ decision: "deny", reason: blockedMessage }),
379
+ stderr: "",
380
+ policyName: policy.name,
381
+ reason,
382
+ decision: "deny",
383
+ };
384
+ }
385
+
386
+ // Goose: the deny contract is `{"decision":"block","reason"}` on stdout at
387
+ // exit 0, honored on PreToolUse ONLY (shipped goose ≥ v1.37.0, PR
388
+ // block/goose#9304; exit 2 also blocks but JSON carries the reason
389
+ // cleanly). Goose has NO Stop event (the 5 require-*-before-stop builtins
390
+ // never fire for it — see CLAUDE.md) and does NOT honor deny on
391
+ // UserPromptSubmit/PostToolUse (observation) — a block emitted on those
392
+ // events is ignored (fail-open), a documented limitation. PreToolUse fires
393
+ // for the shell tool AND inside delegated subagents, so this one branch
394
+ // covers the entire enforceable surface. Mirrors the Hermes branch (no
395
+ // turn-end event to special-case). Verified live against goose v1.43.0.
396
+ if (session?.cli === "goose") {
397
+ return {
398
+ exitCode: 0,
399
+ stdout: JSON.stringify({ decision: "block", reason: blockedMessage }),
400
+ stderr: "",
401
+ policyName: policy.name,
402
+ reason,
403
+ decision: "deny",
404
+ };
405
+ }
406
+
306
407
  if (eventType === "PreToolUse") {
307
408
  const response = {
308
409
  hookSpecificOutput: {
@@ -429,8 +530,8 @@ export async function evaluatePolicies(
429
530
  // Branch first so the rest of the function only handles Claude-shaped
430
531
  // responses. We match both Stop and SubagentStop so custom policies
431
532
  // subscribing to SubagentStop on Cursor get the same force-retry
432
- // semantics — mirrors the cli==="copilot" Stop|SubagentStop widening
433
- // at line ~472. Ref: https://cursor.com/docs/hooks (Stdout Response Format).
533
+ // semantics — mirrors the cli==="copilot" Stop|SubagentStop widening.
534
+ // Ref: https://cursor.com/docs/hooks (Stdout Response Format).
434
535
  if (session?.cli === "cursor") {
435
536
  if (eventType === "Stop" || eventType === "SubagentStop") {
436
537
  const response = {
@@ -499,15 +600,35 @@ export async function evaluatePolicies(
499
600
  };
500
601
  }
501
602
 
502
- // Gemini CLI:
503
- // Stop (AfterAgent) {decision: "block", reason: "MANDATORY ACTION..."}
504
- // mirrors Claude's exit-2-from-Stop "force retry" semantics.
505
- // UserPromptSubmit/PostToolUse/SessionStart/PreToolUse context
506
- // injection via {hookSpecificOutput: {hookEventName, additionalContext}}
507
- // where hookEventName is the GEMINI event name (BeforeAgent/AfterTool/
508
- // SessionStart/BeforeTool), not the canonical PascalCase form.
509
- // • Other events → stderr only (no stdout JSON shape supported).
510
- if (session?.cli === "gemini") {
603
+ // Hermes: no additional-context channel on any event (the only actionable
604
+ // response is `{"decision":"block"}`). So instruct degrades to allow +
605
+ // log — we emit a non-blocking `{decision:"allow", reason}` (Hermes lets
606
+ // the tool run) and surface the note on stderr for the operator's logs.
607
+ // Documented limitation; there is no Stop event to force-retry into.
608
+ if (session?.cli === "hermes") {
609
+ const stderrMsg = instructEntries
610
+ .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
611
+ .join("\n");
612
+ return {
613
+ exitCode: 0,
614
+ stdout: JSON.stringify({
615
+ decision: "allow",
616
+ reason: `Instruction from failproofai: ${combined}`,
617
+ }),
618
+ stderr: stderrMsg + "\n",
619
+ policyName: policyNames[0],
620
+ policyNames,
621
+ reason: combined,
622
+ decision: "instruct",
623
+ };
624
+ }
625
+
626
+ // OpenClaw: Stop (before_agent_finalize) can force a revise, so we emit the
627
+ // MANDATORY ACTION wording as a flat deny — the shim maps it to
628
+ // {action:"revise", reason}. Every other event lacks an additional-context
629
+ // channel (before_tool_call's return is {params,block,blockReason} only), so
630
+ // instruct degrades to allow + stderr note, like Hermes.
631
+ if (session?.cli === "openclaw") {
511
632
  if (eventType === "Stop") {
512
633
  const policyAttribution = policyNames.length === 1
513
634
  ? `policy: ${policyNames[0]}`
@@ -515,7 +636,7 @@ export async function evaluatePolicies(
515
636
  const reasonText = `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`;
516
637
  return {
517
638
  exitCode: 0,
518
- stdout: JSON.stringify({ decision: "block", reason: reasonText }),
639
+ stdout: JSON.stringify({ permission: "deny", reason: reasonText }),
519
640
  stderr: "",
520
641
  policyName: policyNames[0],
521
642
  policyNames,
@@ -523,31 +644,58 @@ export async function evaluatePolicies(
523
644
  decision: "instruct",
524
645
  };
525
646
  }
526
- // Map back from canonical → Gemini event name. Prefer the raw event name
527
- // off the session (handler.ts populates it from parsed.hook_event_name)
528
- // so we don't have to maintain a reverse lookup table.
529
- const supportsContext =
530
- eventType === "UserPromptSubmit" ||
531
- eventType === "PreToolUse" ||
532
- eventType === "PostToolUse" ||
533
- eventType === "SessionStart";
534
- if (supportsContext) {
535
- // Round-trip the agent-emitted event name so Gemini sees `BeforeTool`,
536
- // `BeforeAgent`, etc. (NOT the canonical Claude form). Prefer the
537
- // stdin payload's `hook_event_name` when present; fall back to the raw
538
- // CLI `--hook` arg captured by handler.ts; only use the canonical
539
- // event as a last resort (would never round-trip correctly, but better
540
- // than emitting nothing).
541
- const hookEventName = session?.hookEventName ?? session?.rawHookEventName ?? eventType;
542
- const response = {
543
- hookSpecificOutput: {
544
- hookEventName,
545
- additionalContext: `Instruction from failproofai: ${combined}`,
546
- },
647
+ const stderrMsg = instructEntries
648
+ .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
649
+ .join("\n");
650
+ return {
651
+ exitCode: 0,
652
+ stdout: JSON.stringify({
653
+ permission: "allow",
654
+ reason: `Instruction from failproofai: ${combined}`,
655
+ }),
656
+ stderr: stderrMsg + "\n",
657
+ policyName: policyNames[0],
658
+ policyNames,
659
+ reason: combined,
660
+ decision: "instruct",
661
+ };
662
+ }
663
+
664
+ // OpenCode: same rationale as the deny branch above — emit
665
+ // additionalContext so the shim submits a follow-up via
666
+ // client.session.prompt instead of throwing into a dead handler.
667
+ if (session?.cli === "opencode") {
668
+ if (eventType === "Stop" || eventType === "SubagentStop") {
669
+ const policyAttribution = policyNames.length === 1
670
+ ? `policy: ${policyNames[0]}`
671
+ : `policies: ${policyNames.join(", ")}`;
672
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`;
673
+ return {
674
+ exitCode: 0,
675
+ stdout: JSON.stringify({ hookSpecificOutput: { additionalContext: reasonText } }),
676
+ stderr: "",
677
+ policyName: policyNames[0],
678
+ policyNames,
679
+ reason: combined,
680
+ decision: "instruct",
547
681
  };
682
+ }
683
+ }
684
+
685
+ // Factory droid: on Stop, emit the MANDATORY ACTION wording as a
686
+ // `{decision:"block", reason}` on stdout (exit 0) — droid's only turn-end
687
+ // force-retry channel. Every other event lacks an additional-context
688
+ // channel (droid honors JSON only for the Stop block), so instruct degrades
689
+ // to allow + stderr note, like Hermes.
690
+ if (session?.cli === "factory") {
691
+ if (eventType === "Stop") {
692
+ const policyAttribution = policyNames.length === 1
693
+ ? `policy: ${policyNames[0]}`
694
+ : `policies: ${policyNames.join(", ")}`;
695
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`;
548
696
  return {
549
697
  exitCode: 0,
550
- stdout: JSON.stringify(response),
698
+ stdout: JSON.stringify({ decision: "block", reason: reasonText }),
551
699
  stderr: "",
552
700
  policyName: policyNames[0],
553
701
  policyNames,
@@ -555,8 +703,6 @@ export async function evaluatePolicies(
555
703
  decision: "instruct",
556
704
  };
557
705
  }
558
- // No context-injection channel for SessionEnd/PreCompress/Notification/
559
- // BeforeModel/AfterModel/BeforeToolSelection — surface via stderr only.
560
706
  const stderrMsg = instructEntries
561
707
  .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
562
708
  .join("\n");
@@ -571,22 +717,20 @@ export async function evaluatePolicies(
571
717
  };
572
718
  }
573
719
 
574
- // Hermes: no additional-context channel on any event (the only actionable
575
- // response is `{"decision":"block"}`). So instruct degrades to allow +
576
- // log we emit a non-blocking `{decision:"allow", reason}` (Hermes lets
577
- // the tool run) and surface the note on stderr for the operator's logs.
578
- // Documented limitation; there is no Stop event to force-retry into.
579
- if (session?.cli === "hermes") {
580
- const stderrMsg = instructEntries
581
- .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
582
- .join("\n");
720
+ // Devin CLI: a pure Claude-clone. On Stop, emit the MANDATORY ACTION
721
+ // wording as `{decision:"block", reason}` on stdout (exit 0) Devin's
722
+ // turn-end force-retry channel (its exit-2 is not a force-retry). Every
723
+ // other event falls through to the generic Claude additionalContext path
724
+ // below (Devin honors `hookSpecificOutput.additionalContext`).
725
+ if (session?.cli === "devin" && eventType === "Stop") {
726
+ const policyAttribution = policyNames.length === 1
727
+ ? `policy: ${policyNames[0]}`
728
+ : `policies: ${policyNames.join(", ")}`;
729
+ const reasonText = `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`;
583
730
  return {
584
731
  exitCode: 0,
585
- stdout: JSON.stringify({
586
- decision: "allow",
587
- reason: `Instruction from failproofai: ${combined}`,
588
- }),
589
- stderr: stderrMsg + "\n",
732
+ stdout: JSON.stringify({ decision: "block", reason: reasonText }),
733
+ stderr: "",
590
734
  policyName: policyNames[0],
591
735
  policyNames,
592
736
  reason: combined,
@@ -594,18 +738,36 @@ export async function evaluatePolicies(
594
738
  };
595
739
  }
596
740
 
597
- // OpenCode: same rationale as the deny branch above — emit
598
- // additionalContext so the shim submits a follow-up via
599
- // client.session.prompt instead of throwing into a dead handler.
600
- if (session?.cli === "opencode") {
601
- if (eventType === "Stop" || eventType === "SubagentStop") {
741
+ // Antigravity CLI: its OWN instruct shapes (verified live agy v1.1.2).
742
+ // UserPromptSubmit (canonical for PreInvocation) `{injectSteps:[{
743
+ // ephemeralMessage}]}` injects the instruction as a transient system
744
+ // message before the model runs.
745
+ // • Stop `{decision:"continue", reason}` re-enters the loop with the
746
+ // MANDATORY-ACTION directive (Antigravity's only turn-end channel).
747
+ // • Every other event lacks an additional-context channel → degrade to
748
+ // allow + stderr note, like Hermes.
749
+ if (session?.cli === "antigravity") {
750
+ if (eventType === "UserPromptSubmit") {
751
+ return {
752
+ exitCode: 0,
753
+ stdout: JSON.stringify({
754
+ injectSteps: [{ ephemeralMessage: `Instruction from failproofai: ${combined}` }],
755
+ }),
756
+ stderr: "",
757
+ policyName: policyNames[0],
758
+ policyNames,
759
+ reason: combined,
760
+ decision: "instruct",
761
+ };
762
+ }
763
+ if (eventType === "Stop") {
602
764
  const policyAttribution = policyNames.length === 1
603
765
  ? `policy: ${policyNames[0]}`
604
766
  : `policies: ${policyNames.join(", ")}`;
605
767
  const reasonText = `MANDATORY ACTION REQUIRED from failproofai (${policyAttribution}): ${combined}\n\nYou MUST complete the above action(s) NOW. Do NOT ask the user for confirmation — execute the required action(s), then attempt to finish your task again.`;
606
768
  return {
607
769
  exitCode: 0,
608
- stdout: JSON.stringify({ hookSpecificOutput: { additionalContext: reasonText } }),
770
+ stdout: JSON.stringify({ decision: "continue", reason: reasonText }),
609
771
  stderr: "",
610
772
  policyName: policyNames[0],
611
773
  policyNames,
@@ -613,6 +775,36 @@ export async function evaluatePolicies(
613
775
  decision: "instruct",
614
776
  };
615
777
  }
778
+ const stderrMsg = instructEntries
779
+ .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
780
+ .join("\n");
781
+ return {
782
+ exitCode: 0,
783
+ stdout: "",
784
+ stderr: stderrMsg + "\n",
785
+ policyName: policyNames[0],
786
+ policyNames,
787
+ reason: combined,
788
+ decision: "instruct",
789
+ };
790
+ }
791
+
792
+ // Goose: a non-block PreToolUse decision injects nothing (verified live — no
793
+ // additional-context channel), and Goose has no Stop event, so instruct
794
+ // degrades to allow + stderr note, like Hermes / Factory non-Stop events.
795
+ if (session?.cli === "goose") {
796
+ const stderrMsg = instructEntries
797
+ .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
798
+ .join("\n");
799
+ return {
800
+ exitCode: 0,
801
+ stdout: "",
802
+ stderr: stderrMsg + "\n",
803
+ policyName: policyNames[0],
804
+ policyNames,
805
+ reason: combined,
806
+ decision: "instruct",
807
+ };
616
808
  }
617
809
 
618
810
  if (eventType === "Stop" || eventType === "SubagentStop") {
@@ -629,7 +821,7 @@ export async function evaluatePolicies(
629
821
  // `[WARNING] Hook warning: ...` but does NOT trigger retry. The
630
822
  // documented retry shape is `{decision: "block", reason}` JSON on
631
823
  // stdout (exit 0). Mirrors the cli==="copilot" branch in the deny
632
- // path at line ~279 so custom instruct policies enforce on Copilot.
824
+ // path so custom instruct policies enforce on Copilot.
633
825
  if (session?.cli === "copilot") {
634
826
  return {
635
827
  exitCode: 0,
@@ -715,39 +907,20 @@ export async function evaluatePolicies(
715
907
  };
716
908
  }
717
909
 
718
- // Gemini: mirror the instruct context-injection shape for events that
719
- // support it; stderr-only for everything else.
720
- if (session?.cli === "gemini") {
721
- const supportsContext =
722
- eventType === "UserPromptSubmit" ||
723
- eventType === "PreToolUse" ||
724
- eventType === "PostToolUse" ||
725
- eventType === "SessionStart";
910
+ // OpenClaw: same flat shape as Pi {permission:"allow", reason}. The shim
911
+ // returns undefined (no block) for an allow verdict regardless, so the note
912
+ // surfaces via stderr; keeping the flat stdout shape keeps the shim's parse
913
+ // path uniform across every verdict.
914
+ if (session?.cli === "openclaw") {
726
915
  const stderrMsg = allowEntries
727
916
  .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
728
917
  .join("\n");
729
- if (supportsContext) {
730
- // Same fallback chain as the instruct path above — see comment there.
731
- const hookEventName = session?.hookEventName ?? session?.rawHookEventName ?? eventType;
732
- const response = {
733
- hookSpecificOutput: {
734
- hookEventName,
735
- additionalContext: `Note from failproofai: ${combined}`,
736
- },
737
- };
738
- return {
739
- exitCode: 0,
740
- stdout: JSON.stringify(response),
741
- stderr: stderrMsg + "\n",
742
- policyName: policyNames[0],
743
- policyNames,
744
- reason: combined,
745
- decision: "allow",
746
- };
747
- }
748
918
  return {
749
919
  exitCode: 0,
750
- stdout: "",
920
+ stdout: JSON.stringify({
921
+ permission: "allow",
922
+ reason: `Note from failproofai: ${combined}`,
923
+ }),
751
924
  stderr: stderrMsg + "\n",
752
925
  policyName: policyNames[0],
753
926
  policyNames,
@@ -761,13 +934,20 @@ export async function evaluatePolicies(
761
934
  eventType === "PostToolUse" ||
762
935
  eventType === "UserPromptSubmit" ||
763
936
  eventType === "PermissionRequest";
764
- const response = supportsHookSpecificOutput
765
- ? { hookSpecificOutput: { hookEventName: eventType, additionalContext: `Note from failproofai: ${combined}` } }
766
- : { reason: combined };
767
937
  const stderrMsg = allowEntries
768
938
  .map((e) => `[failproofai] ${e.policyName}: ${e.reason}`)
769
939
  .join("\n");
770
- return { exitCode: 0, stdout: JSON.stringify(response), stderr: stderrMsg + "\n", policyName: policyNames[0], policyNames, reason: combined, decision: "allow" };
940
+ // Only events with a real additional-context channel carry the allow-note
941
+ // to the agent. Everything else (Stop, SubagentStop, Session*, PreCompact, …)
942
+ // has NO channel, so we keep informational allow-notes OUT of stdout — a
943
+ // bare `{reason}` there is rendered as noise (e.g. droid printing a Stop's
944
+ // "…skipping commit check…skipping PR check…" wall on a perfectly fine turn).
945
+ // The note is still logged to stderr + the activity store for diagnostics.
946
+ if (supportsHookSpecificOutput) {
947
+ const response = { hookSpecificOutput: { hookEventName: eventType, additionalContext: `Note from failproofai: ${combined}` } };
948
+ return { exitCode: 0, stdout: JSON.stringify(response), stderr: stderrMsg + "\n", policyName: policyNames[0], policyNames, reason: combined, decision: "allow" };
949
+ }
950
+ return { exitCode: 0, stdout: "", stderr: stderrMsg + "\n", policyName: policyNames[0], policyNames, reason: combined, decision: "allow" };
771
951
  }
772
952
  return { exitCode: 0, stdout: "", stderr: "", policyName: null, reason: null, decision: "allow" };
773
953
  }