failproofai 0.0.14 → 0.0.15-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 (270) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +6 -6
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +2 -2
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  8. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  14. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  20. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  26. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/invite/route.js +1 -1
  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 +1 -1
  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 +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/status/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  45. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  47. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/index.html +1 -1
  52. package/.next/standalone/.next/server/app/index.rsc +15 -15
  53. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  54. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  55. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  56. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  57. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +2 -2
  61. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  64. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +20 -8
  65. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  66. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  69. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -4
  71. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  76. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
  77. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  80. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  81. package/.next/standalone/.next/server/app/projects/page.js +3 -3
  82. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0hv08mx._.js → [root-of-the-server]__0-dh5jy._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0obaznk._.js → [root-of-the-server]__0h3lfmp._.js} +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xvsnvt._.js → [root-of-the-server]__0l4vuqm._.js} +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sb_5m8._.js +4 -4
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xuaoik._.js +6 -4
  94. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1hkdkmz._.js → [root-of-the-server]__10oa-v9._.js} +1 -1
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__16le-kd._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1i9rknc._.js → [root-of-the-server]__18d-9oi._.js} +1 -1
  97. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +3 -0
  98. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1is3glr._.js +3 -0
  100. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +6 -6
  102. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0hdsupo._.js → [root-of-the-server]__0114ewg._.js} +3 -3
  106. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01od0m6._.js +3 -0
  107. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +4 -0
  108. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +4 -0
  109. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +4 -0
  110. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lpvwqm._.js +21 -0
  111. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ynf7tx._.js → [root-of-the-server]__0qucxyj._.js} +3 -3
  112. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +4 -0
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +3 -0
  114. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1s2b6h0._.js → [root-of-the-server]__0x3anhr._.js} +2 -2
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0y4wf1u._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +10 -10
  117. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00cz9as._.js → [root-of-the-server]__1apb7k_._.js} +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +4 -0
  119. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1py795m._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/_077y5np._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/_1-3unkg._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  128. package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  130. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  131. package/.next/standalone/.next/server/chunks/ssr/lib_0xkhw_v._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/lib_hermes-projects_ts_05b8-it._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/lib_openclaw-projects_ts_0qxq3bp._.js +1 -1
  134. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0gkzjaa.js → node_modules_html-to-image_es_index_1wnt2ya.js} +1 -1
  135. package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
  136. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  137. package/.next/standalone/.next/server/pages/404.html +1 -1
  138. package/.next/standalone/.next/server/pages/500.html +1 -1
  139. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/server-reference-manifest.json +22 -10
  141. package/.next/standalone/.next/static/chunks/{29o4j32z643_a.js → 05h7pau8d3rpj.js} +1 -1
  142. package/.next/standalone/.next/static/chunks/0oos4ntyvkmjl.js +1 -0
  143. package/.next/standalone/.next/static/chunks/0tuwf_7uhfn2y.js +6 -0
  144. package/.next/standalone/.next/static/chunks/1hdhteui932bq.js +1 -0
  145. package/.next/standalone/.next/static/chunks/1l6q2-8m0gk19.js +1 -0
  146. package/.next/standalone/.next/static/chunks/2geqsiz0n8e3w.js +1 -0
  147. package/.next/standalone/.next/static/chunks/{3-ogr79xqbw0h.js → 2mazwb1qmur1p.js} +1 -1
  148. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +2 -0
  149. package/.next/standalone/.next/static/chunks/{1ua5armwfph8o.js → 31fmsp1soxi07.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/{3s8aaig91937v.js → 37-so-zy208k7.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/3a_o5v_sl858j.js +1 -0
  152. package/.next/standalone/.next/static/chunks/{02jcotghcxrbu.js → 3kmwfieo15mr2.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/{2uh_4k4m296z5.js → 3p66lo2y-ezfr.js} +1 -1
  154. package/.next/standalone/.next/static/chunks/{06z_ex1ts3n4s.js → 412ajx6jkb5_i.js} +1 -1
  155. package/.next/standalone/.next/static/chunks/{turbopack-10hj8t5pf33tu.js → turbopack-1lsv92x7klwrn.js} +1 -1
  156. package/.next/standalone/app/actions/get-hooks-config.ts +125 -8
  157. package/.next/standalone/app/actions/update-hooks-config.ts +38 -1
  158. package/.next/standalone/app/components/project-list.tsx +229 -42
  159. package/.next/standalone/app/policies/hooks-client.tsx +149 -27
  160. package/.next/standalone/app/project/[name]/page.tsx +12 -2
  161. package/.next/standalone/integration-suite/README.md +43 -1
  162. package/.next/standalone/integration-suite/ci-entrypoint.sh +21 -6
  163. package/.next/standalone/integration-suite/install-clis.sh +58 -13
  164. package/.next/standalone/integration-suite/probe-cli.sh +51 -11
  165. package/.next/standalone/integration-suite/report.js +52 -6
  166. package/.next/standalone/integration-suite/run.sh +55 -3
  167. package/.next/standalone/lib/hermes-profiles.ts +156 -0
  168. package/.next/standalone/lib/hermes-projects.ts +91 -25
  169. package/.next/standalone/lib/hermes-sessions.ts +65 -10
  170. package/.next/standalone/lib/openclaw-projects.ts +111 -18
  171. package/.next/standalone/lib/openclaw-sessions.ts +20 -0
  172. package/.next/standalone/lib/paths.ts +36 -0
  173. package/.next/standalone/lib/project-tree.ts +193 -0
  174. package/.next/standalone/lib/projects.ts +22 -0
  175. package/.next/standalone/node_modules/@next/env/package.json +1 -1
  176. package/.next/standalone/node_modules/next/dist/build/next-config-ts/transpile-config.js +3 -114
  177. package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
  178. package/.next/standalone/node_modules/next/dist/cli/next-test.js +2 -1
  179. package/.next/standalone/node_modules/next/dist/compiled/http-proxy/index.js +5 -5
  180. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +11 -10
  181. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +11 -10
  182. package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +2 -2
  183. package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +9 -9
  184. package/.next/standalone/node_modules/next/dist/lib/install-dependencies.js +1 -1
  185. package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
  186. package/.next/standalone/node_modules/next/dist/lib/typescript/loadTsConfig.js +181 -0
  187. package/.next/standalone/node_modules/next/dist/lib/typescript/missingDependencyError.js +1 -1
  188. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeCheckCli.js +53 -0
  189. package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +256 -0
  190. package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +97 -56
  191. package/.next/standalone/node_modules/next/dist/server/app-render/action-handler.js +129 -34
  192. package/.next/standalone/node_modules/next/dist/server/app-render/manifests-singleton.js +52 -4
  193. package/.next/standalone/node_modules/next/dist/server/config-schema.js +1 -1
  194. package/.next/standalone/node_modules/next/dist/server/config-shared.js +1 -2
  195. package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
  196. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
  197. package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
  198. package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +8 -27
  199. package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
  200. package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/index.js +90 -53
  201. package/.next/standalone/node_modules/next/dist/server/lib/patch-fetch.js +15 -2
  202. package/.next/standalone/node_modules/next/dist/server/lib/router-utils/proxy-request.js +4 -0
  203. package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
  204. package/.next/standalone/node_modules/next/dist/server/web/spec-extension/unstable-cache.js +1 -1
  205. package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  206. package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/prepare-destination.js +2 -1
  207. package/.next/standalone/node_modules/next/dist/shared/lib/server-reference-info.js +12 -0
  208. package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
  209. package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
  210. package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
  211. package/.next/standalone/node_modules/next/package.json +10 -10
  212. package/.next/standalone/package.json +7 -6
  213. package/.next/standalone/pi-extension/index.ts +38 -5
  214. package/.next/standalone/server.js +1 -1
  215. package/bin/failproofai.mjs +14 -13
  216. package/dist/cli.mjs +789 -399
  217. package/lib/hermes-profiles.ts +156 -0
  218. package/lib/hermes-projects.ts +91 -25
  219. package/lib/hermes-sessions.ts +65 -10
  220. package/lib/openclaw-projects.ts +111 -18
  221. package/lib/openclaw-sessions.ts +20 -0
  222. package/lib/paths.ts +36 -0
  223. package/lib/project-tree.ts +193 -0
  224. package/lib/projects.ts +22 -0
  225. package/package.json +7 -6
  226. package/pi-extension/index.ts +38 -5
  227. package/scripts/launch.ts +7 -0
  228. package/src/audit/cli-adapters/hermes.ts +6 -3
  229. package/src/audit/cli-adapters/openclaw.ts +17 -13
  230. package/src/hooks/configure-wizard.ts +24 -2
  231. package/src/hooks/custom-hooks-loader.ts +90 -13
  232. package/src/hooks/enforcement-capability.ts +266 -0
  233. package/src/hooks/handler.ts +20 -3
  234. package/src/hooks/hook-activity-store.ts +14 -0
  235. package/src/hooks/hooks-config.ts +90 -6
  236. package/src/hooks/integrations.ts +123 -19
  237. package/src/hooks/manager.ts +206 -75
  238. package/src/hooks/policy-evaluator.ts +59 -7
  239. package/src/hooks/policy-types.ts +31 -0
  240. package/src/hooks/types.ts +50 -2
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q2nbsl._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d9qen3._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1mrihkj._.js +0 -3
  244. package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__015_i4t._.js +0 -4
  246. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__030k0c6._.js +0 -3
  247. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__03lv-pe._.js +0 -4
  248. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__042d3cd._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09q1168._.js +0 -3
  250. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qeuy9c._.js +0 -4
  251. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s89hoe._.js +0 -4
  252. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0zf8xlq._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17ai7sy._.js +0 -4
  254. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1d8omgc._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1fax1sl._.js +0 -19
  256. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tkjqka._.js +0 -4
  257. package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/ssr/_0_9_ytf._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/_1p0-leb._.js +0 -3
  261. package/.next/standalone/.next/static/chunks/0o0ut0ugd0_-s.js +0 -1
  262. package/.next/standalone/.next/static/chunks/0xl97x-874k-u.js +0 -1
  263. package/.next/standalone/.next/static/chunks/19d7hqx8udopz.js +0 -1
  264. package/.next/standalone/.next/static/chunks/36j05kb2x323i.css +0 -2
  265. package/.next/standalone/.next/static/chunks/370sj2lgkw5ey.js +0 -1
  266. package/.next/standalone/.next/static/chunks/3w7g0n0iwwnbs.js +0 -1
  267. package/.next/standalone/.next/static/chunks/42oeva1-w0z2d.js +0 -6
  268. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_buildManifest.js +0 -0
  269. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_clientMiddlewareManifest.js +0 -0
  270. /package/.next/standalone/.next/static/{WQRKgSfFekLcC_pvyzp4S → nhn6Tt0--xZKdgVGIqR6E}/_ssgManifest.js +0 -0
package/lib/paths.ts CHANGED
@@ -29,6 +29,42 @@ export function decodeFolderName(name: string): string {
29
29
  return name.replace(/-/g, "/");
30
30
  }
31
31
 
32
+ /**
33
+ * Synthetic project path for gateway CLIs with no working directory —
34
+ * `hermes:<profile>:<source>`, `openclaw:<channel>`. Two or more leading letters
35
+ * so a Windows drive letter (`c:/code`) can never match.
36
+ */
37
+ const SYNTHETIC_PROJECT_PATH = /^[a-z]{2,}:/;
38
+
39
+ /**
40
+ * Whether a project `path` is a synthetic gateway path rather than a real
41
+ * filesystem path — `hermes:<profile>:<source>`, `openclaw:<agent>:<channel>`.
42
+ *
43
+ * These are the only projects with a meaningful segment hierarchy, so this is
44
+ * also what decides which rows the projects panel renders as a folder tree.
45
+ */
46
+ export function isSyntheticProjectPath(path?: string): boolean {
47
+ return !!path && SYNTHETIC_PROJECT_PATH.test(path);
48
+ }
49
+
50
+ /** Segments of a synthetic gateway path, outermost first. `[]` for real paths. */
51
+ export function syntheticPathSegments(path?: string): string[] {
52
+ return isSyntheticProjectPath(path) ? path!.split(":") : [];
53
+ }
54
+
55
+ /**
56
+ * Label to display for a project card.
57
+ *
58
+ * Filesystem-backed projects decode their folder name. Gateway projects must
59
+ * NOT: `decodeFolderName` turns every `-` into `/`, which splits a Hermes
60
+ * profile named `my-bot` into `my/bot`. Their `path` already carries the
61
+ * segments explicitly, so use it.
62
+ */
63
+ export function projectDisplayName(name: string, path?: string): string {
64
+ if (isSyntheticProjectPath(path)) return path!.replace(/:/g, "/");
65
+ return decodeFolderName(name);
66
+ }
67
+
32
68
  /**
33
69
  * Encodes a filesystem path into a Claude-compatible project folder name.
34
70
  * Inverse of `decodeFolderName`.
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Folder tree for the projects panel.
3
+ *
4
+ * Gateway CLIs (Hermes, OpenClaw) are USER-scoped: their sessions have no
5
+ * working directory, so they can't sit in the cwd-based project model the other
6
+ * CLIs use. What they have instead is a hierarchy — which profile/agent, then
7
+ * which channel — and that hierarchy is already encoded in the synthetic
8
+ * `path` those producers emit (`hermes:work:slack`,
9
+ * `openclaw:weather-bot:telegram`).
10
+ *
11
+ * So this is a pure PRESENTATION transform over the flat `ProjectFolder[]` the
12
+ * server already returns: split those paths into nodes, leave every real
13
+ * filesystem project as a top-level leaf. Nothing about project `name`s
14
+ * changes, which is what keeps existing `/project/<name>` links, bookmarks and
15
+ * stored audit history resolving.
16
+ *
17
+ * Pure and dependency-free on purpose — `import type` only, so the client
18
+ * bundle never pulls in `lib/projects.ts`'s node:fs imports.
19
+ */
20
+ import type { ProjectCli, ProjectFolder } from "./projects";
21
+ import { syntheticPathSegments } from "./paths";
22
+
23
+ export interface ProjectTreeNode {
24
+ /** Colon-joined segment path (`hermes:work`) — stable across renders, used as
25
+ * the React key and as the collapsed-state key. */
26
+ key: string;
27
+ /** The single segment this row shows (`work`), not the whole path. */
28
+ label: string;
29
+ /** 0 for top-level rows; each level below adds 1 (drives indentation). */
30
+ depth: number;
31
+ children: ProjectTreeNode[];
32
+ /** The real project, on rows that map to one. Leaves always have it; a folder
33
+ * row has it only if a project exists at exactly that path. Rows without one
34
+ * are pure grouping and must not link anywhere. */
35
+ project?: ProjectFolder;
36
+ /** Sessions beneath this row, summed. `undefined` when nothing underneath
37
+ * reported a count — rendered as nothing rather than as `0`, since the two
38
+ * mean very different things. */
39
+ sessionCount?: number;
40
+ /** How many real projects this row covers (1 for a leaf). Lets the caller
41
+ * paginate over top-level rows while still reporting a project range. */
42
+ projectCount: number;
43
+ /** Newest activity anywhere beneath this row. */
44
+ lastModified: Date;
45
+ lastModifiedFormatted?: string;
46
+ /** Union of the CLIs beneath this row, in first-seen order. */
47
+ cli: ProjectCli[];
48
+ }
49
+
50
+ interface Builder {
51
+ key: string;
52
+ label: string;
53
+ depth: number;
54
+ children: Map<string, Builder>;
55
+ project?: ProjectFolder;
56
+ sessionCount?: number;
57
+ projectCount: number;
58
+ lastModified: Date;
59
+ lastModifiedFormatted?: string;
60
+ cli: string[];
61
+ }
62
+
63
+ function makeBuilder(key: string, label: string, depth: number): Builder {
64
+ return {
65
+ key,
66
+ label,
67
+ depth,
68
+ children: new Map(),
69
+ projectCount: 0,
70
+ lastModified: new Date(0),
71
+ cli: [],
72
+ };
73
+ }
74
+
75
+ /** Fold a project's stats into a node — newest date wins, counts add, CLIs union. */
76
+ function absorb(node: Builder, folder: ProjectFolder): void {
77
+ if (folder.lastModified.getTime() > node.lastModified.getTime()) {
78
+ node.lastModified = folder.lastModified;
79
+ node.lastModifiedFormatted = folder.lastModifiedFormatted;
80
+ }
81
+ if (typeof folder.sessionCount === "number") {
82
+ node.sessionCount = (node.sessionCount ?? 0) + folder.sessionCount;
83
+ }
84
+ node.projectCount += 1;
85
+ for (const c of folder.cli) if (!node.cli.includes(c)) node.cli.push(c);
86
+ }
87
+
88
+ function finalize(node: Builder): ProjectTreeNode {
89
+ const children = [...node.children.values()]
90
+ .map(finalize)
91
+ .sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
92
+ return {
93
+ key: node.key,
94
+ label: node.label,
95
+ depth: node.depth,
96
+ children,
97
+ project: node.project,
98
+ sessionCount: node.sessionCount,
99
+ projectCount: node.projectCount,
100
+ lastModified: node.lastModified,
101
+ lastModifiedFormatted: node.lastModifiedFormatted,
102
+ cli: node.cli as ProjectCli[],
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Build the render tree for a list of projects (already filtered, sorted and
108
+ * paginated by the caller — this adds no policy of its own).
109
+ *
110
+ * Real filesystem projects become depth-0 leaves in input order-by-recency,
111
+ * exactly as the flat table showed them. Gateway projects nest under one node
112
+ * per path segment.
113
+ */
114
+ export function buildProjectTree(folders: ProjectFolder[]): ProjectTreeNode[] {
115
+ const roots = new Map<string, Builder>();
116
+
117
+ for (const folder of folders) {
118
+ const segments = syntheticPathSegments(folder.path);
119
+
120
+ if (segments.length === 0) {
121
+ // Real filesystem project — a top-level leaf, keyed by name so two
122
+ // projects with an identical path (or none) can't collide.
123
+ const node = makeBuilder(`project:${folder.name}`, folder.name, 0);
124
+ node.project = folder;
125
+ absorb(node, folder);
126
+ roots.set(node.key, node);
127
+ continue;
128
+ }
129
+
130
+ let level = roots;
131
+ let key = "";
132
+ let node: Builder | undefined;
133
+ for (let depth = 0; depth < segments.length; depth++) {
134
+ const label = segments[depth];
135
+ key = depth === 0 ? label : `${key}:${label}`;
136
+ let next = level.get(key);
137
+ if (!next) {
138
+ next = makeBuilder(key, label, depth);
139
+ level.set(key, next);
140
+ }
141
+ // Every ancestor rolls up the project's stats, so a collapsed folder still
142
+ // reports the freshest activity and total sessions underneath it.
143
+ absorb(next, folder);
144
+ node = next;
145
+ level = next.children;
146
+ }
147
+ // The deepest segment IS this project. Guard against two projects claiming
148
+ // the same path (shouldn't happen post-merge, but silently losing one would
149
+ // be worse than keeping the first).
150
+ if (node && !node.project) node.project = folder;
151
+ }
152
+
153
+ return [...roots.values()]
154
+ .map(finalize)
155
+ .sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
156
+ }
157
+
158
+ /**
159
+ * Flatten the tree to the rows actually rendered, honouring collapsed state.
160
+ *
161
+ * We track COLLAPSED rather than expanded keys deliberately: a profile or agent
162
+ * that appears for the first time should be visible, not hidden behind a click
163
+ * the user doesn't know to make.
164
+ */
165
+ export function visibleTreeRows(
166
+ nodes: ProjectTreeNode[],
167
+ isCollapsed: (key: string) => boolean,
168
+ ): ProjectTreeNode[] {
169
+ const out: ProjectTreeNode[] = [];
170
+ const walk = (list: ProjectTreeNode[]) => {
171
+ for (const node of list) {
172
+ out.push(node);
173
+ if (node.children.length > 0 && !isCollapsed(node.key)) walk(node.children);
174
+ }
175
+ };
176
+ walk(nodes);
177
+ return out;
178
+ }
179
+
180
+ /** Keys of every node that has children — i.e. every collapsible row. */
181
+ export function collapsibleKeys(nodes: ProjectTreeNode[]): string[] {
182
+ const out: string[] = [];
183
+ const walk = (list: ProjectTreeNode[]) => {
184
+ for (const node of list) {
185
+ if (node.children.length > 0) {
186
+ out.push(node.key);
187
+ walk(node.children);
188
+ }
189
+ }
190
+ };
191
+ walk(nodes);
192
+ return out;
193
+ }
package/lib/projects.ts CHANGED
@@ -29,6 +29,20 @@ export interface ProjectFolder {
29
29
  * the same cwd has transcripts from more than one CLI; rendered as badges.
30
30
  */
31
31
  cli: ProjectCli[];
32
+ /**
33
+ * How many sessions this project holds.
34
+ *
35
+ * Only the GATEWAY producers (Hermes, OpenClaw) fill this in, because only
36
+ * they render as a folder tree where a collapsed row has to say how much is
37
+ * underneath it — and for them the count is free, since their grouping loop
38
+ * already visits every session. The cwd-based CLIs leave it undefined and the
39
+ * UI renders nothing, rather than a `0` that would read as "empty".
40
+ *
41
+ * Deliberately NOT filled for Claude: counting there needs an extra readdir
42
+ * per project folder, and a number that exists for some CLIs and not others
43
+ * would sum to a wrong-but-plausible total on a merged row.
44
+ */
45
+ sessionCount?: number;
32
46
  }
33
47
 
34
48
  export interface SessionFile {
@@ -126,6 +140,14 @@ function mergeProjectFolders(...sources: ProjectFolder[][]): ProjectFolder[] {
126
140
  cli: mergedCli,
127
141
  lastModified: newer.lastModified,
128
142
  lastModifiedFormatted: newer.lastModifiedFormatted,
143
+ // Spreading `existing` alone would silently keep only the FIRST source's
144
+ // count for a name several CLIs contribute to, so sum explicitly. Stays
145
+ // undefined unless at least one side reported a count — `0` would claim
146
+ // "no sessions" for producers that simply don't count.
147
+ sessionCount:
148
+ existing.sessionCount === undefined && f.sessionCount === undefined
149
+ ? undefined
150
+ : (existing.sessionCount ?? 0) + (f.sessionCount ?? 0),
129
151
  });
130
152
  }
131
153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "0.0.14",
3
+ "version": "0.0.15-beta.1",
4
4
  "description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs"
@@ -71,7 +71,7 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@anthropic-ai/sdk": "^0.112.4",
74
+ "@anthropic-ai/sdk": "^0.115.0",
75
75
  "@mdx-js/mdx": "^3.1.1",
76
76
  "@tailwindcss/postcss": "^4.3.1",
77
77
  "@tanstack/react-virtual": "^3.14.3",
@@ -85,9 +85,9 @@
85
85
  "clsx": "^2.1.1",
86
86
  "eslint": "^10.5.0",
87
87
  "eslint-config-next": "^16.2.9",
88
- "jsdom": "^29.1.1",
88
+ "jsdom": "^30.0.0",
89
89
  "lucide-react": "^1.18.0",
90
- "next": "^16.2.9",
90
+ "next": "^16.2.11",
91
91
  "react": "^19.2.4",
92
92
  "react-dom": "^19.2.4",
93
93
  "tailwind-merge": "^3.4.0",
@@ -103,10 +103,11 @@
103
103
  "yaml": "^2.9.0"
104
104
  },
105
105
  "overrides": {
106
- "postcss": "8.5.14",
106
+ "postcss": "8.5.23",
107
107
  "eslint-plugin-react-hooks": "7.0.1",
108
108
  "vite": "8.0.16",
109
109
  "undici": "7.28.0",
110
- "brace-expansion": "5.0.7"
110
+ "brace-expansion": "5.0.8",
111
+ "sharp": "0.35.0"
111
112
  }
112
113
  }
@@ -347,6 +347,10 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
347
347
  cwd: resolveCwd(e.cwd),
348
348
  hook_event_name: "PreToolUse",
349
349
  });
