failproofai 1.0.0-beta.2 → 1.0.0-beta.4
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/build-manifest.json +6 -6
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- 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.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/build-manifest.json +3 -3
- 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.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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +2 -2
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -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.nft.json +1 -1
- 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.nft.json +1 -1
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +1 -1
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
- package/.next/standalone/.next/server/app/policies/page.js +2 -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/build-manifest.json +3 -3
- 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 +1 -1
- 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/build-manifest.json +3 -3
- 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 +1 -1
- 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/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +1 -1
- 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/chunks/[externals]__0h9_83k._.js +3 -0
- package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-dh5jy._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0h3lfmp._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l4vuqm._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0longh-._.js → [root-of-the-server]__0szqzl8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10oa-v9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__18d-9oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__1p7s8-d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0nb0syg._.js → [root-of-the-server]__0cmdyn_._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ath6v_._.js → [root-of-the-server]__0ls0gsr._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0uno39i._.js → [root-of-the-server]__0n_uvv0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__0rag5dg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0rtdfof._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1psjybo._.js → [root-of-the-server]__0sdrz-j._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0114ewg._.js → [root-of-the-server]__1j4u3yd._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- 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_html-to-image_es_index_0oy_-36.js → node_modules_html-to-image_es_index_1q9xxca.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
- 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 +32 -20
- package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 06jakt_xhoolk.js} +1 -1
- package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
- package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +6 -0
- package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 0ue9ak_xexiqh.js} +1 -1
- package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
- package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +1 -0
- package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
- package/.next/standalone/.next/static/chunks/2c90h1g6x8z8y.js +1 -0
- package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 2zb3m1tzq6h98.js} +1 -1
- package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
- package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 31cqiv_cpgj9m.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 3h5_3u46gu9jx.js} +1 -1
- package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +2 -0
- package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 43a1ywqsg9bq9.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
- package/.next/standalone/Cargo.lock +1992 -198
- package/.next/standalone/Cargo.toml +1 -1
- package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
- package/.next/standalone/app/components/pause-notices.tsx +111 -0
- package/.next/standalone/app/policies/hooks-client.tsx +81 -6
- package/.next/standalone/crates/CLOUD_POLICIES.md +136 -0
- package/.next/standalone/crates/failproofaid/Cargo.toml +7 -0
- package/.next/standalone/crates/failproofaid/src/cloud_client.rs +484 -0
- package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +921 -0
- package/.next/standalone/crates/failproofaid/src/main.rs +452 -3
- package/.next/standalone/crates/failproofaid/src/paths.rs +29 -0
- package/.next/standalone/crates/fpai-collect/Cargo.toml +43 -0
- package/.next/standalone/crates/fpai-collect/src/config.rs +412 -0
- package/.next/standalone/crates/fpai-collect/src/cursor.rs +520 -0
- package/.next/standalone/crates/fpai-collect/src/delivery.rs +472 -0
- package/.next/standalone/crates/fpai-collect/src/filetail.rs +484 -0
- package/.next/standalone/crates/fpai-collect/src/health.rs +369 -0
- package/.next/standalone/crates/fpai-collect/src/lib.rs +34 -0
- package/.next/standalone/crates/fpai-collect/src/redact.rs +516 -0
- package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +279 -0
- package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +619 -0
- package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +156 -0
- package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +715 -0
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +158 -0
- package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +512 -0
- package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +511 -0
- package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +586 -0
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +318 -0
- package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +579 -0
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +310 -0
- package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +448 -0
- package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +23 -0
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +176 -0
- package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +500 -0
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +315 -0
- package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +493 -0
- package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +201 -0
- package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +533 -0
- package/.next/standalone/crates/fpai-collect/src/spool.rs +458 -0
- package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +387 -0
- package/.next/standalone/crates/fpai-collect/src/supervisor.rs +423 -0
- package/.next/standalone/crates/fpai-collect/src/uploader.rs +600 -0
- package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +738 -0
- package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +393 -0
- package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +975 -0
- package/.next/standalone/crates/fpai-collect/tests/config.rs +286 -0
- package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +1019 -0
- package/.next/standalone/crates/fpai-collect/tests/delivery.rs +281 -0
- package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +1008 -0
- package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +1129 -0
- package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +621 -0
- package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +854 -0
- package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +1478 -0
- package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +1191 -0
- package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +290 -0
- package/.next/standalone/crates/fpai-collect/tests/uploader.rs +506 -0
- package/.next/standalone/package.json +15 -9
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +129 -0
- package/dist/cli.mjs +2255 -779
- package/dist/worker.mjs +365 -52
- package/package.json +15 -9
- package/scripts/build-daemon-packages.mjs +210 -0
- package/scripts/daemon-platforms.mjs +42 -0
- package/scripts/prune-standalone.mjs +6 -0
- package/scripts/publish-aliases.mjs +62 -41
- package/src/hooks/builtin-policies.ts +139 -0
- package/src/hooks/cloud-connection.ts +205 -0
- package/src/hooks/cloud-enrollment-cli.ts +227 -0
- package/src/hooks/cloud-enrollment.ts +173 -0
- package/src/hooks/cloud-managed-policies.ts +138 -0
- package/src/hooks/collector-config.ts +226 -0
- package/src/hooks/configure-wizard.ts +221 -5
- package/src/hooks/custom-hooks-loader.ts +69 -25
- package/src/hooks/daemon-client.ts +18 -0
- package/src/hooks/daemon-download.ts +137 -27
- package/src/hooks/daemon-service.ts +23 -8
- package/src/hooks/handler.ts +144 -6
- package/src/hooks/hook-activity-store.ts +50 -0
- package/src/hooks/loader-utils.ts +6 -4
- package/src/hooks/policy-types.ts +35 -0
- package/src/hooks/session-pause-cli.ts +157 -0
- package/src/hooks/session-pause.ts +236 -0
- package/src/hooks/tui.ts +115 -0
- package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
- package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
- package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
- package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
- package/.next/standalone/.next/static/chunks/31feojxhjyjpl.js +0 -1
- package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
- package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
- package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → StV6Lay7Q1cU4ysYCFJmH}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → StV6Lay7Q1cU4ysYCFJmH}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → StV6Lay7Q1cU4ysYCFJmH}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
//! Pi (`@mariozechner/pi-coding-agent`) session capture — a
|
|
2
|
+
//! [`filetail`](crate::filetail) adapter.
|
|
3
|
+
//!
|
|
4
|
+
//! pi writes one JSONL transcript per session under
|
|
5
|
+
//! `~/.pi/agent/sessions/<encoded-cwd>/<ISO-timestamp>_<uuid>.jsonl`. We open
|
|
6
|
+
//! them read-only and turn each newly-appended line into AgentEye events; pi's
|
|
7
|
+
//! own files are never written, moved or deleted.
|
|
8
|
+
//!
|
|
9
|
+
//! # The file does not exist until the first assistant message completes
|
|
10
|
+
//!
|
|
11
|
+
//! `SessionManager._persist()` short-circuits while the session has no
|
|
12
|
+
//! assistant message in it, buffering entries in memory and marking itself
|
|
13
|
+
//! unflushed; the first assistant response then writes the whole prefix at
|
|
14
|
+
//! once. Measured on a live run: the file appeared at 1551 bytes, already
|
|
15
|
+
//! containing the header, the model change, the thinking-level change, the user
|
|
16
|
+
//! prompt and the assistant turn.
|
|
17
|
+
//!
|
|
18
|
+
//! Two consequences, both of which look like bugs if you do not know this:
|
|
19
|
+
//!
|
|
20
|
+
//! * **A run killed before its first assistant message leaves ZERO trace on
|
|
21
|
+
//! disk.** Discovery finding nothing in a directory pi is actively writing to
|
|
22
|
+
//! is normal, not an error, and must never be logged or handled as one.
|
|
23
|
+
//! * The engine's `agent_start` retry loop is never exercised for pi in
|
|
24
|
+
//! practice — by the time a file exists, its header is complete — which is
|
|
25
|
+
//! also why [`Format::seed_state`](crate::filetail::Format::seed_state) is
|
|
26
|
+
//! left as a no-op here. See `transform_line`'s `model_change` arm: pi orders
|
|
27
|
+
//! the model ahead of the first prompt inside the file itself, so the opening
|
|
28
|
+
//! row is never blank and nothing has to be seeded out of band. That matters
|
|
29
|
+
//! because seeding is the one part of the format table that is NOT dedup-safe
|
|
30
|
+
//! between a live tail and a later full re-read.
|
|
31
|
+
//!
|
|
32
|
+
//! # `_rewriteFile()` rewrites the whole file, but only on cold paths
|
|
33
|
+
//!
|
|
34
|
+
//! Steady-state turns are `appendFileSync`, so a byte cursor is correct and
|
|
35
|
+
//! [`RereadPolicy::ByteCursor`] is what this format declares. Two paths do a
|
|
36
|
+
//! full `writeFileSync` over an existing transcript:
|
|
37
|
+
//!
|
|
38
|
+
//! * resuming a file whose entries all failed to parse — it is replaced by a
|
|
39
|
+
//! lone fresh header, a large **shrink**;
|
|
40
|
+
//! * migrating a session written by pi with `version < 3`.
|
|
41
|
+
//!
|
|
42
|
+
//! The shrink case is covered: the engine notices `size < cursor.offset`,
|
|
43
|
+
//! resets to zero and re-reads, which re-ships the surviving prefix for the
|
|
44
|
+
//! server to dedup. That is the behaviour `a_rewritten_transcript_that_shrank_
|
|
45
|
+
//! is_re_read_rather_than_seeked_past` pins. A migration that happens to leave
|
|
46
|
+
//! the file the same size or larger would not be detected — accepted, because
|
|
47
|
+
//! v3 has been current since well before either probed release and a
|
|
48
|
+
//! `ValidatePrefix` policy would not help anyway (the migration rewrites
|
|
49
|
+
//! interior records, not line 1).
|
|
50
|
+
//!
|
|
51
|
+
//! Forking and branching are NOT in that list, contrary to what the shape of
|
|
52
|
+
//! the code suggests: both write a **new** file and leave the source transcript
|
|
53
|
+
//! untouched, so they appear to this source as an ordinary new session whose
|
|
54
|
+
//! header carries a `parentSession` pointer.
|
|
55
|
+
//!
|
|
56
|
+
//! # Scope
|
|
57
|
+
//!
|
|
58
|
+
//! Session start/end, user prompts, assistant turns, tool calls and their
|
|
59
|
+
//! results. `compaction`, `branch_summary`, `label`, `session_info`, `custom`
|
|
60
|
+
//! and `thinking_level_change` records are recognised as records — they keep
|
|
61
|
+
//! the session clock moving — but emit no events; they need their own event
|
|
62
|
+
//! types rather than being forced into these.
|
|
63
|
+
|
|
64
|
+
pub mod transform;
|
|
65
|
+
|
|
66
|
+
use std::path::{Path, PathBuf};
|
|
67
|
+
|
|
68
|
+
use crate::filetail::{Format, RereadPolicy, no_seed_state};
|
|
69
|
+
use serde_json::Value;
|
|
70
|
+
|
|
71
|
+
/// Session transcripts: `<ISO-timestamp>_<uuid>.jsonl` under an encoded-cwd
|
|
72
|
+
/// directory.
|
|
73
|
+
pub const FORMAT: Format = Format {
|
|
74
|
+
kind: "pi",
|
|
75
|
+
is_source_file: is_transcript,
|
|
76
|
+
session_id_from_path,
|
|
77
|
+
agent_id_from_path,
|
|
78
|
+
agent_start: transform::agent_start,
|
|
79
|
+
// Deliberately nothing: pi's own record order already primes the model
|
|
80
|
+
// before the first prompt. See the module docs.
|
|
81
|
+
seed_state: no_seed_state,
|
|
82
|
+
agent_end: transform::agent_end,
|
|
83
|
+
transform_line: transform::transform_line,
|
|
84
|
+
reread: RereadPolicy::ByteCursor,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/// The agent id used when nothing can be derived.
|
|
88
|
+
pub const DEFAULT_AGENT_ID: &str = "pi";
|
|
89
|
+
|
|
90
|
+
/// Where pi keeps its sessions.
|
|
91
|
+
///
|
|
92
|
+
/// `PI_CODING_AGENT_DIR` relocates the whole agent directory (`~/.pi/agent`),
|
|
93
|
+
/// and sessions hang off it — there is no separate root override. The tilde is
|
|
94
|
+
/// expanded because pi expands it too: a value of `~/elsewhere` that we took
|
|
95
|
+
/// literally would have us watching a directory named `~` under the process
|
|
96
|
+
/// cwd, i.e. capturing nothing, silently.
|
|
97
|
+
///
|
|
98
|
+
/// Not exhaustive by construction. `PI_CODING_AGENT_SESSION_DIR`, `--session-dir`
|
|
99
|
+
/// and a project's `settings.json` can each point ONE session at an arbitrary
|
|
100
|
+
/// directory outside this root; those sessions are not discovered. Widening the
|
|
101
|
+
/// walk to find them would mean walking the whole filesystem, so they are out of
|
|
102
|
+
/// scope rather than half-handled.
|
|
103
|
+
pub fn sessions_root() -> PathBuf {
|
|
104
|
+
let agent_dir = match std::env::var_os("PI_CODING_AGENT_DIR") {
|
|
105
|
+
Some(raw) => expand_tilde(PathBuf::from(raw)),
|
|
106
|
+
None => home().join(".pi").join("agent"),
|
|
107
|
+
};
|
|
108
|
+
agent_dir.join("sessions")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fn home() -> PathBuf {
|
|
112
|
+
std::env::var_os("HOME")
|
|
113
|
+
.map(PathBuf::from)
|
|
114
|
+
.unwrap_or_default()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fn expand_tilde(path: PathBuf) -> PathBuf {
|
|
118
|
+
let Some(s) = path.to_str() else {
|
|
119
|
+
return path;
|
|
120
|
+
};
|
|
121
|
+
match s {
|
|
122
|
+
"~" => home(),
|
|
123
|
+
_ => match s.strip_prefix("~/") {
|
|
124
|
+
Some(rest) => home().join(rest),
|
|
125
|
+
None => path,
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn is_uuid36(s: &str) -> bool {
|
|
131
|
+
s.len() == 36 && s.chars().all(|c| c.is_ascii_hexdigit() || c == '-')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// The uuid half of a `<ISO-timestamp>_<uuid>` stem.
|
|
135
|
+
///
|
|
136
|
+
/// Split from the RIGHT: the timestamp half is `2026-08-03T07-57-44-591Z`, full
|
|
137
|
+
/// of the same `-` the uuid uses, so only the underscore separates them
|
|
138
|
+
/// reliably. The uuid itself never contains one.
|
|
139
|
+
fn uuid_in_stem(stem: &str) -> Option<&str> {
|
|
140
|
+
let (ts, id) = stem.rsplit_once('_')?;
|
|
141
|
+
(!ts.is_empty() && is_uuid36(id)).then_some(id)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// A transcript is `<ISO-timestamp>_<uuid>.jsonl`.
|
|
145
|
+
///
|
|
146
|
+
/// The walk is recursive, so this predicate carries the whole filter. Requiring
|
|
147
|
+
/// both halves of the stem keeps this from claiming anything an operator (or a
|
|
148
|
+
/// future pi) drops beside the transcripts — an editor's `.jsonl~`, an export,
|
|
149
|
+
/// a hand-made note — any of which a byte cursor would otherwise tail as if it
|
|
150
|
+
/// were a session.
|
|
151
|
+
fn is_transcript(path: &Path) -> bool {
|
|
152
|
+
path.file_name()
|
|
153
|
+
.and_then(|n| n.to_str())
|
|
154
|
+
.and_then(|n| n.strip_suffix(".jsonl"))
|
|
155
|
+
.and_then(uuid_in_stem)
|
|
156
|
+
.is_some()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// The uuid after the `_` in the filename IS the session id.
|
|
160
|
+
///
|
|
161
|
+
/// Deliberately the only source of it: the header's `id` field holds the same
|
|
162
|
+
/// value, but taking it from both would give two answers that could disagree,
|
|
163
|
+
/// and the path is what the cursor keys on. The uuid is also what the hook
|
|
164
|
+
/// source reports as `session_id`, so hook events and transcript events for one
|
|
165
|
+
/// run land on a single timeline.
|
|
166
|
+
fn session_id_from_path(path: &Path) -> Option<String> {
|
|
167
|
+
path.file_name()
|
|
168
|
+
.and_then(|n| n.to_str())
|
|
169
|
+
.and_then(|n| n.strip_suffix(".jsonl"))
|
|
170
|
+
.and_then(uuid_in_stem)
|
|
171
|
+
.map(str::to_string)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// `pi-<project>`, from the real `cwd` on the transcript's header.
|
|
175
|
+
///
|
|
176
|
+
/// NOT decoded from the directory name. pi encodes the working directory as
|
|
177
|
+
/// `--${cwd.replace(/^[/\\]/,"").replace(/[/\\:]/g,"-")}--`, mapping every
|
|
178
|
+
/// separator to `-` while leaving literal `-` alone — so the encoding is not
|
|
179
|
+
/// invertible, and the loss is not a corner case: the probe's own
|
|
180
|
+
/// `/tmp/probe-pi` becomes `--tmp-probe-pi--`, which decodes back to
|
|
181
|
+
/// `/tmp/probe/pi` and yields the project name `pi` instead of `probe-pi`. The
|
|
182
|
+
/// `cwd` field on line 1 is exact.
|
|
183
|
+
///
|
|
184
|
+
/// The scheme matches what the hook-activity source derives, so hook events and
|
|
185
|
+
/// transcript events for one run land under a single agent rather than two that
|
|
186
|
+
/// look unrelated.
|
|
187
|
+
fn agent_id_from_path(_path: &Path, header: &[String]) -> Option<String> {
|
|
188
|
+
let cwd = header.iter().find_map(|line| {
|
|
189
|
+
serde_json::from_str::<Value>(line)
|
|
190
|
+
.ok()?
|
|
191
|
+
.get("cwd")?
|
|
192
|
+
.as_str()
|
|
193
|
+
.map(str::to_string)
|
|
194
|
+
})?;
|
|
195
|
+
let project = transform::sanitize_id_part(
|
|
196
|
+
cwd.trim_end_matches('/')
|
|
197
|
+
.rsplit('/')
|
|
198
|
+
.find(|p| !p.is_empty())?,
|
|
199
|
+
);
|
|
200
|
+
(!project.is_empty()).then(|| format!("pi-{project}"))
|
|
201
|
+
}
|