failproofai 1.0.0-beta.4 → 1.0.0-beta.6

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 (369) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  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 +3 -1
  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 +3 -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 +4 -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/audit/status/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  39. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  47. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  50. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/index.html +1 -1
  53. package/.next/standalone/.next/server/app/index.rsc +15 -15
  54. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  56. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  57. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  58. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +3 -1
  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/server-reference-manifest.json +10 -10
  64. package/.next/standalone/.next/server/app/policies/page.js +4 -2
  65. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  69. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  77. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  78. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  81. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  82. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  83. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  84. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  85. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  86. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  87. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  88. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  89. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  90. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
  97. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
  98. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
  102. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
  109. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
  110. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
  111. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  112. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_a_gwn._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e7-pa7._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
  125. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/_0igtyj5._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  133. package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
  135. package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
  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 +2 -2
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  141. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
  143. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/standalone/.next/server/middleware.js +2 -2
  145. package/.next/standalone/.next/server/pages/404.html +1 -1
  146. package/.next/standalone/.next/server/pages/500.html +1 -1
  147. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
  149. package/.next/standalone/.next/static/chunks/{2c90h1g6x8z8y.js → 0_2n13i94rnw5.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  151. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  152. package/.next/standalone/.next/static/chunks/23557z4e6dtsr.js +1 -0
  153. package/.next/standalone/.next/static/chunks/28zkwzu50puki.js +1 -0
  154. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  155. package/.next/standalone/.next/static/chunks/3081gykdubv8b.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{06jakt_xhoolk.js → 34fa65k0cldkj.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/35v07j9gc1pvr.js +6 -0
  158. package/.next/standalone/.next/static/chunks/{2zb3m1tzq6h98.js → 3f_939wi3kfnx.js} +22 -22
  159. package/.next/standalone/.next/static/chunks/{31cqiv_cpgj9m.js → 3p1vpm2_-tfwm.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/{0ue9ak_xexiqh.js → 3rbpw_waieafm.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/40ffnfpkeyvb0.js +1 -0
  162. package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
  163. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  164. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  165. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  166. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  167. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  168. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  169. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  170. package/.next/standalone/app/audit/audit-styles.css +6 -149
  171. package/.next/standalone/app/settings/page.tsx +31 -0
  172. package/.next/standalone/app/settings/settings-client.tsx +488 -0
  173. package/.next/standalone/components/navbar.tsx +2 -0
  174. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  175. package/.next/standalone/lib/auth/auth-store.ts +4 -4
  176. package/.next/standalone/lib/codex-sessions.ts +2 -1
  177. package/.next/standalone/lib/dashboard-host.ts +101 -0
  178. package/.next/standalone/lib/install-check.ts +22 -7
  179. package/.next/standalone/lib/pi-sessions.ts +99 -14
  180. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  181. package/.next/standalone/lib/telemetry-id.ts +52 -1
  182. package/.next/standalone/lib/telemetry.ts +8 -4
  183. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  184. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  185. package/.next/standalone/node_modules/react/package.json +3 -3
  186. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  187. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  188. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  189. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  190. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  191. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  192. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  193. package/.next/standalone/package.json +7 -6
  194. package/.next/standalone/proxy.ts +89 -1
  195. package/.next/standalone/server.js +1 -1
  196. package/bin/failproofai.mjs +197 -80
  197. package/dist/cli.mjs +17699 -15345
  198. package/dist/worker.mjs +1620 -257
  199. package/lib/auth/api-server-client.ts +2 -3
  200. package/lib/auth/auth-store.ts +4 -4
  201. package/lib/codex-sessions.ts +2 -1
  202. package/lib/dashboard-host.ts +101 -0
  203. package/lib/install-check.ts +22 -7
  204. package/lib/pi-sessions.ts +99 -14
  205. package/lib/telemetry-enabled.ts +54 -0
  206. package/lib/telemetry-id.ts +52 -1
  207. package/lib/telemetry.ts +8 -4
  208. package/package.json +7 -6
  209. package/scripts/install-telemetry.mjs +8 -1
  210. package/scripts/launch.ts +25 -3
  211. package/scripts/parse-script-args.ts +17 -1
  212. package/scripts/prune-standalone.mjs +20 -0
  213. package/scripts/translate-docs/readme-translator.ts +163 -4
  214. package/scripts/validate-mdx.ts +153 -13
  215. package/src/audit/audit-lock.ts +285 -0
  216. package/src/audit/audit-schedule.ts +111 -0
  217. package/src/audit/cache.ts +18 -4
  218. package/src/audit/cli-adapters/goose.ts +2 -1
  219. package/src/audit/cli-adapters/hermes.ts +28 -10
  220. package/src/audit/cli.ts +191 -35
  221. package/src/audit/dashboard-cache.ts +2 -1
  222. package/src/hooks/builtin-policies.ts +100 -3
  223. package/src/hooks/cloud-connection.ts +163 -14
  224. package/src/hooks/cloud-enrollment-cli.ts +119 -21
  225. package/src/hooks/cloud-enrollment.ts +87 -17
  226. package/src/hooks/cloud-introspect.ts +147 -0
  227. package/src/hooks/cloud-managed-policies.ts +33 -2
  228. package/src/hooks/collector-config.ts +32 -40
  229. package/src/hooks/configure-wizard.ts +696 -318
  230. package/src/hooks/custom-hooks-loader.ts +125 -11
  231. package/src/hooks/daemon-client.ts +83 -17
  232. package/src/hooks/daemon-download.ts +54 -2
  233. package/src/hooks/daemon-service.ts +595 -27
  234. package/src/hooks/first-run-gate.ts +39 -0
  235. package/src/hooks/fp-config.ts +497 -0
  236. package/src/hooks/fp-home.ts +342 -0
  237. package/src/hooks/fp-reset.ts +332 -0
  238. package/src/hooks/handler.ts +42 -1
  239. package/src/hooks/hook-activity-store.ts +35 -20
  240. package/src/hooks/hook-logger.ts +2 -1
  241. package/src/hooks/hook-telemetry.ts +5 -1
  242. package/src/hooks/hooks-config.ts +4 -3
  243. package/src/hooks/loader-utils.ts +166 -5
  244. package/src/hooks/manager.ts +5 -4
  245. package/src/hooks/onboarding-lock.ts +119 -0
  246. package/src/hooks/policy-types.ts +0 -7
  247. package/src/hooks/session-pause-cli.ts +15 -13
  248. package/src/hooks/session-pause.ts +36 -4
  249. package/src/hooks/setup-state.ts +256 -0
  250. package/src/hooks/tui.ts +4 -4
  251. package/src/hooks/worker-server.ts +53 -1
  252. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cmdyn_._.js +0 -3
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sdrz-j._.js +0 -3
  267. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  268. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  269. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  272. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1q9xxca.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  275. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  276. package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +0 -6
  277. package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +0 -1
  278. package/.next/standalone/.next/static/chunks/3h5_3u46gu9jx.js +0 -1
  279. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  280. package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
  281. package/.next/standalone/.next/static/chunks/43a1ywqsg9bq9.js +0 -1
  282. package/.next/standalone/Cargo.lock +0 -2222
  283. package/.next/standalone/Cargo.toml +0 -9
  284. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  285. package/.next/standalone/crates/PROTOCOL.md +0 -122
  286. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  287. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  288. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  289. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  290. package/.next/standalone/crates/failproofaid/src/main.rs +0 -532
  291. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  292. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  293. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  294. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  295. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  296. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -412
  297. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  298. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  299. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -484
  300. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  301. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  302. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  304. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  306. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  308. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  310. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  312. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -310
  314. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  315. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  317. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  319. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  321. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  322. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -458
  323. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -387
  324. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  325. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  326. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -738
  327. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -393
  328. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -975
  329. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  330. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1019
  331. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  332. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1008
  333. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1129
  334. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -621
  335. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -854
  336. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  337. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1191
  338. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  339. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  340. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  341. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  342. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  343. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  344. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  345. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  346. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  347. package/.next/standalone/integration-suite/Dockerfile +0 -47
  348. package/.next/standalone/integration-suite/README.md +0 -129
  349. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  350. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  351. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  352. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  353. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  354. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  355. package/.next/standalone/integration-suite/report.js +0 -120
  356. package/.next/standalone/integration-suite/run.sh +0 -159
  357. package/.next/standalone/openclaw-plugin/index.js +0 -223
  358. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  359. package/.next/standalone/openclaw-plugin/package.json +0 -12
  360. package/.next/standalone/osv-scanner.toml +0 -17
  361. package/.next/standalone/pi-extension/index.ts +0 -524
  362. package/.next/standalone/pi-extension/package.json +0 -12
  363. package/.next/standalone/postcss.config.mjs +0 -8
  364. package/.next/standalone/readme-arch-hq.gif +0 -0
  365. package/.next/standalone/rust-toolchain.toml +0 -4
  366. package/src/auth/cli.ts +0 -371
  367. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_buildManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_clientMiddlewareManifest.js +0 -0
  369. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_ssgManifest.js +0 -0
@@ -1,158 +0,0 @@
1
- //! GitHub Copilot CLI session capture — a [`filetail`](crate::filetail) adapter.
2
- //!
3
- //! Copilot writes one live-appended JSONL transcript per session at
4
- //! `~/.copilot/session-state/<sessionId>/events.jsonl` (`COPILOT_HOME`
5
- //! overrides the home). We open them read-only and turn each newly-appended
6
- //! line into AgentEye events; Copilot's own files are never written, moved or
7
- //! deleted.
8
- //!
9
- //! # The file is genuinely append-only, which is why a byte cursor is safe
10
- //!
11
- //! Proven rather than assumed, because two of the CLIs probed for this engine
12
- //! rewrite their transcripts without saying so (see [`RereadPolicy`]). A
13
- //! session was captured, resumed, and captured again: same inode, and the
14
- //! first 39,797 bytes of the resumed file are md5-identical to the entire
15
- //! pre-resume file. A resume appends `session.resume` and carries on — it does
16
- //! not rewrite, renumber, or compact anything. Hence
17
- //! [`RereadPolicy::ByteCursor`].
18
- //!
19
- //! # Siblings that must not be tailed
20
- //!
21
- //! Every one of these lives inside the same tree, and each breaks something
22
- //! different:
23
- //!
24
- //! * `workspace.yaml` — rewritten **in place** on every resume (`updated_at`
25
- //! moves), so a byte cursor would either re-ship it or resume mid-record.
26
- //! It is also YAML, not JSONL.
27
- //! * `checkpoints/index.md` — rewritten in place as checkpoints are added, and
28
- //! Markdown rather than JSON.
29
- //! * `session.db`, and `session-store.db` / `-wal` / `-shm` one level up —
30
- //! SQLite. A byte cursor over a page-rewriting binary file produces garbage
31
- //! indefinitely, and the `-wal` here is 900 KB and churning.
32
- //! * `logs/process-*.log` — process-scoped, NOT session-scoped: one file spans
33
- //! every session a `copilot` process ran, so its lines have no single session
34
- //! id to file events under. Plain text, and it embeds base64 icon blobs.
35
- //!
36
- //! [`is_transcript`] excludes all of them by requiring the exact filename
37
- //! `events.jsonl`, which is an allowlist rather than a denylist on purpose:
38
- //! whatever artifact a future Copilot release drops in a session directory is
39
- //! excluded by default instead of needing to be discovered in production.
40
- //!
41
- //! # Scope
42
- //!
43
- //! This covers the records that carry the session: start/end, prompts,
44
- //! assistant turns, and tool calls with their results. `hook.start` / `hook.end`
45
- //! are deliberately not mapped — failproofai's own hook-activity source already
46
- //! ships those from the store that records the decision, and re-deriving them
47
- //! here would double-count every hook. Checkpoints, `files/`, `research/` and
48
- //! the SQLite indexes are out of scope by construction.
49
-
50
- pub mod transform;
51
-
52
- use std::path::{Path, PathBuf};
53
-
54
- use crate::filetail::{Format, RereadPolicy, no_seed_state};
55
- use serde_json::Value;
56
-
57
- /// One transcript per session directory: `<sessionId>/events.jsonl`.
58
- pub const FORMAT: Format = Format {
59
- kind: "copilot",
60
- is_source_file: is_transcript,
61
- session_id_from_path,
62
- agent_id_from_path,
63
- agent_start: transform::agent_start,
64
- // No priming needed, unlike the Claude source. Copilot writes
65
- // `session.model_change` before the first `user.message`, so the opening
66
- // prompt already has a model by the time it is transformed — which also
67
- // means this source avoids the live-tail-vs-re-read hazard documented on
68
- // `Format::seed_state`.
69
- seed_state: no_seed_state,
70
- agent_end: transform::agent_end,
71
- transform_line: transform::transform_line,
72
- reread: RereadPolicy::ByteCursor,
73
- };
74
-
75
- /// The agent id used when nothing can be derived.
76
- pub const DEFAULT_AGENT_ID: &str = "copilot";
77
-
78
- /// The one file per session directory that is a transcript.
79
- const TRANSCRIPT_FILE: &str = "events.jsonl";
80
-
81
- /// Directory holding one subdirectory per session.
82
- const SESSION_STATE_DIR: &str = "session-state";
83
-
84
- /// Where Copilot keeps its per-session directories.
85
- ///
86
- /// `COPILOT_HOME` overrides `~/.copilot` — the same override the CLI itself
87
- /// honours, and the one `lib/copilot-sessions.ts` reads, so a machine that
88
- /// relocates its Copilot home stays covered by both halves of the product.
89
- pub fn session_state_root() -> PathBuf {
90
- if let Some(home) = std::env::var_os("COPILOT_HOME") {
91
- return PathBuf::from(home).join(SESSION_STATE_DIR);
92
- }
93
- std::env::var_os("HOME")
94
- .map(PathBuf::from)
95
- .unwrap_or_default()
96
- .join(".copilot")
97
- .join(SESSION_STATE_DIR)
98
- }
99
-
100
- /// A transcript is any file named exactly `events.jsonl`.
101
- ///
102
- /// The directory walk is recursive, so this predicate carries the whole filter.
103
- /// See the module docs for what each excluded sibling would break; the point of
104
- /// matching the name exactly is that they are excluded by construction rather
105
- /// than by a list that has to be kept current.
106
- fn is_transcript(path: &Path) -> bool {
107
- path.file_name().and_then(|n| n.to_str()) == Some(TRANSCRIPT_FILE)
108
- }
109
-
110
- /// The session id is the name of the directory containing the transcript.
111
- ///
112
- /// Every transcript is `events.jsonl`, so unlike Claude the filename carries
113
- /// nothing — the directory does. Verified on disk: the directory name equals
114
- /// `session.start`'s `data.sessionId` in all 4 live sessions, including one
115
- /// started with an explicitly chosen `--session-id`.
116
- ///
117
- /// Deliberately the only source of it. The header carries the same id, and
118
- /// reading both would give two answers that can disagree — while the path is
119
- /// what the cursor keys on and what the hook source's `sessionId` must match
120
- /// for hook events and transcript events to land on one timeline.
121
- fn session_id_from_path(path: &Path) -> Option<String> {
122
- if !is_transcript(path) {
123
- return None;
124
- }
125
- let dir = path.parent()?.file_name()?.to_str()?;
126
- // A transcript sitting directly in the session-state root has no session
127
- // directory; a synthetic id is better than filing it under the root's name,
128
- // which every such file would share.
129
- (!dir.is_empty() && dir != SESSION_STATE_DIR).then(|| dir.to_string())
130
- }
131
-
132
- /// `copilot-<project>`, from the real `cwd` on the transcript's first lines.
133
- ///
134
- /// NOT decoded from the directory name, which is a bare session UUID and
135
- /// encodes no path at all — Copilot keeps the working directory as a real
136
- /// field (`session.start` → `data.context.cwd`, repeated on `session.resume`),
137
- /// so there is nothing to invert and nothing to get wrong.
138
- ///
139
- /// The scheme matches what the Claude and hook sources derive, so a hook event
140
- /// and the transcript events for one run land under a single agent rather than
141
- /// two that look unrelated.
142
- fn agent_id_from_path(_path: &Path, header: &[String]) -> Option<String> {
143
- let cwd = header.iter().find_map(|line| {
144
- serde_json::from_str::<Value>(line)
145
- .ok()?
146
- .get("data")?
147
- .get("context")?
148
- .get("cwd")?
149
- .as_str()
150
- .map(str::to_string)
151
- })?;
152
- let project = transform::sanitize_id_part(
153
- cwd.trim_end_matches('/')
154
- .rsplit('/')
155
- .find(|p| !p.is_empty())?,
156
- );
157
- (!project.is_empty()).then(|| format!("copilot-{project}"))
158
- }
@@ -1,512 +0,0 @@
1
- //! GitHub Copilot CLI `events.jsonl` line → AgentEye events.
2
- //!
3
- //! Every function here is pure in `(line, ctx, offset, state)`, which is what
4
- //! makes a live tail and a full re-read byte-identical and therefore
5
- //! dedup-collapsible. Nothing may consult the clock, a counter, or anything
6
- //! outside its arguments.
7
- //!
8
- //! # Record shapes, verified on disk
9
- //!
10
- //! Read from 4 live sessions under `~/.copilot/session-state/` plus 3 captured
11
- //! transcripts (Copilot CLI 1.0.77, 140 records total). Every line — with no
12
- //! exceptions in that corpus — is:
13
- //!
14
- //! ```text
15
- //! {type, data, id, timestamp, parentId}
16
- //! ```
17
- //!
18
- //! `timestamp` is ISO-8601 UTC with **milliseconds** on every record
19
- //! (`2026-08-03T07:56:17.824Z`; 140/140 matched, and each file was monotonic).
20
- //! `parentId` is null exactly once per file, on the opening record. The
21
- //! per-`type` payloads this module reads:
22
- //!
23
- //! ```text
24
- //! session.start data.context.cwd, data.copilotVersion, data.sessionId
25
- //! session.resume data.context.cwd, data.selectedModel
26
- //! session.model_change data.newModel
27
- //! system.message data.content ← the ~31 KB system prompt; SKIPPED
28
- //! user.message data.content (raw) + data.transformedContent (wrapped)
29
- //! assistant.message data.{content, model, outputTokens, toolRequests[],
30
- //! messageId, turnId, interactionId}
31
- //! tool.execution_start data.{toolCallId, toolName, arguments, model}
32
- //! tool.execution_complete data.{toolCallId, success, result.content, model}
33
- //! assistant.turn_start/_end, session.shutdown, hook.start/hook.end
34
- //! ```
35
- //!
36
- //! # Deliberate omissions, each for a measured reason
37
- //!
38
- //! * **`system.message` is skipped entirely** rather than truncated. It is the
39
- //! 31,047-character system prompt, it is not conversation, and a resume
40
- //! re-emits it *verbatim* — the captured resumed session carries two copies
41
- //! of the identical blob. Truncating would still put ~62 KB of boilerplate on
42
- //! the session timeline across a couple of resumes and would still render as
43
- //! a message the user never sent; skipping costs nothing that is not already
44
- //! in the prompt/response pairs.
45
- //! * **`assistant.message.toolRequests[]` does not become `tool_use`.** The
46
- //! same call is announced there AND in its own `tool.execution_start` record.
47
- //! Across all 7 transcripts the two sets were identical (no requested-but-
48
- //! unstarted call, no started-but-unrequested call), so emitting both would
49
- //! double every tool call in the product. `tool.execution_start` wins because
50
- //! it is the record that pairs with `tool.execution_complete`.
51
- //! * **`reasoningText` / `reasoningOpaque` are dropped.** `reasoningText` is
52
- //! *repeated verbatim* on every later `assistant.message` of the same
53
- //! interaction — in the captured corpus turn 1 always carries turn 0's
54
- //! reasoning, byte-identical, including the opaque blob. Emitting it would
55
- //! attribute thinking to a turn that did not produce it.
56
- //! * **`result.detailedContent` is dropped.** For `bash` it equals
57
- //! `result.content` exactly; for `view` it is a synthetic `git diff` rendering
58
- //! of the same bytes. Either way it doubles the payload and adds no
59
- //! information.
60
- //! * **`user.message.transformedContent` is dropped in favour of `content`.**
61
- //! The transformed form wraps the prompt in an injected
62
- //! `<current_datetime>` and `<system_reminder>` — machinery, not what the
63
- //! human typed.
64
- //! * **`hook.start` / `hook.end` produce nothing.** failproofai's own
65
- //! hook-activity source already ships those, from the store that records the
66
- //! *decision*; re-deriving them here would double-count every hook on the
67
- //! `/hooks` page. Their `input` also embeds whole tool results, so they are
68
- //! the largest records after the system prompt.
69
-
70
- use serde_json::{Map, Value, json};
71
-
72
- use crate::cursor::TailState;
73
- use crate::filetail::Ctx;
74
-
75
- /// Longest derived id component.
76
- const MAX_ID_PART: usize = 48;
77
-
78
- /// Longest `goal` excerpt kept on `agent_start`.
79
- const MAX_GOAL_CHARS: usize = 1024;
80
-
81
- /// Make a derived id component safe and bounded.
82
- ///
83
- /// Kept local rather than shared with the Claude or hook sources, which each
84
- /// hold their own copy. The three MUST agree — a hook event and a transcript
85
- /// event for one run landing under two agent ids is the defect this scheme
86
- /// exists to prevent — but that agreement is asserted by a test rather than by
87
- /// a shared symbol, so no source can be refactored into breaking another.
88
- pub fn sanitize_id_part(s: &str) -> String {
89
- let cleaned: String = s
90
- .chars()
91
- .map(|c| {
92
- if c.is_ascii_alphanumeric() || c == '.' || c == '_' {
93
- c
94
- } else {
95
- '-'
96
- }
97
- })
98
- .collect();
99
- cleaned
100
- .split('-')
101
- .filter(|p| !p.is_empty())
102
- .collect::<Vec<_>>()
103
- .join("-")
104
- .chars()
105
- .take(MAX_ID_PART)
106
- .collect()
107
- }
108
-
109
- /// Normalize a Copilot timestamp to the microsecond form ingest expects,
110
- /// offset by `index` microseconds.
111
- ///
112
- /// Copilot writes milliseconds (`…T07:56:17.824Z`); the hook source emits
113
- /// microseconds. Both streams share one session timeline, so they must sort
114
- /// against each other — a millisecond string and a microsecond string do not
115
- /// compare correctly as text, and the server orders by `(ts, random id)`.
116
- ///
117
- /// The offset keeps several events derived from ONE line in order. Index 0 is
118
- /// untouched, so the primary event of a line keeps the line's exact time.
119
- ///
120
- /// Saturates within the second rather than carrying, so an offset can never
121
- /// reorder an event past a genuinely later line.
122
- pub fn with_index(ts: &str, index: usize) -> Option<String> {
123
- let body = ts.strip_suffix('Z')?;
124
- let (main, frac) = match body.rsplit_once('.') {
125
- Some((m, f)) => (m, f),
126
- None => (body, ""),
127
- };
128
- let mut micros: u32 = if frac.is_empty() {
129
- 0
130
- } else {
131
- let padded = format!("{frac:0<6}");
132
- padded.get(..6)?.parse().ok()?
133
- };
134
- micros = (micros + index.min(999) as u32).min(999_999);
135
- Some(format!("{main}.{micros:06}Z"))
136
- }
137
-
138
- fn ts_of(v: &Value) -> Option<&str> {
139
- v.get("timestamp")?.as_str()
140
- }
141
-
142
- fn type_of(v: &Value) -> Option<&str> {
143
- v.get("type")?.as_str()
144
- }
145
-
146
- fn str_at<'a>(data: &'a Value, key: &str) -> Option<&'a str> {
147
- data.get(key)?.as_str().filter(|s| !s.is_empty())
148
- }
149
-
150
- /// `data.context.cwd` off a `session.start` or `session.resume` payload.
151
- fn context_cwd(data: &Value) -> Option<&str> {
152
- data.get("context")?.get("cwd")?.as_str()
153
- }
154
-
155
- /// The envelope every emitted event carries.
156
- ///
157
- /// `copilot_line_offset` is the dedup discriminator: the server collapses
158
- /// events by content hash, so two genuinely repeated records — the same prompt
159
- /// asked twice, the same command run twice — must hash differently or one of
160
- /// them disappears from the product.
161
- fn base(ctx: &Ctx, kind: &str, ts: &str, index: usize, offset: u64) -> Option<Map<String, Value>> {
162
- let mut m = Map::new();
163
- m.insert("timestamp".into(), json!(with_index(ts, index)?));
164
- m.insert("session_id".into(), json!(ctx.session_id));
165
- m.insert("agent_id".into(), json!(ctx.agent_id));
166
- m.insert("type".into(), json!(kind));
167
- m.insert("environment".into(), json!(ctx.environment));
168
- m.insert("copilot_line_offset".into(), json!(offset));
169
- if index > 0 {
170
- m.insert("copilot_block_index".into(), json!(index));
171
- }
172
- Some(m)
173
- }
174
-
175
- /// Build the session's `agent_start` from its header lines.
176
- ///
177
- /// Scans for the first line carrying a usable timestamp rather than trusting
178
- /// line 1: a session with no `agent_start` is not merely incomplete, the server
179
- /// selects sessions on that event, so it is absent from the product entirely.
180
- ///
181
- /// The `goal` is reachable from the header despite the enormous
182
- /// `system.message` sitting in front of it — the first `user.message` is the
183
- /// 4th record and the whole prefix is ~32 KB, well inside the engine's 1 MB /
184
- /// 64-line header budget.
185
- pub fn agent_start(header: &[String], ctx: &Ctx, offset: u64) -> Option<(Value, Option<String>)> {
186
- let mut first_ts: Option<String> = None;
187
- let mut cwd: Option<String> = None;
188
- let mut version: Option<String> = None;
189
- let mut branch: Option<String> = None;
190
- let mut goal: Option<String> = None;
191
-
192
- for line in header {
193
- let Ok(v) = serde_json::from_str::<Value>(line) else {
194
- continue;
195
- };
196
- if first_ts.is_none()
197
- && let Some(t) = ts_of(&v)
198
- {
199
- first_ts = Some(t.to_string());
200
- }
201
- let Some(data) = v.get("data") else {
202
- continue;
203
- };
204
- if cwd.is_none()
205
- && let Some(c) = context_cwd(data)
206
- {
207
- cwd = Some(c.to_string());
208
- }
209
- if version.is_none()
210
- && let Some(x) = str_at(data, "copilotVersion")
211
- {
212
- version = Some(x.to_string());
213
- }
214
- // Present only when the session's cwd is inside a repository. The
215
- // captured corpus has none — the probe ran in a non-git `/tmp` dir —
216
- // but `lib/copilot-sessions.ts` records `context.{gitRoot, branch, …}`
217
- // as verified against Copilot 1.0.39, so it is taken opportunistically
218
- // rather than assumed absent.
219
- if branch.is_none()
220
- && let Some(b) = data.get("context").and_then(|c| c.get("branch"))
221
- && let Some(b) = b.as_str().filter(|s| !s.is_empty())
222
- {
223
- branch = Some(b.to_string());
224
- }
225
- // The session's goal is its first real human prompt — `content`, not
226
- // `transformedContent`, which prepends injected machinery.
227
- if goal.is_none()
228
- && type_of(&v) == Some("user.message")
229
- && let Some(text) = str_at(data, "content")
230
- {
231
- goal = Some(text.chars().take(MAX_GOAL_CHARS).collect());
232
- }
233
- }
234
-
235
- let ts = first_ts?;
236
- let mut m = base(ctx, "agent_start", &ts, 0, offset)?;
237
- if let Some(g) = goal {
238
- m.insert("goal".into(), json!(g));
239
- }
240
- if let Some(c) = cwd {
241
- m.insert("copilot_cwd".into(), json!(c));
242
- }
243
- if let Some(b) = branch {
244
- m.insert("copilot_git_branch".into(), json!(b));
245
- }
246
- if let Some(v) = version {
247
- m.insert("copilot_version".into(), json!(v));
248
- }
249
- Some((Value::Object(m), Some(ts)))
250
- }
251
-
252
- /// The single `agent_end`, derived deterministically from the last timestamp
253
- /// and the file size (used as its offset, so it is unique per session).
254
- ///
255
- /// Index 999 puts it after every content event sharing that timestamp — the
256
- /// server's tie-break is a random id, so without this a session can appear to
257
- /// end before its last turn. It matters here in particular because
258
- /// `session.shutdown` follows the final `assistant.turn_end` by ~70 ms, and a
259
- /// crashed session has no shutdown record at all, leaving `agent_end` sharing
260
- /// the exact timestamp of the last real event.
261
- pub fn agent_end(ctx: &Ctx, last_ts: &str, size: u64) -> Value {
262
- match base(ctx, "agent_end", last_ts, 999, size) {
263
- Some(m) => Value::Object(m),
264
- // `base` only fails on an unparseable timestamp, which cannot happen
265
- // here: `last_ts` came from a line this transform already accepted.
266
- None => json!({
267
- "timestamp": last_ts,
268
- "session_id": ctx.session_id,
269
- "agent_id": ctx.agent_id,
270
- "type": "agent_end",
271
- "environment": ctx.environment,
272
- }),
273
- }
274
- }
275
-
276
- /// One content line to its timestamp and the events it yields.
277
- ///
278
- /// The `type` dispatch is an explicit allowlist rather than Claude's
279
- /// "no timestamp means metadata" trick: every Copilot record carries a
280
- /// timestamp, including the ones that must produce nothing, so there is no
281
- /// free signal to lean on. Unknown types fall through to no events while still
282
- /// returning their timestamp, so a record type a future Copilot release adds
283
- /// costs nothing and still advances `agent_end`.
284
- pub fn transform_line(
285
- line: &str,
286
- ctx: &Ctx,
287
- offset: u64,
288
- state: &mut TailState,
289
- ) -> (Option<String>, Vec<Value>) {
290
- let Ok(v) = serde_json::from_str::<Value>(line) else {
291
- return (None, Vec::new());
292
- };
293
- let Some(ts) = ts_of(&v).map(str::to_string) else {
294
- return (None, Vec::new());
295
- };
296
- let Some(data) = v.get("data") else {
297
- return (Some(ts), Vec::new());
298
- };
299
-
300
- let events = match type_of(&v) {
301
- // A user prompt names no model, and the server builds a
302
- // `model_request` row's summary from the model alone. Copilot writes
303
- // `session.model_change` BEFORE the first `user.message` in every
304
- // captured transcript, which is why this source needs no `seed_state`
305
- // — and therefore does not inherit the live-tail-vs-re-read hazard
306
- // documented on `Format::seed_state`.
307
- Some("session.model_change") => {
308
- remember_model(state, str_at(data, "newModel"));
309
- Vec::new()
310
- }
311
- // A resume re-announces the model it is resuming with, so the first
312
- // prompt after a resume is never modelless either.
313
- Some("session.resume") => {
314
- remember_model(state, str_at(data, "selectedModel"));
315
- Vec::new()
316
- }
317
- Some("user.message") => user_message(data, ctx, &ts, offset, state),
318
- Some("assistant.message") => assistant_message(data, ctx, &ts, offset, state),
319
- Some("tool.execution_start") => tool_use(data, ctx, &ts, offset, state),
320
- Some("tool.execution_complete") => tool_result(data, ctx, &ts, offset, state),
321
- _ => Vec::new(),
322
- };
323
- (Some(ts), events)
324
- }
325
-
326
- fn remember_model(state: &mut TailState, model: Option<&str>) {
327
- if let Some(m) = model {
328
- state.last_model = Some(m.to_string());
329
- }
330
- }
331
-
332
- /// A human prompt → `model_request`.
333
- fn user_message(data: &Value, ctx: &Ctx, ts: &str, offset: u64, state: &TailState) -> Vec<Value> {
334
- let Some(text) = str_at(data, "content") else {
335
- return Vec::new();
336
- };
337
- let Some(mut m) = base(ctx, "model_request", ts, 0, offset) else {
338
- return Vec::new();
339
- };
340
- if let Some(model) = &state.last_model {
341
- m.insert("model".into(), json!(model));
342
- }
343
- m.insert(
344
- "messages".into(),
345
- json!([{ "role": "user", "content": text }]),
346
- );
347
- if let Some(id) = str_at(data, "interactionId") {
348
- m.insert("copilot_interaction_id".into(), json!(id));
349
- }
350
- vec![Value::Object(m)]
351
- }
352
-
353
- /// An assistant turn → exactly one `model_response`.
354
- ///
355
- /// Emitted even when `content` is empty, which is the case for every
356
- /// tool-calling turn. That is deliberate and load-bearing: `outputTokens`
357
- /// appears ONLY on `assistant.message` — `tool.execution_start` carries none —
358
- /// so skipping empty-content turns the way the Claude source skips empty text
359
- /// blocks would silently drop most of a session's output tokens. Measured on
360
- /// the captured corpus: 209 of 320 tokens in one session, 188 of 304 in
361
- /// another. The `content` key is omitted rather than set to `""`, so a
362
- /// consumer can tell "the model said nothing, it called tools" from "the model
363
- /// said the empty string".
364
- fn assistant_message(
365
- data: &Value,
366
- ctx: &Ctx,
367
- ts: &str,
368
- offset: u64,
369
- state: &mut TailState,
370
- ) -> Vec<Value> {
371
- let model = str_at(data, "model");
372
- remember_model(state, model);
373
-
374
- let Some(mut m) = base(ctx, "model_response", ts, 0, offset) else {
375
- return Vec::new();
376
- };
377
- m.insert("role".into(), json!("assistant"));
378
- if let Some(text) = str_at(data, "content") {
379
- m.insert("content".into(), json!(text));
380
- }
381
- if let Some(model) = model {
382
- m.insert("model".into(), json!(model));
383
- }
384
- if let Some(n) = data.get("outputTokens").and_then(|x| x.as_u64()) {
385
- m.insert("output_tokens".into(), json!(n));
386
- }
387
- if let Some(id) = str_at(data, "messageId") {
388
- m.insert("copilot_message_id".into(), json!(id));
389
- }
390
- if let Some(id) = str_at(data, "turnId") {
391
- m.insert("copilot_turn_id".into(), json!(id));
392
- }
393
- if let Some(id) = str_at(data, "interactionId") {
394
- m.insert("copilot_interaction_id".into(), json!(id));
395
- }
396
- // The count, not the calls: the calls themselves arrive as their own
397
- // `tool.execution_start` records. Keeping the count is what makes a
398
- // contentless turn legible instead of looking like a dropped response.
399
- let requests = data
400
- .get("toolRequests")
401
- .and_then(|r| r.as_array())
402
- .map(|r| r.len())
403
- .unwrap_or(0);
404
- if requests > 0 {
405
- m.insert("copilot_tool_request_count".into(), json!(requests));
406
- }
407
- vec![Value::Object(m)]
408
- }
409
-
410
- /// A tool call → `tool_use`, and the name is stashed for its result.
411
- fn tool_use(data: &Value, ctx: &Ctx, ts: &str, offset: u64, state: &mut TailState) -> Vec<Value> {
412
- let Some(mut m) = base(ctx, "tool_use", ts, 0, offset) else {
413
- return Vec::new();
414
- };
415
- let name = str_at(data, "toolName").unwrap_or("tool");
416
- // Fall back to an offset-derived id so two identical parallel calls cannot
417
- // hash-collapse into one row, and so a result can still find nothing
418
- // rather than finding the wrong call.
419
- let id = str_at(data, "toolCallId")
420
- .map(str::to_string)
421
- .unwrap_or_else(|| format!("copilot-{offset}"));
422
- state.remember_tool(id.clone(), name.to_string());
423
-
424
- m.insert("tool_name".into(), json!(name));
425
- m.insert("tool_call_id".into(), json!(id));
426
- if let Some(args) = data.get("arguments") {
427
- m.insert("input".into(), args.clone());
428
- }
429
- if let Some(model) = str_at(data, "model") {
430
- m.insert("model".into(), json!(model));
431
- }
432
- if let Some(t) = str_at(data, "turnId") {
433
- m.insert("copilot_turn_id".into(), json!(t));
434
- }
435
- vec![Value::Object(m)]
436
- }
437
-
438
- /// A tool completion → `tool_result`, paired to its call BY ID.
439
- ///
440
- /// Never by position. Copilot runs a turn's tool calls in parallel and writes
441
- /// each completion when it lands, so completions arrive out of order with
442
- /// respect to their starts — a captured session starts (`glob`, `view`) and
443
- /// completes (`view`, `glob`), 47 ms apart. Pairing positionally would label
444
- /// every row in that turn with the wrong tool, which is worse than labelling
445
- /// none of them: a wrong tool name is indistinguishable from a right one.
446
- fn tool_result(data: &Value, ctx: &Ctx, ts: &str, offset: u64, state: &TailState) -> Vec<Value> {
447
- let Some(mut m) = base(ctx, "tool_result", ts, 0, offset) else {
448
- return Vec::new();
449
- };
450
- if let Some(id) = str_at(data, "toolCallId") {
451
- m.insert("tool_call_id".into(), json!(id));
452
- // The tool's name appears on NO completion record. Without carrying it
453
- // from the call, every tool result is a blank row in the product.
454
- if let Some(name) = state.tool_name(id) {
455
- m.insert("tool_name".into(), json!(name));
456
- }
457
- }
458
- let output = data
459
- .get("result")
460
- .and_then(|r| r.get("content"))
461
- .map(stringify);
462
- if let Some(text) = &output {
463
- m.insert("output".into(), json!(text));
464
- }
465
- // Absent `success` is treated as success: every record in the captured
466
- // corpus carries it, and inventing a failure for a field a future release
467
- // might drop would flag healthy sessions as broken.
468
- if data.get("success").and_then(|s| s.as_bool()) == Some(false) {
469
- // The server's `is_error` is a truthiness check, so this must never be
470
- // an empty string — a failure with no output would otherwise render as
471
- // a success.
472
- let reason = output
473
- .as_deref()
474
- .map(str::trim)
475
- .filter(|r| !r.is_empty())
476
- .unwrap_or("copilot tool execution failed");
477
- m.insert("error".into(), json!(reason));
478
- m.insert("error_type".into(), json!("copilot_tool_error"));
479
- }
480
- if let Some(model) = str_at(data, "model") {
481
- m.insert("model".into(), json!(model));
482
- }
483
- if let Some(t) = str_at(data, "turnId") {
484
- m.insert("copilot_turn_id".into(), json!(t));
485
- }
486
- vec![Value::Object(m)]
487
- }
488
-
489
- /// Render a tool result payload as text.
490
- ///
491
- /// Copilot writes `result.content` as a string in every captured record, but
492
- /// the field is untyped JSON — a structured payload is rendered rather than
493
- /// dropped, so a future tool returning an object does not produce an empty row.
494
- fn stringify(v: &Value) -> String {
495
- match v {
496
- Value::String(s) => s.clone(),
497
- Value::Array(items) => items
498
- .iter()
499
- .map(|b| match b {
500
- Value::String(s) => s.clone(),
501
- other => other
502
- .get("text")
503
- .and_then(|t| t.as_str())
504
- .map(str::to_string)
505
- .unwrap_or_else(|| other.to_string()),
506
- })
507
- .collect::<Vec<_>>()
508
- .join("\n"),
509
- Value::Null => String::new(),
510
- other => other.to_string(),
511
- }
512
- }