350
+ // `ToolCallEventResult` is `{block?, reason?}` (extensions/types.d.ts:766).
351
+ // agent-loop.ts branches on `beforeResult?.block`, so this is the shape that
352
+ // stops the tool. Do NOT put the user_bash full-replacement shape here — it
353
+ // has no `block` field, so the call would sail straight through.
350
354
  if (decision.block) return { block: true, reason: decision.reason };
351
355
  return undefined;
352
356
  });
@@ -361,12 +365,38 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
361
365
  cwd: resolveCwd(e.cwd),
362
366
  hook_event_name: "PreToolUse",
363
367
  });
364
- if (decision.block) return { block: true, reason: decision.reason };
368
+ // `UserBashEventResult` is `{operations?, result?: BashResult}` — there is
369
+ // no `block` field, so the `{block:true}` we used to return matched nothing
370
+ // and the command ran anyway. The supported way to stop it is a FULL
371
+ // REPLACEMENT result: the extension declares it handled execution, so Pi
372
+ // uses this instead of running the command. Verified against the installed
373
+ // pi-coding-agent d.ts (extensions/types.d.ts:772, core/bash-executor.d.ts:15).
374
+ if (decision.block) {
375
+ return {
376
+ result: {
377
+ output: decision.reason ?? "Blocked by failproofai",
378
+ exitCode: 1,
379
+ cancelled: false,
380
+ truncated: false,
381
+ },
382
+ };
383
+ }
365
384
  return undefined;
