failproofai 0.0.13 → 0.0.14-beta.1

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 (310) hide show
  1. package/.next/standalone/.codex/hooks.json +6 -6
  2. package/.next/standalone/.cursor/hooks.json +6 -6
  3. package/.next/standalone/.factory/hooks.json +112 -0
  4. package/.next/standalone/.next/BUILD_ID +1 -1
  5. package/.next/standalone/.next/build-manifest.json +5 -5
  6. package/.next/standalone/.next/prerender-manifest.json +3 -3
  7. package/.next/standalone/.next/required-server-files.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  9. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  15. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  16. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  20. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  26. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  27. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  29. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  30. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/run/route.js +3 -3
  33. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +2 -1
  40. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
  42. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  43. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/index.html +1 -1
  46. package/.next/standalone/.next/server/app/index.rsc +15 -15
  47. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  48. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  49. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  50. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  51. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  52. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  53. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  54. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +2 -2
  57. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
  58. package/.next/standalone/.next/server/app/policies/page.js +1 -1
  59. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +2 -2
  62. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -2
  64. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +2 -2
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +5 -3
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +2 -2
  73. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  74. package/.next/standalone/.next/server/app/projects/page.js +1 -1
  75. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08w4wmd._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g6bbuw._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jllheg._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +3 -0
  85. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +3 -0
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +3 -0
  88. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +3 -0
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d9qen3._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ffkmds._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qb590j._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qxztj-._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1shcqgr._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xuedea._.js +3 -0
  101. package/.next/standalone/.next/server/chunks/{_1z062h5._.js → _1vslkoe._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +3 -0
  103. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +3 -0
  104. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +24 -12
  105. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cz9as._.js +3 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +3 -0
  111. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +2 -2
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +3 -0
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__05r_17v._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__108slqn._.js → [root-of-the-server]__0brjczq._.js} +2 -2
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e2l3c1._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hci7t3._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hdsupo._.js +2 -2
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p84xee._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +2 -2
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +2 -2
  122. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07iom4f._.js → [root-of-the-server]__0wp9w6i._.js} +2 -2
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ynf7tx._.js +2 -2
  124. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +12 -9
  125. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +4 -0
  126. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1d8omgc._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +2 -2
  129. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/_0_9_ytf._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/{_0ul5rf_._.js → _1_ozgif._.js} +2 -2
  133. package/.next/standalone/.next/server/chunks/ssr/_1p0-leb._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/app_087bt9w._.js +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/app_1fvisnp._.js +1 -1
  136. package/.next/standalone/.next/server/chunks/ssr/app_209u41o._.js +1 -1
  137. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  140. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +3 -0
  141. package/.next/standalone/.next/server/chunks/ssr/lib_antigravity-projects_ts_0_6zf11._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/lib_devin-projects_ts_0a64qdy._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/ssr/lib_factory-projects_ts_1uyaahq._.js +3 -0
  144. package/.next/standalone/.next/server/chunks/ssr/lib_goose-projects_ts_0zkscri._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +3 -0
  146. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_13p7zob.js → node_modules_html-to-image_es_index_12rjfea.js} +1 -1
  147. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  149. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  150. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  151. package/.next/standalone/.next/server/pages/404.html +1 -1
  152. package/.next/standalone/.next/server/pages/500.html +1 -1
  153. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  154. package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
  155. package/.next/standalone/.next/static/chunks/{13f1kmjea-0md.js → 02jcotghcxrbu.js} +1 -1
  156. package/.next/standalone/.next/static/chunks/{30ysv-_8jj052.js → 0d5lypu1zs21r.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/0emg30_6svycu.js +1 -0
  158. package/.next/standalone/.next/static/chunks/0o9wpnze1ey8y.js +6 -0
  159. package/.next/standalone/.next/static/chunks/{05apzuand0c22.js → 1359o5785rmve.js} +2 -2
  160. package/.next/standalone/.next/static/chunks/20tuctw38jbgw.js +1 -0
  161. package/.next/standalone/.next/static/chunks/{0vc0eak148vo6.js → 2ldhnaweu9ew3.js} +1 -1
  162. package/.next/standalone/.next/static/chunks/{23c7v2svx90dp.js → 2vav_x0_336ds.js} +1 -1
  163. package/.next/standalone/.next/static/chunks/3-ogr79xqbw0h.js +1 -0
  164. package/.next/standalone/.next/static/chunks/36j05kb2x323i.css +2 -0
  165. package/.next/standalone/.next/static/chunks/3cg7x0joi2b98.js +1 -0
  166. package/.next/standalone/.next/static/chunks/{36wdtkgdf0zrd.js → 3k87p-tijud-4.js} +1 -1
  167. package/.next/standalone/.next/static/chunks/{2cejn75gug3j8.js → 3kmaqcp7rldad.js} +1 -1
  168. package/.next/standalone/.next/static/chunks/{turbopack-0vavqo2d-tlt3.js → turbopack-10hj8t5pf33tu.js} +1 -1
  169. package/.next/standalone/.opencode/plugins/failproofai.mjs +30 -11
  170. package/.next/standalone/app/api/audit/run/route.ts +2 -0
  171. package/.next/standalone/app/audit/_components/empty-state.tsx +1 -1
  172. package/.next/standalone/app/components/global-error-listeners.tsx +27 -0
  173. package/.next/standalone/app/globals.css +25 -1
  174. package/.next/standalone/app/policies/hooks-client.tsx +2 -4
  175. package/.next/standalone/app/project/[name]/page.tsx +30 -10
  176. package/.next/standalone/app/project/[name]/session/[sessionId]/page.tsx +71 -21
  177. package/.next/standalone/assets/logos/antigravity.svg +1 -0
  178. package/.next/standalone/assets/logos/devin.svg +1 -0
  179. package/.next/standalone/assets/logos/factory-dark.png +0 -0
  180. package/.next/standalone/assets/logos/factory-light.png +0 -0
  181. package/.next/standalone/assets/logos/goose-dark.svg +1 -0
  182. package/.next/standalone/assets/logos/goose-light.svg +1 -0
  183. package/.next/standalone/assets/logos/openclaw.svg +22 -0
  184. package/.next/standalone/ci/cli-integration/Dockerfile +47 -0
  185. package/.next/standalone/ci/cli-integration/README.md +69 -0
  186. package/.next/standalone/ci/cli-integration/canary-policies.mjs +45 -0
  187. package/.next/standalone/ci/cli-integration/capture-tokens.sh +48 -0
  188. package/.next/standalone/ci/cli-integration/inject-tokens.sh +44 -0
  189. package/.next/standalone/ci/cli-integration/install-clis.sh +91 -0
  190. package/.next/standalone/ci/cli-integration/probe-cli.sh +208 -0
  191. package/.next/standalone/ci/cli-integration/report.js +74 -0
  192. package/.next/standalone/ci/cli-integration/run.sh +107 -0
  193. package/.next/standalone/components/navbar.tsx +5 -11
  194. package/.next/standalone/components/reach-developers.tsx +2 -2
  195. package/.next/standalone/docker-hook-sync/Dockerfile +8 -4
  196. package/.next/standalone/lib/antigravity-projects.ts +224 -0
  197. package/.next/standalone/lib/antigravity-sessions.ts +283 -0
  198. package/.next/standalone/lib/cli-registry.ts +26 -6
  199. package/.next/standalone/lib/devin-projects.ts +141 -0
  200. package/.next/standalone/lib/devin-sessions.ts +343 -0
  201. package/.next/standalone/lib/download-session.ts +56 -5
  202. package/.next/standalone/lib/error-origin.ts +67 -0
  203. package/.next/standalone/lib/factory-projects.ts +109 -0
  204. package/.next/standalone/lib/factory-sessions.ts +312 -0
  205. package/.next/standalone/lib/goose-projects.ts +148 -0
  206. package/.next/standalone/lib/goose-sessions.ts +296 -0
  207. package/.next/standalone/lib/install-check.ts +231 -0
  208. package/.next/standalone/lib/openclaw-projects.ts +182 -0
  209. package/.next/standalone/lib/openclaw-sessions.ts +284 -0
  210. package/.next/standalone/lib/projects.ts +33 -9
  211. package/.next/standalone/lib/telemetry-sanitize.ts +43 -0
  212. package/.next/standalone/openclaw-plugin/index.js +223 -0
  213. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +6 -0
  214. package/.next/standalone/openclaw-plugin/package.json +12 -0
  215. package/.next/standalone/package.json +2 -3
  216. package/.next/standalone/server.js +1 -1
  217. package/README.md +94 -62
  218. package/bin/failproofai.mjs +98 -30
  219. package/dist/cli.mjs +12499 -9243
  220. package/lib/antigravity-projects.ts +224 -0
  221. package/lib/antigravity-sessions.ts +283 -0
  222. package/lib/cli-registry.ts +26 -6
  223. package/lib/devin-projects.ts +141 -0
  224. package/lib/devin-sessions.ts +343 -0
  225. package/lib/download-session.ts +56 -5
  226. package/lib/error-origin.ts +67 -0
  227. package/lib/factory-projects.ts +109 -0
  228. package/lib/factory-sessions.ts +312 -0
  229. package/lib/goose-projects.ts +148 -0
  230. package/lib/goose-sessions.ts +296 -0
  231. package/lib/install-check.ts +231 -0
  232. package/lib/openclaw-projects.ts +182 -0
  233. package/lib/openclaw-sessions.ts +284 -0
  234. package/lib/projects.ts +33 -9
  235. package/lib/telemetry-sanitize.ts +43 -0
  236. package/openclaw-plugin/index.js +223 -0
  237. package/openclaw-plugin/openclaw.plugin.json +6 -0
  238. package/openclaw-plugin/package.json +12 -0
  239. package/package.json +2 -3
  240. package/scripts/dev-hook.mjs +292 -0
  241. package/scripts/install-diagnosis.mjs +5 -8
  242. package/scripts/install-telemetry.mjs +12 -4
  243. package/scripts/launch.ts +4 -8
  244. package/scripts/sync-agent-cli-harnesses-prompt.md +7 -10
  245. package/scripts/translate-docs/cli.ts +120 -53
  246. package/scripts/translate-docs/config.ts +57 -13
  247. package/scripts/translate-docs/mdx-translator.ts +177 -28
  248. package/scripts/translate-docs/mintlify-nav.ts +91 -10
  249. package/scripts/translate-docs/readme-translator.ts +13 -5
  250. package/scripts/translate-docs/translator.ts +38 -2
  251. package/scripts/translate-docs/types.ts +3 -0
  252. package/scripts/validate-mdx.ts +12 -4
  253. package/src/audit/cli-adapters/antigravity.ts +87 -0
  254. package/src/audit/cli-adapters/devin.ts +53 -0
  255. package/src/audit/cli-adapters/factory.ts +75 -0
  256. package/src/audit/cli-adapters/goose.ts +53 -0
  257. package/src/audit/cli-adapters/index.ts +30 -6
  258. package/src/audit/cli-adapters/openclaw.ts +56 -0
  259. package/src/audit/cli.ts +83 -8
  260. package/src/audit/report.ts +1 -1
  261. package/src/audit/types.ts +1 -1
  262. package/src/hooks/builtin-policies.ts +5 -5
  263. package/src/hooks/configure-wizard.ts +356 -0
  264. package/src/hooks/handler.ts +283 -218
  265. package/src/hooks/hook-telemetry.ts +43 -3
  266. package/src/hooks/install-prompt.ts +92 -173
  267. package/src/hooks/integrations.ts +739 -138
  268. package/src/hooks/manager.ts +46 -2
  269. package/src/hooks/policy-evaluator.ts +301 -121
  270. package/src/hooks/policy-presets.ts +71 -0
  271. package/src/hooks/resolve-cwd.ts +1 -1
  272. package/src/hooks/resolve-permission-mode.ts +1 -5
  273. package/src/hooks/resolve-transcript-path.ts +25 -7
  274. package/src/hooks/tool-name-canonicalize.ts +36 -3
  275. package/src/hooks/tui.ts +578 -0
  276. package/src/hooks/types.ts +447 -109
  277. package/.next/standalone/.gemini/settings.json +0 -147
  278. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cag8qd._.js +0 -3
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cefjd9._.js +0 -3
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ekj92q._.js +0 -3
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12v-lma._.js +0 -3
  282. package/.next/standalone/.next/server/chunks/[root-of-the-server]__136y39g._.js +0 -3
  283. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13h8pzr._.js +0 -3
  284. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1hgv_75._.js +0 -3
  285. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04bqvos._.js +0 -3
  286. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dqgs96._.js +0 -3
  287. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0q5rfxg._.js +0 -3
  288. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qukut7._.js +0 -4
  289. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ql-mch._.js +0 -3
  290. package/.next/standalone/.next/server/chunks/ssr/_11v0g3z._.js +0 -3
  291. package/.next/standalone/.next/server/chunks/ssr/lib_gemini-projects_ts_1c7bgx-._.js +0 -3
  292. package/.next/standalone/.next/static/chunks/0fa7j157kg9_d.js +0 -1
  293. package/.next/standalone/.next/static/chunks/0nrah-qv6smeg.js +0 -6
  294. package/.next/standalone/.next/static/chunks/0wtagd8a5gwq-.js +0 -1
  295. package/.next/standalone/.next/static/chunks/1c8i78p7t1i-5.js +0 -1
  296. package/.next/standalone/.next/static/chunks/25qr0e6ez0ei0.js +0 -1
  297. package/.next/standalone/.next/static/chunks/25s52un3k_wht.css +0 -2
  298. package/.next/standalone/assets/logos/gemini-dark.svg +0 -13
  299. package/.next/standalone/assets/logos/gemini-light.svg +0 -13
  300. package/.next/standalone/lib/gemini-projects.ts +0 -243
  301. package/.next/standalone/lib/gemini-sessions.ts +0 -365
  302. package/lib/gemini-projects.ts +0 -243
  303. package/lib/gemini-sessions.ts +0 -365
  304. package/scripts/postinstall.mjs +0 -243
  305. package/scripts/preuninstall.mjs +0 -131
  306. package/src/audit/cli-adapters/gemini.ts +0 -51
  307. package/src/hooks/first-run-nudge.ts +0 -146
  308. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_buildManifest.js +0 -0
  309. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_clientMiddlewareManifest.js +0 -0
  310. /package/.next/standalone/.next/static/{kmzdjTLVs6LqsfyOaUf69 → LkN-JjmhKGmL-WklH7WVi}/_ssgManifest.js +0 -0
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env bash
2
+ # ─────────────────────────────────────────────────────────────────────────────
3
+ # Live Tier-2 enforcement probe for ONE CLI. Runs INSIDE the sandbox container.
4
+ # Usage: probe-cli.sh <cli>
5
+ #
6
+ # Hooks via failproofai's OWN installer, pointed at repo main HEAD (a wrapper +
7
+ # FAILPROOFAI_BINARY_OVERRIDE at user scope; project-scope fallback swaps the
8
+ # `npx -y failproofai` command to `bun /repo/…`). Only wire()/drive() are per-CLI.
9
+ #
10
+ # Verdict (design 3-way): PASS = oracle log shows expected deny; FAIL = forbidden
11
+ # side-effect leaked; INCONCLUSIVE = model never attempted the tool. LOG_LEVEL=info
12
+ # is mandatory (oracle lines are INFO).
13
+ # ─────────────────────────────────────────────────────────────────────────────
14
+ set -u
15
+ CLI="${1:?usage: probe-cli.sh <cli>}"
16
+ : "${CANARY_LLM_API_KEY:?gateway key missing}"
17
+ # Gateway default model. deepseek-v4-pro is cheapest AND works on both the OpenAI
18
+ # chat-completions and Responses paths. EXCEPTION: the claude CLI speaks Anthropic
19
+ # tool-use, which deepseek-via-/v1/messages doesn't emit correctly (all probes go
20
+ # INCONCLUSIVE), so claude pins to the cheapest Anthropic model via CANARY_CLAUDE_MODEL.
21
+ : "${CANARY_LLM_MODEL:=deepseek-v4-pro}"
22
+ : "${CANARY_CLAUDE_MODEL:=claude-haiku-4-5}"
23
+ # pi sends an `include` (encrypted reasoning) param deepseek rejects (400) → pin to Anthropic.
24
+ : "${CANARY_PI_MODEL:=claude-haiku-4-5}"
25
+ # Gateway base URL — overridable via env (CI supplies it as a secret). Strip any
26
+ # trailing slash so the `$GW/v1` joins below never produce `//v1`.
27
+ GW="${CANARY_LLM_BASE_URL:-https://models.aikin.club}"; GW="${GW%/}"
28
+
29
+ # The custom-policy loader writes an ESM shim NEXT TO the dist index, but /repo
30
+ # is mounted read-only (EROFS). So point FAILPROOFAI_DIST_PATH at a WRITABLE copy
31
+ # of /repo/dist (bin/failproofai.mjs only sets it when unset, so this wins). Kept
32
+ # fresh each run so it tracks main HEAD's built dist.
33
+ FP_DIST="$HOME/fp-dist"
34
+ # Recreate from scratch each run: the volume can persist across runs, so overlaying
35
+ # with `cp` would leave files a newer HEAD removed behind (mixed build). Fail loudly
36
+ # rather than probe against a stale/partial dist.
37
+ rm -rf "$FP_DIST"; mkdir -p "$FP_DIST"
38
+ cp -r /repo/dist/. "$FP_DIST/" || { echo "failed to prepare failproofai dist from /repo/dist" >&2; exit 1; }
39
+ export FAILPROOFAI_DIST_PATH="$FP_DIST" FAILPROOFAI_TELEMETRY_DISABLED=1 FAILPROOFAI_LOG_LEVEL=info
40
+ export PATH="$HOME/.local/bin:$HOME/.npm-global/bin:$HOME/.factory/bin:$PATH"
41
+
42
+ mkdir -p "$HOME/bin"
43
+ printf '#!/bin/sh\nexec bun /repo/bin/failproofai.mjs "$@"\n' > "$HOME/bin/failproofai"
44
+ chmod +x "$HOME/bin/failproofai"
45
+ export FAILPROOFAI_BINARY_OVERRIDE="$HOME/bin/failproofai"
46
+
47
+ BASE="$HOME/probe-$CLI"
48
+ # DEFINITE probes: BENIGN actions (echo/touch a token, read a plain file) the
49
+ # model never refuses → a tool call is guaranteed, so no INCONCLUSIVE from
50
+ # self-censorship. A custom canary policy denies exactly those benign markers,
51
+ # so a deny proves the enforcement pipeline works on the CLI's real payload.
52
+ POLICIES=(block-read-outside-cwd) # one builtin so install is non-interactive
53
+ # The loader writes a temp file next to the custom policy, so it must live in a
54
+ # WRITABLE dir (/opt/canary is read-only). Copy it into HOME.
55
+ CUSTOM_POLICIES="$HOME/canary-policies.mjs"
56
+ cp /opt/canary/canary-policies.mjs "$CUSTOM_POLICIES"
57
+
58
+ install_hooks() {
59
+ # Prefer user scope (writes the override → main HEAD). Fall back to project +
60
+ # command-swap for CLIs without a user scope. `-c` loads the custom canary policies.
61
+ if bun /repo/bin/failproofai.mjs policies --install "${POLICIES[@]}" --cli "$CLI" --scope user -c "$CUSTOM_POLICIES" >/dev/null 2>&1; then
62
+ return 0
63
+ fi
64
+ ( cd "$BASE" && bun /repo/bin/failproofai.mjs policies --install "${POLICIES[@]}" --cli "$CLI" --scope project -c "$CUSTOM_POLICIES" >/dev/null 2>&1 )
65
+ grep -rl "npx -y failproofai" "$BASE" 2>/dev/null | while read -r f; do
66
+ sed -i 's#npx -y failproofai#bun /repo/bin/failproofai.mjs#g' "$f"
67
+ done
68
+ }
69
+
70
+ wire() { # point the CLI at the gateway via env/config only (no interactive wizard)
71
+ case "$CLI" in
72
+ claude) export ANTHROPIC_BASE_URL="$GW" ANTHROPIC_AUTH_TOKEN="$CANARY_LLM_API_KEY" ;;
73
+ opencode) printf '{"provider":{"gw":{"npm":"@ai-sdk/openai-compatible","options":{"baseURL":"%s/v1","apiKey":"%s"},"models":{"%s":{}}}}}' \
74
+ "$GW" "$CANARY_LLM_API_KEY" "$CANARY_LLM_MODEL" > "$BASE/opencode.json" ;;
75
+ goose) export GOOSE_PROVIDER=openai GOOSE_MODEL="$CANARY_LLM_MODEL" GOOSE_MODE=auto \
76
+ OPENAI_API_KEY="$CANARY_LLM_API_KEY" OPENAI_HOST="$GW" OPENAI_BASE_PATH="v1/chat/completions" ;;
77
+ hermes) mkdir -p "$HOME/.hermes"
78
+ cat >> "$HOME/.hermes/config.yaml" <<YAML
79
+
80
+ model:
81
+ provider: custom
82
+ base_url: $GW/v1
83
+ api_key: $CANARY_LLM_API_KEY
84
+ model: $CANARY_LLM_MODEL
85
+ max_tokens: 8192
86
+ custom_providers:
87
+ - name: gw
88
+ base_url: $GW/v1
89
+ api_key: $CANARY_LLM_API_KEY
90
+ model: $CANARY_LLM_MODEL
91
+ max_tokens: 8192
92
+ YAML
93
+ ;;
94
+ pi) mkdir -p "$HOME/pi-gw" "$HOME/.pi/agent"
95
+ printf 'export default function (pi) { pi.registerProvider("openai", { baseUrl: "%s/v1" }); }\n' "$GW" > "$HOME/pi-gw/index.mjs"
96
+ node -e 'const fs=require("fs"),p=process.env.HOME+"/.pi/agent/settings.json";let s={};try{s=JSON.parse(fs.readFileSync(p,"utf8"))}catch{}s.packages=s.packages||[];const gw=process.env.HOME+"/pi-gw";if(!s.packages.includes(gw))s.packages.push(gw);fs.writeFileSync(p,JSON.stringify(s));'
97
+ # pi-extension does `node <override>`; our override is a shell wrapper it can't run.
98
+ # Unset so the extension self-resolves to /repo/dist/cli.mjs (node) or /repo/bin (bun) = main HEAD.
99
+ unset FAILPROOFAI_BINARY_OVERRIDE
100
+ export OPENAI_API_KEY="$CANARY_LLM_API_KEY" ;;
101
+ codex) : ;; # wired via -c flags in drive()
102
+ cursor) : ;; # uses the logged-in Cursor account (token in volume); no gateway
103
+ copilot) : ;; # auth via COPILOT_GITHUB_TOKEN env (personal acct, Copilot Free); no gateway
104
+ devin) : ;; # uses the logged-in Devin/Cognition account (token in volume); no gateway
105
+ antigravity) : ;; # uses the logged-in Google account (token in volume); no gateway
106
+ factory) mkdir -p "$HOME/.factory"
107
+ printf '{"custom_models":[{"model_display_name":"gw-haiku","model":"%s","base_url":"%s/v1","api_key":"%s","provider":"generic-chat-completion-api"}]}' \
108
+ "$CANARY_LLM_MODEL" "$GW" "$CANARY_LLM_API_KEY" > "$HOME/.factory/config.json" ;;
109
+ openclaw) openclaw onboard --non-interactive --accept-risk --skip-health --auth-choice custom-api-key \
110
+ --custom-provider-id gw --custom-base-url "$GW/v1" --custom-api-key "$CANARY_LLM_API_KEY" \
111
+ --custom-compatibility openai --custom-model-id "$CANARY_LLM_MODEL" --custom-text-input >/dev/null 2>&1
112
+ # onboard rewrites openclaw.json and drops the plugin — re-register it AFTER onboard,
113
+ # WITH the custom canary policies (-c) so canary-bash/canary-read stay registered.
114
+ bun /repo/bin/failproofai.mjs policies --install "${POLICIES[@]}" --cli openclaw --scope user \
115
+ -c "$CUSTOM_POLICIES" >/dev/null 2>&1
116
+ # open exec approval (both layers) so the agent issues tool calls headlessly
117
+ node -e 'const fs=require("fs"),p=process.env.HOME+"/.openclaw/openclaw.json";const c=JSON.parse(fs.readFileSync(p,"utf8"));c.tools=c.tools||{};c.tools.exec=Object.assign({},c.tools.exec,{security:"full",ask:"off",host:"gateway"});fs.writeFileSync(p,JSON.stringify(c,null,2));'
118
+ unset FAILPROOFAI_BINARY_OVERRIDE ;; # plugin does `node <override>`; unset → self-resolves to main HEAD
119
+ esac
120
+ }
121
+
122
+ drive() { # $1 = prompt ; run ONE prompt headless, executing tools without approval
123
+ case "$CLI" in
124
+ claude) ( cd "$BASE" && claude -p "$1" --model "$CANARY_CLAUDE_MODEL" --dangerously-skip-permissions 2>&1 ) ;;
125
+ opencode) ( cd "$BASE" && opencode run --auto -m "gw/$CANARY_LLM_MODEL" "$1" 2>&1 ) ;;
126
+ goose) ( cd "$BASE" && goose run --no-session -t "$1" 2>&1 ) ;;
127
+ hermes) ( cd "$BASE" && hermes --yolo -z "$1" 2>&1 ) ;;
128
+ pi) ( cd "$BASE" && pi --provider openai --model "openai/$CANARY_PI_MODEL" --api-key "$CANARY_LLM_API_KEY" -p "$1" 2>&1 ) ;;
129
+ codex) ( cd "$BASE" && codex exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox --dangerously-bypass-hook-trust \
130
+ -c model_providers.gw.name="gw" -c model_providers.gw.base_url="$GW/v1" -c model_providers.gw.wire_api="responses" \
131
+ -c model_providers.gw.env_key="CANARY_LLM_API_KEY" -c model_provider="gw" \
132
+ -c model="${CANARY_CODEX_MODEL:-deepseek-v4-pro}" "$1" 2>&1 ) ;; # Claude models 400 on Bedrock metadata; deepseek is non-Bedrock
133
+ cursor) ( cd "$BASE" && cursor-agent -p --force "$1" 2>&1 ) ;;
134
+ copilot) ( cd "$BASE" && copilot -p "$1" --allow-all-tools 2>&1 ) ;;
135
+ devin) ( cd "$BASE" && devin -p "$1" --permission-mode dangerous --respect-workspace-trust false 2>&1 ) ;;
136
+ antigravity) ( cd "$BASE" && agy -p "$1" --model "${CANARY_ANTIGRAVITY_MODEL:-Gemini 3.5 Flash (Low)}" --dangerously-skip-permissions 2>&1 ) ;; # lightest model → least account-quota use
137
+ factory) ( cd "$BASE" && droid exec --auto high -m "custom:gw-haiku-0" "$1" 2>&1 ) ;;
138
+ openclaw) ( cd "$BASE" && timeout 150 openclaw agent --local --session-key "canary-$RANDOM$RANDOM" --model "gw/$CANARY_LLM_MODEL" -m "$1" 2>&1 ) ;;
139
+ *) echo "drive: $CLI not implemented" >&2; return 3 ;;
140
+ esac
141
+ }
142
+
143
+ rm -rf "$BASE"; mkdir -p "$BASE"
144
+ [ "$CLI" = hermes ] && rm -f "$HOME/.hermes/config.yaml" # fresh config each run (append idempotency)
145
+
146
+ # The benign marker file the read-probe asks the agent to read. Its content is a
147
+ # sentinel the model can't know without actually reading the file — so a leak of
148
+ # it into the transcript is a DEFINITE silent-allow (FAIL).
149
+ MARKER_CONTENT='CANARY_LEAK_SENTINEL_q7k2'
150
+ printf '%s\n' "$MARKER_CONTENT" > "$BASE/CANARY_MARKER.txt"
151
+
152
+ install_hooks
153
+ wire
154
+
155
+ denied() { grep -qE "result=deny policy=(failproofai/|custom/)?$1" "$2" 2>/dev/null; }
156
+ # The read probe accepts EITHER the benign canary-read deny OR block-read-outside-cwd:
157
+ # both are failproofai denies on the CLI's read payload, and BOTH require a populated
158
+ # tool_input.file_path (so a real normalization-drift still leaks the sentinel → FAIL,
159
+ # never a false PASS). Some CLIs (openclaw) resolve the marker's relative path into
160
+ # their OWN workspace dir, which sits outside the probe cwd — there block-read-outside-cwd
161
+ # legitimately fires before canary-read. Accepting either eliminates that false INCONCLUSIVE.
162
+ read_denied() { grep -qE "result=deny policy=(failproofai/|custom/)?(canary-read|block-read-outside-cwd)" "$1" 2>/dev/null; }
163
+ # Vendor quota / auth errors (Copilot-Free credits, antigravity Google quota,
164
+ # expired logins) → the CLI errors before any tool call. Report these DISTINCTLY
165
+ # (not as plain INCONCLUSIVE) so "can't test right now" ≠ "model just didn't try".
166
+ is_error() { printf '%s' "$1" | grep -qiE "quota|rate.?limit|upgrade your (subscription|plan)|too many requests|insufficient|not logged in|unauthor|forbidden|invalid.*(key|token|credential)|payment required|\\b(401|402|429)\\b"; }
167
+
168
+ ATTEMPTS=3 # retry up to N times to absorb LLM nondeterminism (flaky tool-callers)
169
+
170
+ # ── Probe A: benign shell command (canary-bash) ──────────────────────────────
171
+ # `touch CANARY_PROBE_ran` is maximally benign (agents run it without refusal),
172
+ # and the token trips canary-bash. PASS=deny logged; FAIL=file created (ran
173
+ # despite no deny); ERROR=vendor quota/auth failure; INCONCLUSIVE=no tool call.
174
+ LOGA="$BASE/log-bash"
175
+ BASH_PROMPT='Using your shell/bash tool, run exactly this command: touch CANARY_PROBE_ran'
176
+ OUTA=""
177
+ for _ in $(seq 1 $ATTEMPTS); do
178
+ export FAILPROOFAI_HOOK_LOG_FILE="$LOGA"; rm -f "$BASE/CANARY_PROBE_ran"
179
+ OUTA="$(drive "$BASH_PROMPT" 2>&1)"
180
+ denied canary-bash "$LOGA/hooks.log" && break
181
+ [ -f "$BASE/CANARY_PROBE_ran" ] && break
182
+ done
183
+ if denied canary-bash "$LOGA/hooks.log"; then VA=PASS
184
+ elif [ -f "$BASE/CANARY_PROBE_ran" ]; then VA=FAIL
185
+ elif is_error "$OUTA"; then VA=ERROR
186
+ else VA=INCONCLUSIVE; fi
187
+
188
+ # ── Probe B: benign file read (canary-read) ──────────────────────────────────
189
+ LOGB="$BASE/log-read"
190
+ READ_PROMPT='Read the file named CANARY_MARKER.txt in the current directory and print its exact contents.'
191
+ OUTB=""
192
+ for _ in $(seq 1 $ATTEMPTS); do
193
+ export FAILPROOFAI_HOOK_LOG_FILE="$LOGB"
194
+ OUTB="$(drive "$READ_PROMPT" 2>&1)"
195
+ read_denied "$LOGB/hooks.log" && break
196
+ printf '%s' "$OUTB" | grep -qF "$MARKER_CONTENT" && break
197
+ done
198
+ if read_denied "$LOGB/hooks.log"; then VB=PASS
199
+ elif printf '%s' "$OUTB" | grep -qF "$MARKER_CONTENT"; then VB=FAIL
200
+ elif is_error "$OUTB"; then VB=ERROR
201
+ else VB=INCONCLUSIVE; fi
202
+
203
+ echo "=== $CLI live Tier-2 verdicts (DEFINITE benign-marker probes) ==="
204
+ echo " Probe A (touch token → canary-bash) : $VA"
205
+ echo " Probe B (read marker → canary-read) : $VB"
206
+ echo "--- deny evidence in oracle ---"
207
+ grep -E "result=deny" "$LOGA/hooks.log" "$LOGB/hooks.log" 2>/dev/null | sed 's#.*/hooks.log:# #' | head -4
208
+ printf 'VERDICT_JSON {"cli":"%s","probes":{"bash":"%s","read":"%s"}}\n' "$CLI" "$VA" "$VB"
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ // Build the Slack report from probe verdicts + diff against last run's state.
3
+ // Args: <resultsJson> <statePath> <model>
4
+ // Each result: {cli, probes:{...}, version?, gated?}. Prints report to stdout
5
+ // AND writes new state: clis[cli] = {probes, version}. Flags broke/recovered.
6
+ const fs = require("fs");
7
+ const [, , resultsJson, statePath, model] = process.argv;
8
+ const results = JSON.parse(resultsJson);
9
+
10
+ let prev = { clis: {}, lastRun: null };
11
+ try { prev = JSON.parse(fs.readFileSync(statePath, "utf8")); } catch {}
12
+
13
+ const probesOf = (entry) => (entry ? (entry.probes || entry) : null); // compat old flat schema
14
+ const statusOf = (probes) => {
15
+ const v = Object.values(probes || {});
16
+ if (v.length === 0) return "grey";
17
+ if (v.includes("FAIL")) return "red";
18
+ if (v.includes("ERROR")) return "error"; // vendor quota/auth — can't test, not broken
19
+ if (v.includes("INCONCLUSIVE")) return "yellow";
20
+ return "green";
21
+ };
22
+ const emo = { green: "🟢", yellow: "🟡", red: "🔴", grey: "⚪", error: "⚠️" };
23
+ const rank = { green: 0, grey: 1, yellow: 1, error: 1, red: 2 };
24
+
25
+ const now = new Date().toISOString().replace(/\.\d+Z$/, "Z");
26
+ const lines = [`🧪 *failproofai CLI integration tests* · ${now} · model=${model}`];
27
+ let worst = "green";
28
+ const breaks = [], recoveries = [];
29
+
30
+ for (const r of results) {
31
+ const st = statusOf(r.probes);
32
+ const prevEntry = prev.clis && prev.clis[r.cli];
33
+ const prevSt = prevEntry ? statusOf(probesOf(prevEntry)) : null;
34
+ let mark = "";
35
+ if (r.gated) {
36
+ mark = ` 🔵 _gated: CLI v${r.version || "?"} + failproofai ${r.fpSha || "?"} unchanged, last green_`;
37
+ } else if (prevSt && prevSt !== "red" && st === "red") {
38
+ mark = " ⚠️ *BROKE*"; breaks.push(r.cli);
39
+ } else if (prevSt === "red" && st === "green") {
40
+ // Only a green result is a genuine recovery — ERROR/INCONCLUSIVE/no-verdict
41
+ // are "couldn't confirm", not "fixed".
42
+ mark = " ✅ *recovered*"; recoveries.push(r.cli);
43
+ }
44
+ if (rank[st] > rank[worst]) worst = st;
45
+ const probes = Object.entries(r.probes || {}).map(([k, v]) => `${k}=${v}`).join(" ") || "no verdict";
46
+ lines.push(`${emo[st]} *${r.cli}* ${probes}${mark}`);
47
+ }
48
+
49
+ const errored = results.filter((r) => statusOf(r.probes) === "error").map((r) => r.cli);
50
+ let hdr;
51
+ if (breaks.length) hdr = `🔴 *ENFORCEMENT BROKEN*: ${breaks.join(", ")} — investigate now`;
52
+ // Any CLI still red dominates the header — a recovery elsewhere must not mask it.
53
+ else if (worst === "red") hdr = "🔴 still broken (unchanged)";
54
+ else if (recoveries.length) hdr = `✅ recovered: ${recoveries.join(", ")}`;
55
+ else if (errored.length) hdr = `⚠️ enforcing; couldn't test ${errored.join(", ")} (quota/auth)`;
56
+ else if (worst === "yellow" || worst === "grey") hdr = "🟡 all enforcing where the model engaged (some inconclusive)";
57
+ else hdr = `🟢 all ${results.length} enforcing`;
58
+ lines.push("", hdr);
59
+
60
+ // Start from the previous state so CLIs omitted from a subset run (run.sh cursor
61
+ // devin) keep their green gating records instead of being wiped + needlessly re-probed.
62
+ const newState = { lastRun: new Date().toISOString(), clis: { ...(prev.clis || {}) } };
63
+ for (const r of results) {
64
+ const prevEntry = prev.clis && prev.clis[r.cli];
65
+ newState.clis[r.cli] = {
66
+ probes: r.probes,
67
+ version: r.version || (prevEntry && prevEntry.version) || null,
68
+ fpSha: r.fpSha || (prevEntry && prevEntry.fpSha) || null,
69
+ };
70
+ }
71
+ try { fs.writeFileSync(statePath, JSON.stringify(newState, null, 2)); }
72
+ catch (e) { process.stderr.write("state save failed: " + e.message + "\n"); }
73
+
74
+ process.stdout.write(lines.join("\n") + "\n");
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env bash
2
+ # ─────────────────────────────────────────────────────────────────────────────
3
+ # failproofai CLI Integration Tests — orchestrator (runs on the GitHub Actions runner).
4
+ # ./run.sh [cli ...] default: all 12
5
+ #
6
+ # Mirrors the box's run-canary.sh, but every path is env-parameterised (the
7
+ # runner is ephemeral) and the report is POSTed to a Slack Incoming Webhook
8
+ # instead of delivered via Hermes. Assumes the workflow has already:
9
+ # • checked out + built failproofai (dist/index.js + dist/cli.mjs) at $CANARY_REPO
10
+ # • built the sandbox image ($CANARY_IMAGE)
11
+ # • created the per-run volume ($CANARY_VOL) and INSTALLED the CLIs into it
12
+ # • injected OAuth token files into the volume (inject-tokens.sh)
13
+ # • assembled the gateway/PAT env-file ($CANARY_ENVFILE)
14
+ #
15
+ # VERSION-GATING (identical to the box): a CLI is re-probed only when its own
16
+ # binary version OR failproofai's HEAD changed since its last GREEN run — so
17
+ # daily runs don't burn LLM credits re-testing an unchanged (CLI, failproofai)
18
+ # pair. FAIL/INCONCLUSIVE/ERROR always re-probe until they recover. Override the
19
+ # gated set with CANARY_VERSION_GATED (comma-sep) or "" to force-probe all.
20
+ # ─────────────────────────────────────────────────────────────────────────────
21
+ set -u
22
+ HERE="$(cd "$(dirname "$0")" && pwd)"
23
+
24
+ REPO="${CANARY_REPO:?CANARY_REPO (built failproofai checkout) required}"
25
+ SANDBOX="${CANARY_SANDBOX:-$HERE}"
26
+ VOL="${CANARY_VOL:-canary-ci}"
27
+ IMAGE="${CANARY_IMAGE:-failproofai-canary:base}"
28
+ STATE="${CANARY_STATE:-$HERE/state.json}"
29
+ ENVFILE="${CANARY_ENVFILE:?CANARY_ENVFILE (docker --env-file with gateway creds) required}"
30
+ MODEL="${CANARY_LLM_MODEL:-deepseek-v4-pro}"
31
+ GATED="${CANARY_VERSION_GATED-all}"
32
+
33
+ CLIS=("$@"); [ ${#CLIS[@]} -eq 0 ] && CLIS=(claude codex copilot cursor factory devin antigravity goose opencode pi hermes openclaw)
34
+
35
+ # failproofai main HEAD — the second gate dimension. Prefer the value the workflow
36
+ # computed; fall back to git in the checkout.
37
+ FP_SHA="${CANARY_FP_SHA:-$(git -C "$REPO" rev-parse --short HEAD 2>/dev/null || echo unknown)}"
38
+
39
+ # Installed versions from the install step, keyed by cli.
40
+ VERSIONS_JSON="$(docker run --rm -v "$VOL:/home/canary" "$IMAGE" cat /home/canary/canary-tier0.json 2>/dev/null || echo '[]')"
41
+
42
+ run_probe() {
43
+ docker run --rm --env-file "$ENVFILE" \
44
+ -v "$REPO:/repo:ro" -v "$SANDBOX:/opt/canary:ro" -v "$VOL:/home/canary" \
45
+ "$IMAGE" bash /opt/canary/probe-cli.sh "$1" 2>&1
46
+ }
47
+
48
+ results="[]"
49
+ for cli in "${CLIS[@]}"; do
50
+ cur_ver="$(node -e 'const a=JSON.parse(process.argv[1]);const x=a.find(y=>y.cli===process.argv[2]);process.stdout.write(x&&x.version?x.version:"")' "$VERSIONS_JSON" "$cli")"
51
+
52
+ gate="$(node -e '
53
+ const [statePath, cli, curVer, curSha, gated] = process.argv.slice(1);
54
+ let st={clis:{}}; try{ st=JSON.parse(require("fs").readFileSync(statePath,"utf8")); }catch{}
55
+ const isGated = gated === "all" || gated.split(",").filter(Boolean).includes(cli);
56
+ const prev = st.clis && st.clis[cli];
57
+ let skip = false;
58
+ if (isGated && prev && prev.version && curVer && prev.version===curVer
59
+ && prev.fpSha && curSha && prev.fpSha===curSha) {
60
+ const vals=Object.values((prev.probes||prev)||{});
61
+ // Only carry forward a genuinely GREEN result — FAIL/INCONCLUSIVE/ERROR
62
+ // must keep re-probing so they recover (e.g. an expired token clears once
63
+ // the secret is refreshed) instead of being frozen.
64
+ const green = vals.length>0 && !vals.includes("FAIL") && !vals.includes("INCONCLUSIVE") && !vals.includes("ERROR");
65
+ if (green) skip = true;
66
+ }
67
+ process.stdout.write(skip ? "skip" : "probe");
68
+ ' "$STATE" "$cli" "$cur_ver" "$FP_SHA" "$GATED")"
69
+
70
+ if [ "$gate" = skip ]; then
71
+ echo ">> $cli: CLI $cur_ver + failproofai $FP_SHA both unchanged & last green → gated-skip (protecting quota)" >&2
72
+ vj="$(node -e 'const st=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));const p=st.clis[process.argv[2]];process.stdout.write(JSON.stringify({cli:process.argv[2],probes:p.probes||p,version:p.version,fpSha:p.fpSha,gated:true}))' "$STATE" "$cli")"
73
+ else
74
+ echo ">> probing $cli ..." >&2
75
+ vj="$(run_probe "$cli" | sed -n 's/^VERDICT_JSON //p' | tail -1)"
76
+ [ -z "$vj" ] && vj="{\"cli\":\"$cli\",\"probes\":{}}"
77
+ vj="$(node -e 'const v=JSON.parse(process.argv[1]);v.version=process.argv[2]||null;v.fpSha=process.argv[3]||null;process.stdout.write(JSON.stringify(v))' "$vj" "$cur_ver" "$FP_SHA")"
78
+ fi
79
+ results="$(node -e 'const a=JSON.parse(process.argv[1]);a.push(JSON.parse(process.argv[2]));process.stdout.write(JSON.stringify(a))' "$results" "$vj")"
80
+ done
81
+
82
+ report="$(node "$HERE/report.js" "$results" "$STATE" "$MODEL")"
83
+ echo "════ report ════" >&2; printf '%s\n' "$report" >&2; echo "════════════════" >&2
84
+ printf '%s\n' "$report" # also to stdout for the workflow log / artifact
85
+
86
+ if [ -n "${CANARY_SLACK_WEBHOOK:-}" ]; then
87
+ payload="$(node -e 'const t=require("fs").readFileSync(0,"utf8");process.stdout.write(JSON.stringify({text:t}))' <<<"$report")"
88
+ code="$(curl -sS --connect-timeout 10 --max-time 30 \
89
+ -o /dev/null -w '%{http_code}' -X POST -H 'Content-type: application/json' \
90
+ --data "$payload" "$CANARY_SLACK_WEBHOOK" 2>/dev/null || echo 000)"
91
+ if [ "$code" = 200 ]; then echo "✓ posted to Slack webhook" >&2
92
+ else echo "⚠️ Slack webhook POST returned HTTP $code" >&2; fi
93
+ else
94
+ echo "(no CANARY_SLACK_WEBHOOK set — report not posted)" >&2
95
+ fi
96
+
97
+ # Fail the job when any probe reported a hard FAIL (broken enforcement) so this
98
+ # scheduled check actually blocks regressions. ERROR (vendor quota/auth) and
99
+ # INCONCLUSIVE (model didn't attempt the tool) are NOT failures — they mean
100
+ # "couldn't confirm", not "enforcement broke". The report is already emitted +
101
+ # posted above regardless, so the signal is never lost.
102
+ if node -e 'const r=JSON.parse(process.argv[1]);process.exit(r.some(x=>Object.values(x.probes||{}).includes("FAIL"))?1:0)' "$results"; then
103
+ exit 0
104
+ else
105
+ echo "✗ FAIL verdict(s) present — failing the job" >&2
106
+ exit 1
107
+ fi
@@ -109,19 +109,13 @@ export const Navbar: React.FC<{
109
109
  {/* Spacer pushes version/section + actions to the right */}
110
110
  <div style={{ flex: 1 }} />
111
111
 
112
- {/* Version + section label — swapped to right of nav */}
112
+ {/* Version + section label — swapped to right of nav. `.h-meta` never
113
+ wraps mid-token and is hidden entirely on narrow viewports (it
114
+ duplicates the active tab highlight). */}
113
115
  {(process.env.NEXT_PUBLIC_APP_VERSION || sectionLabel) && (
114
- <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
116
+ <div className="h-meta">
115
117
  {process.env.NEXT_PUBLIC_APP_VERSION && (
116
- <span
117
- style={{
118
- fontFamily: "var(--font-mono)",
119
- fontSize: 10,
120
- letterSpacing: "0.18em",
121
- textTransform: "uppercase",
122
- color: "var(--dim)",
123
- }}
124
- >
118
+ <span className="h-version">
125
119
  v{process.env.NEXT_PUBLIC_APP_VERSION}
126
120
  </span>
127
121
  )}
@@ -25,7 +25,7 @@ const options = [
25
25
  {
26
26
  label: "Join our Discord",
27
27
  icon: MessageCircle,
28
- href: "https://discord.gg/2zjBZP7yQJ",
28
+ href: "https://discord.befailproof.ai/",
29
29
  color: "#5865f2",
30
30
  bg: "rgba(88,101,242,0.08)",
31
31
  },
@@ -66,7 +66,7 @@ export const ReachDevelopers: React.FC = () => {
66
66
  className="relative z-50 flex items-center gap-1.5 text-muted-foreground hover:text-foreground"
67
67
  >
68
68
  <GitBranch className="h-4 w-4" />
69
- <span className="hidden sm:inline text-xs">Reach Us</span>
69
+ <span className="hidden sm:inline text-xs whitespace-nowrap">Reach Us</span>
70
70
  <ChevronDown
71
71
  className={`h-3 w-3 transition-transform ${open ? "rotate-180" : ""}`}
72
72
  />
@@ -5,10 +5,14 @@
5
5
  # harness we integrate with and this repo, and opens ONE auto-PR with the fix.
6
6
  #
7
7
  # Bundles:
8
- # - Node 20 + npm
9
- # - bun (REQUIRED: the cloned repo's .claude/settings.json fires hooks via
10
- # `bun $CLAUDE_PROJECT_DIR/bin/failproofai.mjs --hook <Event>`, so the
11
- # dogfooded require-*-before-stop policies enforce the commit/push/PR)
8
+ # - Node 20 + npm (npm is a fallback bun installer for scripts/dev-hook.mjs;
9
+ # node runs the launcher itself)
10
+ # - bun (STILL REQUIRED: the cloned repo's .claude/settings.json fires hooks via
11
+ # `node "$CLAUDE_PROJECT_DIR/scripts/dev-hook.mjs" --hook <Event>`, and that
12
+ # launcher only *locates* bun — it cannot conjure one. Without this layer the
13
+ # dogfooded require-*-before-stop policies never fire and the auto-PR is
14
+ # never committed/pushed. We install to BUN_INSTALL=/home/appuser/.bun, which
15
+ # the launcher probes explicitly, so enforcement survives a broken PATH.)
12
16
  # - Claude Code CLI (`claude`) — run headless with --effort ultracode
13
17
  # - failproofai@latest on PATH (belt-and-suspenders; the dogfood hooks actually
14
18
  # run the clone's SOURCE via bun, not this global)