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
@@ -0,0 +1,284 @@
1
+ /**
2
+ * OpenClaw (openclaw gateway) session transcript loader + parser.
3
+ *
4
+ * AUDIT-ONLY (Pillar 2). OpenClaw writes one JSONL transcript per session at
5
+ * `~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl` (sessionId is a
6
+ * UUID), alongside a much larger `<sessionId>.trajectory.jsonl` OTel trace we
7
+ * IGNORE, and a `sessions.json` index keyed by sessionKey. Verified live
8
+ * against openclaw v2026.7.1.
9
+ *
10
+ * The transcript is type-discriminated JSONL:
11
+ * {type:"session", cwd, …} — header (carries cwd)
12
+ * {type:"model_change" | "thinking_level_change" | "custom", …} — metadata
13
+ * {type:"message", timestamp, message:{role, content, …}} — the turns, where
14
+ * role ∈ "user" (content string) | "assistant" (content = list of
15
+ * {type:"text",text} / {type:"toolCall", id, name, arguments}) |
16
+ * "toolResult" ({toolCallId, toolName, content, details, isError}).
17
+ * `openclawLinesToLogEntries` pairs each assistant `toolCall` with its later
18
+ * `toolResult` by `toolCallId` (mirrors lib/hermes-sessions.ts) and is PURE, so
19
+ * it is unit-testable with plain line objects.
20
+ *
21
+ * Home override: set `OPENCLAW_HOME` (used by tests / to point at a copied
22
+ * gateway config dir).
23
+ */
24
+ import { readFile } from "node:fs/promises";
25
+ import { readdirSync, statSync } from "node:fs";
26
+ import { homedir } from "node:os";
27
+ import { join } from "node:path";
28
+ import { runtimeCache } from "./runtime-cache";
29
+ import {
30
+ baseEntry,
31
+ formatTimestamp,
32
+ parseRawLines,
33
+ type LogEntry,
34
+ type UserEntry,
35
+ type AssistantEntry,
36
+ type GenericEntry,
37
+ type ContentBlock,
38
+ type ToolUseBlock,
39
+ type LogSource,
40
+ } from "./log-entries";
41
+ import { formatDuration } from "./format-duration";
42
+
43
+ /** OpenClaw sessions are stored under UUID filenames. */
44
+ export const OPENCLAW_SESSION_ID_RE = /^[0-9a-fA-F-]{36}$/;
45
+
46
+ /** Absolute path to OpenClaw's config home (override with OPENCLAW_HOME). */
47
+ export function openclawHome(): string {
48
+ return process.env.OPENCLAW_HOME || join(homedir(), ".openclaw");
49
+ }
50
+
51
+ // ── Parsing helpers ──
52
+
53
+ function isPlainObject(v: unknown): v is Record<string, unknown> {
54
+ return !!v && typeof v === "object" && !Array.isArray(v);
55
+ }
56
+
57
+ /** Text from a `content` field that may be a string or an array of
58
+ * `{ type:"text", text }` blocks. */
59
+ function extractText(content: unknown): string {
60
+ if (typeof content === "string") return content;
61
+ if (Array.isArray(content)) {
62
+ return content
63
+ .map((c) => (isPlainObject(c) && typeof c.text === "string" ? (c.text as string) : ""))
64
+ .filter(Boolean)
65
+ .join("\n");
66
+ }
67
+ return "";
68
+ }
69
+
70
+ function toDate(value: unknown, fallbackMs: number): Date {
71
+ if (typeof value === "number" && Number.isFinite(value)) {
72
+ if (value > 1e12) return new Date(value);
73
+ if (value > 1e9) return new Date(value * 1000);
74
+ }
75
+ if (typeof value === "string") {
76
+ const ms = Date.parse(value);
77
+ if (!Number.isNaN(ms)) return new Date(ms);
78
+ }
79
+ return new Date(fallbackMs);
80
+ }
81
+
82
+ // ── Pure parser: transcript lines → LogEntry[] ──
83
+
84
+ /**
85
+ * Convert OpenClaw transcript JSONL lines (parsed objects, in file order) into
86
+ * `LogEntry[]`. Only `type:"message"` lines carry turns; `session` /
87
+ * `model_change` / `thinking_level_change` / `custom` lines are metadata and
88
+ * are skipped (they'd otherwise clutter the audit with empty system entries).
89
+ * Assistant `toolCall` blocks are paired with their later `toolResult` message
90
+ * by `toolCallId`. Pure — unit-testable with plain line objects.
91
+ */
92
+ export function openclawLinesToLogEntries(
93
+ lines: Record<string, unknown>[],
94
+ source: LogSource = "session",
95
+ ): LogEntry[] {
96
+ const entries: LogEntry[] = [];
97
+ const toolUseById = new Map<string, ToolUseBlock>();
98
+ const toolUseStartMs = new Map<string, number>();
99
+ const baseMs = Date.now();
100
+
101
+ for (let i = 0; i < lines.length; i++) {
102
+ const line = lines[i];
103
+ if (!isPlainObject(line)) continue;
104
+ if (line.type !== "message") continue; // skip session/model_change/custom/…
105
+
106
+ const m = isPlainObject(line.message) ? line.message : undefined;
107
+ if (!m) continue;
108
+ const role = typeof m.role === "string" ? m.role : "system";
109
+
110
+ // Outer line `timestamp` is an ISO string; message.timestamp is epoch ms.
111
+ const date = toDate(line.timestamp ?? m.timestamp, baseMs + i);
112
+ const timestamp = date.toISOString();
113
+ const raw: Record<string, unknown> = {
114
+ uuid: line.id != null ? String(line.id) : `openclaw-${i}`,
115
+ parentUuid: line.parentId != null ? String(line.parentId) : null,
116
+ };
117
+ const base = baseEntry(raw, timestamp, date, source);
118
+
119
+ if (role === "user") {
120
+ entries.push({
121
+ type: "user",
122
+ ...base,
123
+ message: { role: "user", content: extractText(m.content) },
124
+ } satisfies UserEntry);
125
+ continue;
126
+ }
127
+
128
+ if (role === "assistant") {
129
+ const blocks: ContentBlock[] = [];
130
+ const content = m.content;
131
+ if (Array.isArray(content)) {
132
+ for (const b of content) {
133
+ if (!isPlainObject(b)) continue;
134
+ if (b.type === "text" && typeof b.text === "string") {
135
+ blocks.push({ type: "text", text: b.text });
136
+ } else if (b.type === "toolCall") {
137
+ const id = typeof b.id === "string" ? b.id : `${String(b.name ?? "tool")}-${blocks.length}`;
138
+ const name = typeof b.name === "string" ? b.name : "tool";
139
+ const input = isPlainObject(b.arguments) ? b.arguments : {};
140
+ const block: ToolUseBlock = { type: "tool_use", id, name, input };
141
+ blocks.push(block);
142
+ toolUseById.set(id, block);
143
+ toolUseStartMs.set(id, date.getTime());
144
+ }
145
+ }
146
+ } else {
147
+ const text = extractText(content);
148
+ if (text) blocks.push({ type: "text", text });
149
+ }
150
+ if (blocks.length === 0) continue; // empty / failed assistant turn
151
+ entries.push({
152
+ type: "assistant",
153
+ ...base,
154
+ message: { role: "assistant", content: blocks, model: typeof m.model === "string" ? m.model : undefined },
155
+ } satisfies AssistantEntry);
156
+ continue;
157
+ }
158
+
159
+ if (role === "toolResult") {
160
+ const callId = typeof m.toolCallId === "string" ? m.toolCallId : undefined;
161
+ const block = callId ? toolUseById.get(callId) : undefined;
162
+ if (block) {
163
+ const details = isPlainObject(m.details) ? m.details : undefined;
164
+ const startMs = (callId && toolUseStartMs.get(callId)) || date.getTime();
165
+ const durationMs =
166
+ details && typeof details.durationMs === "number"
167
+ ? details.durationMs
168
+ : Math.max(0, date.getTime() - startMs);
169
+ block.result = {
170
+ timestamp,
171
+ timestampFormatted: formatTimestamp(date),
172
+ content: extractText(m.content),
173
+ durationMs,
174
+ durationFormatted: formatDuration(durationMs),
175
+ };
176
+ continue;
177
+ }
178
+ // Orphan tool result — fall through to a system entry.
179
+ }
180
+
181
+ entries.push({ type: "system", ...base, raw } satisfies GenericEntry);
182
+ }
183
+
184
+ entries.sort((a, b) => a.timestampMs - b.timestampMs);
185
+ return entries;
186
+ }
187
+
188
+ // ── Discovery + file loader ──
189
+
190
+ export interface OpenClawTranscriptFile {
191
+ agentId: string;
192
+ sessionId: string;
193
+ transcriptPath: string;
194
+ mtimeMs: number;
195
+ sizeBytes: number;
196
+ }
197
+
198
+ /** Enumerate `agents/<agentId>/sessions/<uuid>.jsonl` transcripts, skipping the
199
+ * heavy `.trajectory.jsonl` OTel traces and pointer files. */
200
+ export function listOpenClawTranscripts(): OpenClawTranscriptFile[] {
201
+ const agentsDir = join(openclawHome(), "agents");
202
+ const out: OpenClawTranscriptFile[] = [];
203
+ let agentIds: string[];
204
+ try {
205
+ agentIds = readdirSync(agentsDir, { withFileTypes: true })
206
+ .filter((d) => d.isDirectory())
207
+ .map((d) => d.name);
208
+ } catch {
209
+ return out;
210
+ }
211
+ for (const agentId of agentIds) {
212
+ const sessionsDir = join(agentsDir, agentId, "sessions");
213
+ let files: string[];
214
+ try {
215
+ files = readdirSync(sessionsDir);
216
+ } catch {
217
+ continue;
218
+ }
219
+ for (const file of files) {
220
+ // Only `<uuid>.jsonl` — not `<uuid>.trajectory.jsonl` / `.trajectory-path.json`.
221
+ if (!file.endsWith(".jsonl") || file.endsWith(".trajectory.jsonl")) continue;
222
+ const sessionId = file.slice(0, -".jsonl".length);
223
+ if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) continue;
224
+ const transcriptPath = join(sessionsDir, file);
225
+ try {
226
+ const st = statSync(transcriptPath);
227
+ out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
228
+ } catch {
229
+ // skip unreadable
230
+ }
231
+ }
232
+ }
233
+ return out;
234
+ }
235
+
236
+ /** Resolve a session UUID to its on-disk transcript path (host-side). Guards
237
+ * against traversal by requiring a UUID filename. Shared by the audit adapter
238
+ * and download-session. */
239
+ export function findOpenClawTranscript(sessionId: string): string | null {
240
+ if (!OPENCLAW_SESSION_ID_RE.test(sessionId)) return null;
241
+ for (const t of listOpenClawTranscripts()) {
242
+ if (t.sessionId === sessionId) return t.transcriptPath;
243
+ }
244
+ return null;
245
+ }
246
+
247
+ export interface OpenClawSessionLogData {
248
+ entries: LogEntry[];
249
+ rawLines: Record<string, unknown>[];
250
+ cwd?: string;
251
+ filePath: string;
252
+ }
253
+
254
+ /** Load and parse one session transcript by UUID. Returns `null` when the file
255
+ * is missing/unreadable or the id fails validation. */
256
+ export async function getOpenClawSessionLog(
257
+ sessionId: string,
258
+ ): Promise<OpenClawSessionLogData | null> {
259
+ const filePath = findOpenClawTranscript(sessionId);
260
+ if (!filePath) return null;
261
+ let content: string;
262
+ try {
263
+ content = await readFile(filePath, "utf-8");
264
+ } catch {
265
+ return null;
266
+ }
267
+ const rawLines = parseRawLines(content, "session");
268
+ const entries = openclawLinesToLogEntries(rawLines, "session");
269
+ // cwd lives on the `type:"session"` header line.
270
+ let cwd: string | undefined;
271
+ for (const line of rawLines) {
272
+ if (isPlainObject(line) && line.type === "session" && typeof line.cwd === "string" && line.cwd.length > 0) {
273
+ cwd = line.cwd;
274
+ break;
275
+ }
276
+ }
277
+ return { entries, rawLines, cwd, filePath };
278
+ }
279
+
280
+ export const getCachedOpenClawSessionLog = runtimeCache(
281
+ (sessionId: string) => getOpenClawSessionLog(sessionId),
282
+ 2,
283
+ { maxSize: 50 },
284
+ );
package/lib/projects.ts CHANGED
@@ -16,7 +16,7 @@ import { formatDate } from "./format-date";
16
16
  export const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