366
385
  });
367
386
 
368
- // input → UserPromptSubmit. Honor block decisions if Pi accepts them
369
- // (Pi's docs describe block on input but it's not exhaustively tested).
387
+ // input → UserPromptSubmit.
388
+ //
389
+ // `InputEventResult` is `{action:"continue"|"transform"|"handled"}`
390
+ // (extensions/types.d.ts:629) — there is NO `block` field, so the
391
+ // `{block:true}` we used to return matched neither branch and every prompt
392
+ // was submitted regardless of the verdict. `{action:"handled"}` is the only
393
+ // stop: it tells Pi an extension consumed the input, so nothing is sent to
394
+ // the model.
395
+ //
396
+ // Known UX limitation: `handled` drops the prompt silently — Pi shows the
397
+ // user nothing. We log the reason to stderr so it is at least recoverable
398
+ // from the session output. Pi has no first-class channel for explaining a
399
+ // refusal at this event; if one appears, prefer it over the silent drop.
370
400
  pi.on("input", (event: unknown): unknown => {
371
401
  const e = event as PiInputEvent;
372
402
  const decision = callPolicy("input", {
@@ -375,8 +405,11 @@ export default function failproofaiBridge(pi: PiExtensionApi) {
375
405
  cwd: resolveCwd(e.cwd),
376
406
  hook_event_name: "UserPromptSubmit",
377
407
  });
378
- if (decision.block) return { block: true, reason: decision.reason };
379
- return undefined;
408
+ if (decision.block) {
409
+ console.error(`[failproofai] prompt blocked: ${decision.reason ?? "blocked by policy"}`);
410
+ return { action: "handled" };
411
+ }
412
+ return { action: "continue" };
380
413
  });
381
414
 
382
415
  // session_start → SessionStart. Observe-only; we still forward so the
package/scripts/launch.ts CHANGED
@@ -89,6 +89,13 @@ export function launch(mode: "dev" | "start"): void {
89
89
  stdio: filterLogs ? ["inherit", "pipe", "pipe"] : "inherit",
90
90
  env: {
91
91
  ...process.env,
92
+ // `.next/standalone/server.js` does `process.chdir(__dirname)` on its
93
+ // first line, so by the time a server action runs, `process.cwd()` is the
94
+ // PACKAGE directory, not the directory the user launched from. Anything
95
+ // resolving project-scope paths from cwd would look inside the installed
96
+ // package and find nothing. Capture the real cwd here, before the child
97
+ // starts, the same way FAILPROOFAI_PACKAGE_ROOT is threaded through.
98
+ FAILPROOFAI_LAUNCH_CWD: process.env.FAILPROOFAI_LAUNCH_CWD ?? process.cwd(),
92
99
  ...(filterLogs ? { FORCE_COLOR: process.env.FORCE_COLOR ?? "1" } : {}),
93
100
  ...(loggingLevel ? { FAILPROOFAI_LOG_LEVEL: loggingLevel } : {}),
94
101
  ...(disableTelemetry ? { FAILPROOFAI_TELEMETRY_DISABLED: "1" } : {}),
@@ -8,9 +8,11 @@
8
8
  * `logEntriesToEvents` handles the rest.
9
9
  *
10
10
  * Gateway sessions have no `cwd` (Slack/Telegram runs aren't in a repo), so they
11
- * group by `source` (slack/telegram/cli/cron) instead of working directory.
11
+ * group by (profile, `source`) instead of working directory — a Hermes profile
12
+ * is a whole separate home dir with its own state.db (lib/hermes-profiles.ts).
12
13
  */
13
14
  import { getHermesSessions } from "../../../lib/hermes-projects";
15
+ import { hermesProjectPath } from "../../../lib/hermes-profiles";
14
16
  import { getHermesSessionLog } from "../../../lib/hermes-sessions";
15
17
  import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
16
18
  import type { ListOpts } from "./claude";
@@ -31,8 +33,9 @@ export async function listHermesTranscriptMetadata(
31
33
  if (s.messageCount <= 0 && !s.hasMessages) continue; // empty → no events (message_count can lag; trust real messages)
32
34
  out.push({
33
35
  cli: "hermes",
34
- // Group by channel; gateway sessions are cwd-less.
35
- projectName: s.source ? `hermes:${s.source}` : "hermes",
36
+ // Group by (profile, channel); gateway sessions are cwd-less, and each
37
+ // profile is a separate Hermes home with its own state.db.
38
+ projectName: s.source ? hermesProjectPath(s.profile, s.source) : `hermes:${s.profile}`,
36
39
  sessionId: s.sessionId,
37
40
  transcriptPath: `hermes://${s.sessionId}`,
38
41
  mtimeMs: s.mtimeMs,
@@ -7,13 +7,13 @@
7
7
  * shared LogEntry[] shape, so `logEntriesToEvents` handles the rest.
8
8
  *
9
9
  * Gateway sessions run in the container workspace, not a host repo, so they
10
- * group by agentId (`openclaw:<agentId>`) and contribute nothing to a
11
- * cwd-scoped audit.
10
+ * group by (agentId, channel) — `openclaw:<agentId>:<channel>` — and contribute
11
+ * nothing to a cwd-scoped audit. That is the SAME key the dashboard groups by;
12
+ * the two used to disagree (adapter by agent, panel by channel), so a report and
13
+ * the projects panel sliced identical data on different axes.
12
14
  */
13
- import {
14
- listOpenClawTranscripts,
15
- getOpenClawSessionLog,
16
- } from "../../../lib/openclaw-sessions";
15
+ import { getOpenClawSessions, openClawProjectPath } from "../../../lib/openclaw-projects";
16
+ import { getOpenClawSessionLog } from "../../../lib/openclaw-sessions";
17
17
  import type { NormalizedToolEvent, TranscriptMetadata } from "../types";
18
18
  import type { ListOpts } from "./claude";
19
19
  import { logEntriesToEvents } from "./shared";
@@ -28,15 +28,19 @@ export async function listOpenClawTranscriptMetadata(
28
28
 
29
29
  const sinceMs = opts.sinceMs ?? 0;
30
30
  const out: TranscriptMetadata[] = [];
31
- for (const t of listOpenClawTranscripts()) {
32
- if (t.mtimeMs < sinceMs) continue;
31
+ // getOpenClawSessions layers the per-agent sessions.json index over the same
32
+ // transcript walk, which is what carries `channel`. It reads one small index
33
+ // file per AGENT (not per session) and is memoized, so grouping by channel
34
+ // costs no meaningful extra IO on the audit path.
35
+ for (const s of await getOpenClawSessions()) {
36
+ if (s.mtimeMs < sinceMs) continue;
33
37
  out.push({
34
38
  cli: "openclaw",
35
- projectName: `openclaw:${t.agentId}`,
36
- sessionId: t.sessionId,
37
- transcriptPath: t.transcriptPath,
38
- mtimeMs: t.mtimeMs,
39
- sizeBytes: t.sizeBytes,
39
+ projectName: openClawProjectPath(s.agentId, s.channel),
40
+ sessionId: s.sessionId,
41
+ transcriptPath: s.transcriptPath,
42
+ mtimeMs: s.mtimeMs,
43
+ sizeBytes: s.sizeBytes,
40
44
  });
41
45
  }
42
46
  return out;
@@ -29,6 +29,8 @@ import {
29
29
  import {
30
30
  detectInstalledClis,
31
31
  getIntegration,
32
+ settingsPathsFor,
33
+ unhookedHermesProfiles,
32
34
  } from "./integrations";
33
35
  import { INTEGRATION_TYPES, type IntegrationType, type HookScope } from "./types";
34
36
  import { installHooks } from "./manager";
@@ -126,12 +128,28 @@ export function buildAgentChoices(scope: HookScope, cwd: string): MultiChoice<In
126
128
  } catch {
127
129
  installedHere = false;
128
130
  }
131
+ // Hermes reports installed only when EVERY profile is hooked, so a profile
132
+ // added after install flips it to false. Say which ones, otherwise a mostly
133
+ // configured gateway just reads as "not configured".
134
+ let partialHint: string | undefined;
135
+ if (id === "hermes" && !installedHere) {
136
+ try {
137
+ const unhooked = unhookedHermesProfiles();
138
+ if (unhooked.length > 0) {
139
+ partialHint = `${unhooked.length} unhooked profile(s): ${unhooked.join(", ")}`;
140
+ }
141
+ } catch {
142
+ // Profile discovery is best-effort — never block the wizard.
143
+ }
144
+ }
129
145
  return {
130
146
  label: integration.displayName,
131
147
  value: id,
132
148
  checked: isDetected || installedHere,
133
149
  section: isDetected ? "Detected" : "Not installed · set up ahead of time",
134
- hint: installedHere ? "already configured" : isDetected ? undefined : "not on PATH",
150
+ hint: installedHere
151
+ ? "already configured"
152
+ : (partialHint ?? (isDetected ? undefined : "not on PATH")),
135
153
  };
136
154
  });
137
155
  }
@@ -322,7 +340,11 @@ export function reviewLines(state: {
322
340
  lines.push(" This will update:");
323
341
  for (const cli of clis) {
324
342
  const integration = getIntegration(cli);
325
- lines.push(` ${homeify(integration.getSettingsPath(scope, cwd))} ${integration.displayName} hooks`);
343
+ // Usually one path; Hermes lists one per profile so the operator sees every
344
+ // home dir that is about to be written.
345
+ for (const p of settingsPathsFor(integration, scope, cwd)) {
346
+ lines.push(` ${homeify(p)} ${integration.displayName} hooks`);
347
+ }
326
348
  }
327
349
  lines.push(` ${homeify(getConfigPathForScope(scope, cwd))} ${policies.length} policies`);
328
350
  return lines;