failproofai 1.0.0-beta.5 → 1.0.0-beta.7
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/server/app/policies/page.js +4 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +4 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
- package/.next/standalone/.next/server/app/settings/page.js +18 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c_qcgp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kbc9bs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_194yh9u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +2 -2
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
- package/.next/standalone/.next/static/chunks/0945ybz3fsvba.js +1 -0
- package/.next/standalone/.next/static/chunks/{0cr85wuc9gc9k.js → 0gr76ps8_zm26.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2cqk4a9s893oy.js → 0m-m7wgtxdoav.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1tp9dex23zs8y.js → 11l6oh9cyb35a.js} +1 -1
- package/.next/standalone/.next/static/chunks/166f8qnh03jqc.js +6 -0
- package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/{2s9g1dsde_ts1.js → 2aej0vi1fm87m.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1bkynffp3q1f7.js → 2bxld6-rym3ah.js} +22 -22
- package/.next/standalone/.next/static/chunks/{2v1rpzrh2i1wv.js → 2p2kz4aj1tooj.js} +1 -1
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/36pc3_8fpb6ic.js +1 -0
- package/.next/standalone/.next/static/chunks/{2k285z07qce47.js → 3t1wr_kx6brwc.js} +1 -1
- package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
- package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
- package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
- package/.next/standalone/app/api/audit/run/route.ts +28 -0
- package/.next/standalone/app/api/audit/status/route.ts +13 -1
- package/.next/standalone/app/api/auth/status/route.ts +1 -1
- package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
- package/.next/standalone/app/audit/audit-styles.css +6 -149
- package/.next/standalone/app/settings/page.tsx +31 -0
- package/.next/standalone/app/settings/settings-client.tsx +488 -0
- package/.next/standalone/components/navbar.tsx +2 -0
- package/.next/standalone/lib/auth/api-server-client.ts +2 -3
- package/.next/standalone/lib/auth/auth-store.ts +4 -4
- package/.next/standalone/lib/codex-sessions.ts +2 -1
- package/.next/standalone/lib/dashboard-host.ts +101 -0
- package/.next/standalone/lib/install-check.ts +22 -7
- package/.next/standalone/lib/pi-sessions.ts +99 -14
- package/.next/standalone/lib/telemetry-enabled.ts +54 -0
- package/.next/standalone/lib/telemetry-id.ts +52 -1
- package/.next/standalone/lib/telemetry.ts +8 -4
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +7 -6
- package/.next/standalone/proxy.ts +89 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +202 -86
- package/dist/cli.mjs +17803 -15346
- package/dist/worker.mjs +1620 -257
- package/lib/auth/api-server-client.ts +2 -3
- package/lib/auth/auth-store.ts +4 -4
- package/lib/codex-sessions.ts +2 -1
- package/lib/dashboard-host.ts +101 -0
- package/lib/install-check.ts +22 -7
- package/lib/pi-sessions.ts +99 -14
- package/lib/telemetry-enabled.ts +54 -0
- package/lib/telemetry-id.ts +52 -1
- package/lib/telemetry.ts +8 -4
- package/package.json +7 -6
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +17 -1
- package/scripts/prune-standalone.mjs +20 -0
- package/scripts/translate-docs/readme-translator.ts +163 -4
- package/scripts/validate-mdx.ts +153 -13
- package/src/audit/audit-lock.ts +285 -0
- package/src/audit/audit-schedule.ts +111 -0
- package/src/audit/cache.ts +18 -4
- package/src/audit/cli-adapters/goose.ts +2 -1
- package/src/audit/cli-adapters/hermes.ts +28 -10
- package/src/audit/cli.ts +191 -35
- package/src/audit/dashboard-cache.ts +2 -1
- package/src/hooks/builtin-policies.ts +100 -3
- package/src/hooks/cloud-connection.ts +160 -14
- package/src/hooks/cloud-enrollment-cli.ts +119 -21
- package/src/hooks/cloud-enrollment.ts +87 -17
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +33 -2
- package/src/hooks/collector-config.ts +25 -43
- package/src/hooks/configure-wizard.ts +750 -318
- package/src/hooks/custom-hooks-loader.ts +125 -11
- package/src/hooks/daemon-client.ts +83 -17
- package/src/hooks/daemon-download.ts +54 -2
- package/src/hooks/daemon-service.ts +605 -29
- package/src/hooks/first-run-gate.ts +39 -0
- package/src/hooks/fp-config.ts +497 -0
- package/src/hooks/fp-home.ts +355 -0
- package/src/hooks/fp-reset.ts +332 -0
- package/src/hooks/handler.ts +42 -1
- package/src/hooks/hook-activity-store.ts +35 -20
- package/src/hooks/hook-logger.ts +2 -1
- package/src/hooks/hook-telemetry.ts +5 -1
- package/src/hooks/hooks-config.ts +4 -3
- package/src/hooks/loader-utils.ts +166 -5
- package/src/hooks/manager.ts +5 -4
- package/src/hooks/onboarding-attempt.ts +179 -0
- package/src/hooks/onboarding-lock.ts +119 -0
- package/src/hooks/policy-types.ts +0 -7
- package/src/hooks/session-pause-cli.ts +15 -13
- package/src/hooks/session-pause.ts +36 -4
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +4 -4
- package/src/hooks/worker-server.ts +53 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__094nop3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1gak4pd._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_12c_2cz.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
- package/.next/standalone/.next/static/chunks/1bw4dfzflelcv.js +0 -1
- package/.next/standalone/.next/static/chunks/34sjdw8lv46e3.js +0 -6
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
- package/.next/standalone/Cargo.lock +0 -2222
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
- package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
- package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -639
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
- package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
- package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
- package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
- package/.next/standalone/crates/fpai-collect/src/config.rs +0 -420
- package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
- package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
- package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -487
- package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
- package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
- package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
- package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
- package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
- package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
- package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
- package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
- package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -314
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
- package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
- package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
- package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
- package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -535
- package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -390
- package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
- package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
- package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -739
- package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -394
- package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -976
- package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
- package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1020
- package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
- package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1009
- package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1130
- package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -622
- package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -855
- package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
- package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1192
- package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
- package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
- package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
- package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
- package/.next/standalone/integration-suite/Dockerfile +0 -47
- package/.next/standalone/integration-suite/README.md +0 -129
- package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
- package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
- package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
- package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
- package/.next/standalone/integration-suite/install-clis.sh +0 -136
- package/.next/standalone/integration-suite/probe-cli.sh +0 -248
- package/.next/standalone/integration-suite/report.js +0 -120
- package/.next/standalone/integration-suite/run.sh +0 -159
- package/.next/standalone/openclaw-plugin/index.js +0 -223
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
- package/.next/standalone/openclaw-plugin/package.json +0 -12
- package/.next/standalone/osv-scanner.toml +0 -17
- package/.next/standalone/pi-extension/index.ts +0 -524
- package/.next/standalone/pi-extension/package.json +0 -12
- package/.next/standalone/postcss.config.mjs +0 -8
- package/.next/standalone/readme-arch-hq.gif +0 -0
- package/.next/standalone/rust-toolchain.toml +0 -4
- package/src/auth/cli.ts +0 -371
- /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_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
|
-
}
|