17
17
  export const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
18
18
 
19
- export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "gemini" | "hermes";
19
+ export type ProjectCli = "claude" | "codex" | "copilot" | "cursor" | "opencode" | "pi" | "hermes" | "openclaw" | "factory" | "devin" | "antigravity" | "goose";
20
20
 
21
21
  export interface ProjectFolder {
22
22
  name: string;
@@ -143,18 +143,26 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
143
143
  { getCursorProjects },
144
144
  { getOpenCodeProjects },
145
145
  { getPiProjects },
146
- { getGeminiProjects },
147
146
  { getHermesProjects },
147
+ { getOpenClawProjects },
148
+ { getFactoryProjects },
149
+ { getDevinProjects },
150
+ { getAntigravityProjects },
151
+ { getGooseProjects },
148
152
  ] = await Promise.all([
149
153
  import("./codex-projects"),
150
154
  import("./copilot-projects"),
151
155
  import("./cursor-projects"),
152
156
  import("./opencode-projects"),
153
157
  import("./pi-projects"),
154
- import("./gemini-projects"),
155
158
  import("./hermes-projects"),
159
+ import("./openclaw-projects"),
160
+ import("./factory-projects"),
161
+ import("./devin-projects"),
162
+ import("./antigravity-projects"),
163
+ import("./goose-projects"),
156
164
  ]);
