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,182 @@
1
+ /**
2
+ * OpenClaw (openclaw gateway) session enumeration — AUDIT-ONLY.
3
+ *
4
+ * Surfaces the on-disk transcripts (agents/<agentId>/sessions/<uuid>.jsonl) as
5
+ * synthetic dashboard "projects" grouped by agentId. The per-agent
6
+ * `sessions.json` index maps sessionKey → {sessionId, timestamps}; we read it to
7
+ * recover the sessionKey (which encodes the channel for gateway sessions) and a
8
+ * reliable last-activity time. Verified live against openclaw v2026.7.1.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { runtimeCache } from "./runtime-cache";
13
+ import { listOpenClawTranscripts, openclawHome } from "./openclaw-sessions";
14
+ import type { ProjectFolder, SessionFile } from "./projects";
15
+ import { formatDate } from "./format-date";
16
+
17
+ export interface OpenClawSessionRef {
18
+ sessionId: string;
19
+ agentId: string;
20
+ /** Channel/source the session last ran in (from sessions.json metadata) —
21
+ * e.g. "telegram", "slack", or "local" for a CLI session. Drives grouping. */
22
+ channel: string;
23
+ /** Human-readable label from `origin.label` (e.g. "Chetan (@chhhee10) id:…"). */
24
+ label?: string;
25
+ /** Chat id (e.g. "telegram:8674922496") + type ("direct"/"group") for the
26
+ * gateway-metadata columns. */
27
+ chatId?: string;
28
+ chatType?: string;
29
+ mtimeMs: number;
30
+ sizeBytes: number;
31
+ }
32
+
33
+ interface SessionIndexMeta {
34
+ lastMs?: number;
35
+ channel?: string;
36
+ chatType?: string;
37
+ label?: string;
38
+ chatId?: string;
39
+ }
40
+
41
+ function str(v: unknown): string | undefined {
42
+ return typeof v === "string" && v.length > 0 ? v : undefined;
43
+ }
44
+
45
+ /** Read the per-agent sessions.json index → sessionId → routing metadata.
46
+ * OpenClaw routes gateway sessions through the agent's default key
47
+ * (`agent:<id>:main`) and records the channel in metadata fields
48
+ * (`lastChannel`, `chatType`, `origin.{label,provider,from}`, `lastTo`) rather
49
+ * than in the key — verified live against v2026.7.1. */
50
+ function readSessionsIndex(agentId: string): Map<string, SessionIndexMeta> {
51
+ const out = new Map<string, SessionIndexMeta>();
52
+ const indexPath = join(openclawHome(), "agents", agentId, "sessions", "sessions.json");
53
+ let raw: unknown;
54
+ try {
55
+ raw = JSON.parse(readFileSync(indexPath, "utf-8"));
56
+ } catch {
57
+ return out;
58
+ }
59
+ if (!raw || typeof raw !== "object") return out;
60
+ for (const v of Object.values(raw as Record<string, unknown>)) {
61
+ if (!v || typeof v !== "object") continue;
62
+ const e = v as Record<string, unknown>;
63
+ const sessionId = str(e.sessionId);
64
+ if (!sessionId) continue;
65
+ const origin = (e.origin && typeof e.origin === "object" ? e.origin : {}) as Record<string, unknown>;
66
+ const lastMs =
67
+ typeof e.lastInteractionAt === "number"
68
+ ? e.lastInteractionAt
69
+ : typeof e.updatedAt === "number"
70
+ ? (e.updatedAt as number)
71
+ : undefined;
72
+ out.set(sessionId, {
73
+ lastMs,
74
+ channel: str(e.lastChannel) ?? str(origin.provider) ?? str(origin.surface),
75
+ chatType: str(e.chatType) ?? str(origin.chatType),
76
+ label: str(origin.label),
77
+ chatId: str(e.lastTo) ?? str(origin.from),
78
+ });
79
+ }
80
+ return out;
81
+ }
82
+
83
+ /** List every OpenClaw session across all agents. Fail-open ([] on any error). */
84
+ export async function getOpenClawSessions(): Promise<OpenClawSessionRef[]> {
85
+ const transcripts = listOpenClawTranscripts();
86
+ const indexByAgent = new Map<string, Map<string, SessionIndexMeta>>();
87
+ const refs: OpenClawSessionRef[] = [];
88
+ for (const t of transcripts) {
89
+ let idx = indexByAgent.get(t.agentId);
90
+ if (!idx) {
91
+ idx = readSessionsIndex(t.agentId);
92
+ indexByAgent.set(t.agentId, idx);
93
+ }
94
+ const meta = idx.get(t.sessionId);
95
+ refs.push({
96
+ sessionId: t.sessionId,
97
+ agentId: t.agentId,
98
+ // Gateway sessions group by channel; CLI/local runs have none.
99
+ channel: meta?.channel ?? "local",
100
+ label: meta?.label,
101
+ chatType: meta?.chatType,
102
+ chatId: meta?.chatId,
103
+ mtimeMs: meta?.lastMs ?? t.mtimeMs,
104
+ sizeBytes: t.sizeBytes,
105
+ });
106
+ }
107
+ refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
108
+ return refs;
109
+ }
110
+
111
+ export const getCachedOpenClawSessions = runtimeCache(getOpenClawSessions, 2);
112
+
113
+ // ── Dashboard history browser (projects list + project-detail sessions) ──
114
+
115
+ /**
116
+ * Surface OpenClaw sessions as synthetic "projects" grouped by **channel**
117
+ * (telegram/slack/…/local) — gateway sessions run per channel, not in a host
118
+ * repo. Mirrors Hermes's group-by-source. One ProjectFolder per channel; its
119
+ * `name` is `openclaw-<channel>`, reversed in `getOpenClawSessionsByEncodedName`.
120
+ */
121
+ export async function getOpenClawProjects(): Promise<ProjectFolder[]> {
122
+ const sessions = await getOpenClawSessions();
123
+ const latestByChannel = new Map<string, number>();
124
+ for (const s of sessions) {
125
+ latestByChannel.set(s.channel, Math.max(latestByChannel.get(s.channel) ?? 0, s.mtimeMs));
126
+ }
127
+ const out: ProjectFolder[] = [];
128
+ for (const [channel, latest] of latestByChannel) {
129
+ const lastModified = new Date(latest);
130
+ out.push({
131
+ name: `openclaw-${channel}`,
132
+ path: `openclaw:${channel}`,
133
+ isDirectory: true,
134
+ lastModified,
135
+ lastModifiedFormatted: formatDate(lastModified),
136
+ cli: ["openclaw"],
137
+ });
138
+ }
139
+ out.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
140
+ return out;
141
+ }
142
+
143
+ export interface OpenClawProjectByName {
144
+ cwd: string | null;
145
+ sessions: SessionFile[];
146
+ }
147
+
148
+ /** Resolve the OpenClaw sessions for a synthetic project name
149
+ * (`openclaw-<channel>`), for the project-detail page. Session names use the
150
+ * human-readable `origin.label` (e.g. "Chetan (@chhhee10) id:…") rather than
151
+ * the raw session key. */
152
+ export async function getOpenClawSessionsByEncodedName(
153
+ name: string,
154
+ ): Promise<OpenClawProjectByName> {
155
+ if (!name.startsWith("openclaw-")) return { cwd: null, sessions: [] };
156
+ const channel = name.slice("openclaw-".length);
157
+ const sessions = await getOpenClawSessions();
158
+ const matched = sessions.filter((s) => s.channel === channel);
159
+ return {
160
+ cwd: `openclaw:${channel}`,
161
+ sessions: matched.map((s) => {
162
+ const lastModified = new Date(s.mtimeMs);
163
+ return {
164
+ name: s.label ?? s.chatId ?? s.sessionId,
165
+ path: s.sessionId,
166
+ lastModified,
167
+ lastModifiedFormatted: formatDate(lastModified),
168
+ sessionId: s.sessionId,
169
+ cli: "openclaw" as const,
170
+ channelId: s.chatId,
171
+ channelType: s.chatType,
172
+ };
173
+ }),
174
+ };
175
+ }
176
+
177
+ export const getCachedOpenClawProjects = runtimeCache(getOpenClawProjects, 2);
178
+ export const getCachedOpenClawSessionsByEncodedName = runtimeCache(
179
+ (name: string) => getOpenClawSessionsByEncodedName(name),
180
+ 2,
181
+ { maxSize: 50 },
182
+ );
@@ -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
+ );
@@ -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
+ }