157
- const [claude, codex, copilot, cursor, opencode, pi, gemini, hermes] = await Promise.all([
165
+ const [claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose] = await Promise.all([
158
166
  getClaudeProjectFolders(),
159
167
  getCodexProjects().catch((error) => {
160
168
  logError("Error reading Codex projects:", error);
@@ -176,16 +184,32 @@ export async function getProjectFolders(): Promise<ProjectFolder[]> {
176
184
  logError("Error reading Pi projects:", error);
177
185
  return [] as ProjectFolder[];
178
186
  }),
179
- getGeminiProjects().catch((error) => {
180
- logError("Error reading Gemini projects:", error);
181
- return [] as ProjectFolder[];
182
- }),
183
187
  getHermesProjects().catch((error) => {
184
188
  logError("Error reading Hermes projects:", error);
185
189
  return [] as ProjectFolder[];
186
190
  }),
191
+ getOpenClawProjects().catch((error) => {
192
+ logError("Error reading OpenClaw projects:", error);
193
+ return [] as ProjectFolder[];
194
+ }),
195
+ getFactoryProjects().catch((error) => {
196
+ logError("Error reading Factory projects:", error);
197
+ return [] as ProjectFolder[];
198
+ }),
199
+ getDevinProjects().catch((error) => {
200
+ logError("Error reading Devin projects:", error);
201
+ return [] as ProjectFolder[];
202
+ }),
203
+ getAntigravityProjects().catch((error) => {
204
+ logError("Error reading Antigravity projects:", error);
205
+ return [] as ProjectFolder[];
206
+ }),
207
+ getGooseProjects().catch((error) => {
208
+ logError("Error reading Goose projects:", error);
209
+ return [] as ProjectFolder[];
210
+ }),
187
211
  ]);
188
- return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi, gemini, hermes);
212
+ return mergeProjectFolders(claude, codex, copilot, cursor, opencode, pi, hermes, openclaw, factory, devin, antigravity, goose);
189
213
  }
190
214
 
191
215
  /**
@@ -0,0 +1,43 @@
1
+ import os from "node:os";
2
+
3
+ /** Max length of a telemetry error message; longer strings are truncated. */
4
+ const MAX_ERROR_MESSAGE_LEN = 300;
5
+
6
+ /**
7
+ * Turn an error into a telemetry-safe message string for the `error_message`
8
+ * property on failure events (e.g. `cli_audit_failed`, `audit_run_failed`).
9
+ *
10
+ * Two protections keep this consistent with failproofai's telemetry philosophy
11
+ * (no raw local paths / PII on the wire):
12
+ * 1. The user's home directory is collapsed to `~`, so an error that embeds a
13
+ * path like `/home/alice/.codex/…` is reported as `~/.codex/…`.
14
+ * 2. The message is truncated to a bounded length (with an ellipsis marker).
15
+ *
16
+ * Never throws — falls back to `"unknown"` if the error can't be stringified.
17
+ */
18
+ export function sanitizeErrorMessage(err: unknown): string {
19
+ let msg: string;
20
+ try {
21
+ msg = err instanceof Error ? err.message : String(err);
22
+ } catch {
23
+ return "unknown";
24
+ }
25
+ if (!msg) return "";
26
+
27
+ let home = "";
28
+ try {
29
+ home = os.homedir();
30
+ } catch {
31
+ /* homedir unavailable — skip path stripping */
32
+ }
33
+ // Only strip a real, non-root home to avoid mangling messages when homedir is
34
+ // "/" or empty.
35
+ if (home && home.length > 1) {
36
+ msg = msg.split(home).join("~");
37
+ }
38
+
39
+ if (msg.length > MAX_ERROR_MESSAGE_LEN) {
40
+ msg = msg.slice(0, MAX_ERROR_MESSAGE_LEN) + "…";
41
+ }
42
+ return msg;
43
+ }
@@ -0,0 +1,223 @@
1
+ /**
2
+ * failproofai policy bridge for OpenClaw (openclaw gateway).
3
+ *
4
+ * Loaded in-process by the gateway (registered via `plugins.load.paths` +
5
+ * `plugins.entries.failproofai` in ~/.openclaw/openclaw.json). It subscribes to
6
+ * OpenClaw's typed PLUGIN hooks — the only surface that can block; OpenClaw's
7
+ * file-based "internal hooks" are observation-only — and forwards each to the
8
+ * failproofai binary as `failproofai --hook <event> --cli openclaw`. failproofai
9
+ * prints a flat `{permission, reason}` verdict on stdout; this shim maps it to
10
+ * each hook's native return shape.
11
+ *
12
+ * Marker comment for failproofai's installer detection (do not remove):
13
+ * __failproofai_hook__: true
14
+ *
15
+ * Design notes (verified live against openclaw v2026.7.1):
16
+ * • ASYNC spawn, never spawnSync — the gateway is a long-running multi-channel
17
+ * process; a synchronous spawn on every hook would stall every channel.
18
+ * 30s guard timer; fail-open on any error (never block on infra failure).
19
+ * • Raw forwarding — we send the raw tool name / params and a Claude-shaped
20
+ * stdin (params→tool_input, toolName→tool_name, transcriptPath→
21
+ * transcript_path, stopHookActive→stop_hook_active, sessionKey→session_id).
22
+ * The binary canonicalizes via OPENCLAW_* maps (single source of truth); the
23
+ * shim ships NO inline maps (unlike the OpenCode/Pi shims).
24
+ * • No top-level `await` — OpenClaw's plugin loader rejects it; all async work
25
+ * happens inside register()'s handlers.
26
+ *
27
+ * Binary resolution mirrors pi-extension/index.ts: prefer the bundled
28
+ * dist/cli.mjs (node-compatible), fall back to bin/failproofai.mjs with bun for
29
+ * dev; honor FAILPROOFAI_BINARY_OVERRIDE. Paths resolve relative to this file
30
+ * via import.meta.url (the gateway spawns plugins with an undefined cwd).
31
+ */
32
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
33
+ import { spawn } from "node:child_process";
34
+ import { existsSync } from "node:fs";
35
+ import { resolve, dirname } from "node:path";
36
+ import { fileURLToPath } from "node:url";
37
+
38
+ const HERE = dirname(fileURLToPath(import.meta.url));
39
+ const DIST_BIN = resolve(HERE, "..", "dist", "cli.mjs");
40
+ const SRC_BIN = resolve(HERE, "..", "bin", "failproofai.mjs");
41
+
42
+ function resolveSpawn() {
43
+ if (process.env.FAILPROOFAI_BINARY_OVERRIDE) {
44
+ return { cmd: "node", args: [process.env.FAILPROOFAI_BINARY_OVERRIDE] };
45
+ }
46
+ if (existsSync(DIST_BIN)) {
47
+ return { cmd: "node", args: [DIST_BIN] };
48
+ }
49
+ return { cmd: "bun", args: [SRC_BIN] };
50
+ }
51
+
52
+ function debug(msg) {
53
+ if (process.env.FAILPROOFAI_OPENCLAW_DEBUG === "1") {
54
+ process.stderr.write(`[failproofai-openclaw-shim] ${msg}\n`);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Spawn `failproofai --hook <rawEvent> --cli openclaw`, write the JSON payload
60
+ * to stdin, and resolve the parsed flat `{permission, reason}` verdict. Async +
61
+ * fail-open: any spawn/parse error or timeout resolves to `{permission:"allow"}`
62
+ * so a failproofai fault never blocks the gateway.
63
+ */
64
+ function callPolicy(rawEvent, payload) {
65
+ return new Promise((done) => {
66
+ const { cmd, args } = resolveSpawn();
67
+ let child;
68
+ try {
69
+ child = spawn(cmd, [...args, "--hook", rawEvent, "--cli", "openclaw"], {
70
+ stdio: ["pipe", "pipe", "pipe"],
71
+ });
72
+ } catch (err) {
73
+ debug(`spawn threw: ${err && err.message ? err.message : String(err)}`);
74
+ done({ permission: "allow" });
75
+ return;
76
+ }
77
+ let out = "";
78
+ let settled = false;
79
+ const finish = (verdict) => {
80
+ if (settled) return;
81
+ settled = true;
82
+ clearTimeout(timer);
83
+ done(verdict);
84
+ };
85
+ const timer = setTimeout(() => {
86
+ debug(`timeout on ${rawEvent}; killing child`);
87
+ try { child.kill(); } catch { /* ignore */ }
88
+ finish({ permission: "allow" });
89
+ }, 30_000);
90
+ child.stdout.on("data", (d) => { out += d; });
91
+ child.on("error", (err) => {
92
+ debug(`child error: ${err && err.message ? err.message : String(err)}`);
93
+ finish({ permission: "allow" });
94
+ });
95
+ child.on("close", () => {
96
+ const stdout = out.trim();
97
+ if (!stdout) { finish({ permission: "allow" }); return; }
98
+ try {
99
+ finish(JSON.parse(stdout));
100
+ } catch (err) {
101
+ debug(`parse error: ${err && err.message ? err.message : String(err)}`);
102
+ finish({ permission: "allow" });
103
+ }
104
+ });
105
+ try {
106
+ child.stdin.end(JSON.stringify(payload));
107
+ } catch (err) {
108
+ debug(`stdin write failed: ${err && err.message ? err.message : String(err)}`);
109
+ finish({ permission: "allow" });
110
+ }
111
+ });
112
+ }
113
+
114
+ /** Claude-shaped stdin base. Extra keys are ignored by the binary's payload
115
+ * parser; they're forwarded for future use / activity attribution. */
116
+ function baseMeta(payload, ctx) {
117
+ const p = payload || {};
118
+ const c = ctx || {};
119
+ return {
120
+ session_id: c.sessionId ?? p.sessionId ?? c.sessionKey ?? p.sessionKey,
121
+ cwd: p.cwd ?? c.workspaceDir ?? process.cwd(),
122
+ transcript_path: p.transcriptPath,
123
+ stop_hook_active: p.stopHookActive === true,
124
+ openclaw: {
125
+ agentId: c.agentId,
126
+ sessionKey: c.sessionKey ?? p.sessionKey,
127
+ runId: c.runId ?? p.runId,
128
+ provider: p.provider,
129
+ model: p.model,
130
+ },
131
+ };
132
+ }
133
+
134
+ export default definePluginEntry({
135
+ id: "failproofai",
136
+ name: "failproofai",
137
+ description: "Real-time policy enforcement for OpenClaw by failproofai",
138
+ register(api) {
139
+ // before_tool_call → PreToolUse. Full deny: return {block:true, blockReason}.
140
+ api.on(
141
+ "before_tool_call",
142
+ async (payload, ctx) => {
143
+ const p = payload || {};
144
+ const verdict = await callPolicy("before_tool_call", {
145
+ ...baseMeta(payload, ctx),
146
+ tool_name: p.toolName,
147
+ tool_input: p.params,
148
+ hook_event_name: "before_tool_call",
149
+ });
150
+ if (verdict.permission === "deny") {
151
+ return { block: true, blockReason: verdict.reason || "Blocked by failproofai" };
152
+ }
153
+ return undefined;
154
+ },
155
+ { priority: 100, timeoutMs: 60_000 },
156
+ );
157
+
158
+ // before_agent_run → UserPromptSubmit. Deny: return {outcome:"block", reason}.
159
+ api.on(
160
+ "before_agent_run",
161
+ async (payload, ctx) => {
162
+ const p = payload || {};
163
+ const verdict = await callPolicy("before_agent_run", {
164
+ ...baseMeta(payload, ctx),
165
+ prompt: p.prompt,
166
+ hook_event_name: "before_agent_run",
167
+ });
168
+ if (verdict.permission === "deny") {
169
+ return { outcome: "block", reason: verdict.reason || "Blocked by failproofai" };
170
+ }
171
+ return undefined;
172
+ },
173
+ { priority: 100, timeoutMs: 60_000 },
174
+ );
175
+
176
+ // before_agent_finalize → Stop. The real turn-end gate: a deny becomes a
177
+ // revise so the agent runs another pass. The evaluator supplies the
178
+ // MANDATORY ACTION wording; the payload's stop_hook_active guards the
179
+ // require-*-before-stop builtins against infinite loops.
180
+ api.on(
181
+ "before_agent_finalize",
182
+ async (payload, ctx) => {
183
+ const verdict = await callPolicy("before_agent_finalize", {
184
+ ...baseMeta(payload, ctx),
185
+ hook_event_name: "before_agent_finalize",
186
+ });
187
+ if (verdict.permission === "deny") {
188
+ return { action: "revise", reason: verdict.reason || "Action required by failproofai" };
189
+ }
190
+ return undefined;
191
+ },
192
+ { priority: 100, timeoutMs: 60_000 },
193
+ );
194
+
195
+ // Observation hooks — await so activity ordering is stable, ignore verdict.
196
+ // after_tool_call → PostToolUse; session_start/end → SessionStart/End;
197
+ // subagent_ended → SubagentStop (obs only); before_compaction → PreCompact.
198
+ for (const ev of [
199
+ "after_tool_call",
200
+ "session_start",
201
+ "session_end",
202
+ "subagent_ended",
203
+ "before_compaction",
204
+ ]) {
205
+ api.on(
206
+ ev,
207
+ async (payload, ctx) => {
208
+ const p = payload || {};
209
+ await callPolicy(ev, {
210
+ ...baseMeta(payload, ctx),
211
+ tool_name: p.toolName,
212
+ tool_input: p.params,
213
+ tool_response: p.result,
214
+ reason: p.reason,
215
+ hook_event_name: ev,
216
+ });
217
+ return undefined;
218
+ },
219
+ { priority: 100, timeoutMs: 60_000 },
220
+ );
221
+ }
222
+ },
223
+ });
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "failproofai",
3
+ "name": "failproofai",
4
+ "description": "Real-time policy enforcement for OpenClaw by failproofai",
5
+ "configSchema": {}
6
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@failproofai/openclaw-plugin",
3
+ "version": "0.0.1",
4
+ "description": "failproofai policy bridge for OpenClaw (openclaw gateway)",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "private": true,
8
+ "keywords": [
9
+ "openclaw-plugin",
10
+ "failproofai"
11
+ ]
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.13",
3
+ "version": "0.0.14-beta.0",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -11,6 +11,7 @@
11
11
  "scripts/",
12
12
  "lib/",
13
13
  "pi-extension/",
14
+ "openclaw-plugin/",
14
15
  ".next/standalone/",
15
16
  "dist/",
16
17
  "README.md"
package/scripts/launch.ts CHANGED
@@ -9,19 +9,15 @@ import { createInterface } from "node:readline";
9
9
  import { parseScriptArgs } from "./parse-script-args";
10
10
  import { diagnoseShadow } from "./install-diagnosis.mjs";
11
11
  import { makeSkewLogFilter } from "./skew-log-filter";
12
+ import { renderLaunchBanner } from "../src/hooks/tui";
12
13
  import { version } from "../package.json";
13
14
 
14
15
  export function launch(mode: "dev" | "start"): void {
15
16
  const { loggingLevel, disableTelemetry, allowedDevOrigins, remainingArgs } = parseScriptArgs(process.argv.slice(2));
16
17
 
17
- // Plain-text title + a labeled `Version` line that lines up with the
18
- // `Star us` / `Docs` / `Discord` lines below (all four labels pad to the
19
- // same column so the values form a clean right-hand column).
20
- console.log(`\n failproof ai\n`);
21
- console.log(` 📦 Version: ${version}`);
22
- console.log(` ⭐ Star us: https://github.com/failproofai/failproofai`);
23
- console.log(` 📖 Docs: https://docs.befailproof.ai/introduction`);
24
- console.log(` 💬 Discord: https://discord.gg/2zjBZP7yQJ\n`);
18
+ // Branded splash the same logomark + palette as the `configure` wizard, then
19
+ // a tidy version/links column. Falls back to plain text off a TTY.
20
+ for (const line of renderLaunchBanner(version)) console.log(line);
25
21
 
26
22
  let cmd: string;
27
23
  let cmdArgs: string[];