failproofai 1.0.7 → 1.0.8
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 +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/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +4 -4
- 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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- 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 +4 -4
- 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/_not-found/__PAGE__.segment.rsc +14 -14
- 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 +8 -7
- 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.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/status/route.js.nft.json +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 +9 -7
- 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 +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- 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 +6 -6
- 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 +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +11 -10
- 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 +8 -7
- 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 -6
- 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 +7 -6
- 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/server-reference-manifest.json +52 -8
- package/.next/standalone/.next/server/app/settings/page.js +12 -7
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1msfs-h._.js → [externals]__19_pzeq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1_bftcl._.js → [externals]__20kzpkf._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ttrwnd._.js +22 -0
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
- package/.next/standalone/.next/server/chunks/{_1c3k-8x._.js → _1q5i8mb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/lib_16xa545._.js +3 -0
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0u._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +4 -2
- package/.next/standalone/.next/server/chunks/ssr/[externals]__0ohnuzs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__056wjo4._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06pflha._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0n0xg95._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__0rwtwpm._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__11mayhe._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12e7nhs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13t1zkw._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19evfi8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0cxe_2_._.js → [root-of-the-server]__1mf3zp6._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__1pprgri._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__1q4p5b8._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_166t73i._.js → _0-vcssj._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_13kfn90._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1es2j7i._.js → _1gb0ifp._.js} +5 -5
- package/.next/standalone/.next/server/chunks/ssr/{_1_qswah._.js → _1q46vxx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +3 -0
- 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/app_settings_02tf1h4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0drixxt._.js → node_modules_next_dist_0w6mzq5._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +151 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0-q0umm._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9d._.js +12 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0g194sy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +4 -2
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1kx9e0d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_semantic_pack-policies_ts_0gh_bu_._.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- 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 +67 -23
- package/.next/standalone/.next/static/chunks/06dnzbolj00mc.js +6 -0
- package/.next/standalone/.next/static/chunks/0bhidk90e-07f.css +2 -0
- package/.next/standalone/.next/static/chunks/{2mdh397ghgnvv.js → 0m-9d6yn9hx4j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0__8a7m868fvf.js → 0wbjy0zo-m7is.js} +1 -1
- package/.next/standalone/.next/static/chunks/16f3fa-lx38hk.js +1 -0
- package/.next/standalone/.next/static/chunks/{0o6qlkgubtoex.js → 21uv-uusw329x.js} +1 -1
- package/.next/standalone/.next/static/chunks/2g2tki08kdhie.js +1 -0
- package/.next/standalone/.next/static/chunks/2qdpj67x6ifk_.js +69 -0
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +1 -0
- package/.next/standalone/.next/static/chunks/3-nbtkhg9y-1j.js +1 -0
- package/.next/standalone/.next/static/chunks/355km0ihuqo1p.js +1 -0
- package/.next/standalone/.next/static/chunks/{13i7-9is-vhys.js → 3c3qbmosdjl6w.js} +1 -1
- package/.next/standalone/PROBE-FOLLOWUP.md +186 -0
- package/.next/standalone/app/actions/get-jev-config.ts +604 -0
- package/.next/standalone/app/actions/pack-actions.ts +12 -0
- package/.next/standalone/app/actions/update-jev-config.ts +569 -0
- package/.next/standalone/app/components/jev-notices.tsx +96 -0
- package/.next/standalone/app/policies/hooks-client.tsx +14 -3
- package/.next/standalone/app/settings/jev-panel.tsx +630 -0
- package/.next/standalone/app/settings/page.tsx +20 -1
- package/.next/standalone/app/settings/settings-client.tsx +27 -1
- package/.next/standalone/app/settings/settings.css +79 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +22 -4
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +14 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +17 -3
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +3 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +22 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +169 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +65 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +61 -0
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/typescript/CHANGELOG.md +18 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +0 -274
- package/.next/standalone/sdk/typescript/scripts/release.mjs +30 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +3 -2
- package/bin/failproofai.mjs +148 -4
- package/dist/cli.mjs +18599 -9478
- package/dist/index.js +19 -1
- package/dist/worker.mjs +10500 -3773
- package/package.json +10 -10
- package/pi-extension/index.ts +11 -0
- package/scripts/build-policy-pack.mjs +53 -2
- package/src/audit/features.ts +3 -2
- package/src/hooks/builtin-policies.ts +195 -8
- package/src/hooks/cloud-connection.ts +190 -1
- package/src/hooks/cloud-enrollment-cli.ts +57 -8
- package/src/hooks/cloud-introspect.ts +6 -0
- package/src/hooks/cloud-managed-policies.ts +22 -0
- package/src/hooks/configure-wizard.ts +26 -6
- package/src/hooks/custom-hooks-loader.ts +98 -10
- package/src/hooks/custom-hooks-registry.ts +45 -1
- package/src/hooks/effective-reviewers.ts +243 -0
- package/src/hooks/first-run-gate.ts +5 -0
- package/src/hooks/flush-cli.ts +35 -8
- package/src/hooks/fp-config.ts +266 -1
- package/src/hooks/fp-home.ts +23 -0
- package/src/hooks/fp-reset.ts +22 -4
- package/src/hooks/handler.ts +303 -7
- package/src/hooks/hook-activity-store.ts +118 -6
- package/src/hooks/hook-telemetry.ts +41 -0
- package/src/hooks/jev-activity.ts +385 -0
- package/src/hooks/jev-cli.ts +2172 -0
- package/src/hooks/jev-cloud-connection.ts +392 -0
- package/src/hooks/loader-utils.ts +6 -0
- package/src/hooks/manager.ts +45 -7
- package/src/hooks/pack-cli.ts +568 -37
- package/src/hooks/pack-failclosed.ts +3 -0
- package/src/hooks/pack-manifest.ts +489 -7
- package/src/hooks/pack-store.ts +194 -11
- package/src/hooks/policy-authority.ts +386 -0
- package/src/hooks/policy-catalog.ts +206 -0
- package/src/hooks/policy-evaluator.ts +963 -796
- package/src/hooks/policy-registry.ts +27 -1
- package/src/hooks/policy-reviewability.ts +258 -0
- package/src/hooks/policy-types.ts +128 -0
- package/src/hooks/semantic/combine.ts +581 -0
- package/src/hooks/semantic/compile.ts +176 -0
- package/src/hooks/semantic/decide.ts +513 -0
- package/src/hooks/semantic/envelope.ts +1255 -0
- package/src/hooks/semantic/evaluator.ts +556 -0
- package/src/hooks/semantic/facts.ts +387 -0
- package/src/hooks/semantic/intent.ts +1190 -0
- package/src/hooks/semantic/jev-client.ts +1155 -0
- package/src/hooks/semantic/jev-config.ts +1142 -0
- package/src/hooks/semantic/jev-review.ts +389 -0
- package/src/hooks/semantic/jev-stats.ts +289 -0
- package/src/hooks/semantic/jev-throttle.ts +427 -0
- package/src/hooks/semantic/pack-policies.ts +294 -0
- package/src/hooks/semantic/policies.ts +597 -0
- package/src/hooks/semantic/precondition-names.ts +60 -0
- package/src/hooks/semantic/preconditions.ts +58 -0
- package/src/hooks/semantic/redact.ts +2937 -0
- package/src/hooks/semantic/session-root.ts +116 -0
- package/src/hooks/semantic/types.ts +162 -0
- package/src/hooks/semver-precedence.ts +128 -0
- package/src/hooks/tui.ts +4 -0
- package/src/hooks/types.ts +1 -1
- package/src/hooks/worker-server.ts +121 -27
- package/src/index.ts +6 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_r2rbg._.js +0 -24
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bd3mje._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_0h_douw._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1-i_gzc._.js +0 -23
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +0 -3
- package/.next/standalone/.next/static/chunks/094xgi4owxaqf.js +0 -1
- package/.next/standalone/.next/static/chunks/1rz20_pz828f3.js +0 -6
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +0 -1
- package/.next/standalone/.next/static/chunks/2klitrtzpaoe0.js +0 -1
- package/.next/standalone/.next/static/chunks/2rshywgeqsyzk.css +0 -2
- package/.next/standalone/.next/static/chunks/3pzx4chkhko9k.js +0 -1
- package/.next/standalone/.next/static/chunks/3rh5o7e16irrm.js +0 -69
- package/.next/standalone/.next/static/chunks/43ufqrz8qo3h-.js +0 -1
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → 5yKM8NoTOpUI0MoQl8eo5}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → 5yKM8NoTOpUI0MoQl8eo5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{PgeWCHmyVbjRznv2VO7KF → 5yKM8NoTOpUI0MoQl8eo5}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project a session started in, pinned the first time the session is seen.
|
|
3
|
+
*
|
|
4
|
+
* The hook payload's `cwd` is the agent's LIVE shell directory. Claude Code
|
|
5
|
+
* keeps a `cd` across calls, so a session started in `~/work` that once ran
|
|
6
|
+
* `cd ~/work/api` reports `~/work/api` from then on — and `facts.projectRoot`,
|
|
7
|
+
* derived from that, shrank with it. Every file elsewhere in `~/work` then read
|
|
8
|
+
* as `outside_project_in_home`, and `read-outside-workspace` fired on the
|
|
9
|
+
* project the human opened.
|
|
10
|
+
*
|
|
11
|
+
* Letting the root FOLLOW the `cd` instead would be a bypass: `cd ~/.ssh` in
|
|
12
|
+
* one call and `cat id_rsa` in the next would make `~/.ssh` the project. So the
|
|
13
|
+
* root is fixed at the session's first reviewed call and never moves after.
|
|
14
|
+
* A `cd` can change how a relative path resolves (that is still the live cwd's
|
|
15
|
+
* job, in `extractPaths`) but never what counts as inside the project.
|
|
16
|
+
*
|
|
17
|
+
* Failure is never stricter or looser than before this file existed: no
|
|
18
|
+
* session id, an unreadable store, or a store someone else can write to all
|
|
19
|
+
* fall back to the live cwd's root — exactly the old behaviour. A session whose
|
|
20
|
+
* first reviewed call came after a `cd` pins that later directory; same again.
|
|
21
|
+
*/
|
|
22
|
+
import { mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { isAbsolute, resolve } from "node:path";
|
|
24
|
+
import { semanticDir } from "../fp-home";
|
|
25
|
+
import { findProjectRoot } from "./facts";
|
|
26
|
+
import { looseIntentStoreDir } from "./intent";
|
|
27
|
+
|
|
28
|
+
const SESSION_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
29
|
+
const ROOT_FILE_RE = /^[A-Za-z0-9._-]{1,128}\.json$/;
|
|
30
|
+
/** Sessions outlive the intent store's six hours; a week covers any real one. */
|
|
31
|
+
export const SESSION_ROOT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
32
|
+
const DIR_WRITABLE_BY_OTHERS = 0o022;
|
|
33
|
+
|
|
34
|
+
const rootsDir = (): string => resolve(semanticDir(), "roots");
|
|
35
|
+
const rootFile = (sessionId: string): string => resolve(rootsDir(), `${sessionId}.json`);
|
|
36
|
+
|
|
37
|
+
/** A stored root is only believed when it is a plain absolute path, and never `/`. */
|
|
38
|
+
function validRoot(v: unknown): v is string {
|
|
39
|
+
return typeof v === "string" && isAbsolute(v) && resolve(v) === v && v !== "/";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether the store may be trusted. A directory anyone else can write to lets
|
|
44
|
+
* them swap in a root of their choosing — a wider root is fewer paths outside
|
|
45
|
+
* it — so a loose one is ignored, after the same tightening the intent store
|
|
46
|
+
* applies (`looseIntentStoreDir`).
|
|
47
|
+
*/
|
|
48
|
+
function storeIsOurs(): boolean {
|
|
49
|
+
if (process.platform === "win32") return true;
|
|
50
|
+
if (looseIntentStoreDir() !== null) return false;
|
|
51
|
+
try {
|
|
52
|
+
const mode = statSync(rootsDir()).mode & 0o777;
|
|
53
|
+
return (mode & DIR_WRITABLE_BY_OTHERS) === 0;
|
|
54
|
+
} catch {
|
|
55
|
+
return true; // Not there yet: nothing to read out of it.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readPinned(sessionId: string): string | null {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(readFileSync(rootFile(sessionId), "utf8")) as { root?: unknown };
|
|
62
|
+
return validRoot(parsed?.root) ? parsed.root : null;
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Delete root files older than {@link SESSION_ROOT_MAX_AGE_MS}. Best effort. */
|
|
69
|
+
export function pruneSessionRoots(now: number = Date.now()): number {
|
|
70
|
+
let removed = 0;
|
|
71
|
+
try {
|
|
72
|
+
const dir = rootsDir();
|
|
73
|
+
for (const name of readdirSync(dir)) {
|
|
74
|
+
if (!ROOT_FILE_RE.test(name)) continue;
|
|
75
|
+
const path = resolve(dir, name);
|
|
76
|
+
try {
|
|
77
|
+
const st = statSync(path);
|
|
78
|
+
if (st.isFile() && now - st.mtimeMs > SESSION_ROOT_MAX_AGE_MS) {
|
|
79
|
+
unlinkSync(path);
|
|
80
|
+
removed++;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Raced with another writer, or already gone.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// No directory yet.
|
|
88
|
+
}
|
|
89
|
+
return removed;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The project root to judge this call's paths against: the one pinned for the
|
|
94
|
+
* session, or — on its first reviewed call — the live cwd's root, pinned now.
|
|
95
|
+
* Null only when there is no cwd at all. Never throws.
|
|
96
|
+
*/
|
|
97
|
+
export function sessionProjectRoot(sessionId: string | undefined, cwd: string | undefined, now: number = Date.now()): string | null {
|
|
98
|
+
if (!cwd) return null;
|
|
99
|
+
const live = findProjectRoot(cwd);
|
|
100
|
+
if (!sessionId || !SESSION_ID_RE.test(sessionId)) return live;
|
|
101
|
+
try {
|
|
102
|
+
if (!storeIsOurs()) return live;
|
|
103
|
+
const pinned = readPinned(sessionId);
|
|
104
|
+
if (pinned !== null) return pinned;
|
|
105
|
+
if (!validRoot(live)) return live;
|
|
106
|
+
mkdirSync(rootsDir(), { recursive: true, mode: 0o700 });
|
|
107
|
+
// `wx`: the first writer wins. Two calls racing on a new session both
|
|
108
|
+
// computed a root from the same moment; whichever lands is the pin, and
|
|
109
|
+
// the loser's EEXIST just means it answers with the live root this once.
|
|
110
|
+
writeFileSync(rootFile(sessionId), JSON.stringify({ root: live, at: now }), { mode: 0o600, flag: "wx" });
|
|
111
|
+
pruneSessionRoots(now);
|
|
112
|
+
} catch {
|
|
113
|
+
// Unwritable store: the old behaviour, for this call.
|
|
114
|
+
}
|
|
115
|
+
return live;
|
|
116
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the semantic (Jev-backed) evaluator.
|
|
3
|
+
*
|
|
4
|
+
* The regex engine answers "does this string match"; this one asks TypeSafe's
|
|
5
|
+
* Jev a set of typed yes/no questions about the tool call and decides from the
|
|
6
|
+
* probabilities. Everything here is plain data so the decision can be replayed
|
|
7
|
+
* offline from a recorded response — see `decide.ts`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Coarse class of a tool, derived deterministically from its canonical name. */
|
|
11
|
+
export type ToolClass = "shell" | "write" | "read" | "network" | "other";
|
|
12
|
+
|
|
13
|
+
/** A single yes/no question sent to Jev (the `noul` primitive). */
|
|
14
|
+
export interface NoulQuestion {
|
|
15
|
+
type: "noul";
|
|
16
|
+
instructions: string;
|
|
17
|
+
criteria?: { true: string; false: string };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface JevRequest {
|
|
21
|
+
model: string;
|
|
22
|
+
state: Record<string, unknown>;
|
|
23
|
+
questions: Record<string, NoulQuestion>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface JevNoulAnswer {
|
|
27
|
+
type?: "noul";
|
|
28
|
+
noul: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface JevResponse {
|
|
32
|
+
model: string;
|
|
33
|
+
answers: Record<string, JevNoulAnswer>;
|
|
34
|
+
usage?: { input_tokens?: number; output_tokens?: number };
|
|
35
|
+
/** Set when the provider did not report which Jev version answered (Cloudflare's unversioned alias). */
|
|
36
|
+
modelUnverified?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A yes/no probe. A policy fires only when every one of its probes holds. */
|
|
40
|
+
export interface Probe {
|
|
41
|
+
id: string;
|
|
42
|
+
instructions: string;
|
|
43
|
+
criteria?: { true: string; false: string };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SemanticPolicy {
|
|
47
|
+
/** Short slug; registered as `semantic/<name>` in reasons and logs. */
|
|
48
|
+
name: string;
|
|
49
|
+
/** Past-tense, human-readable description of what was caught. */
|
|
50
|
+
title: string;
|
|
51
|
+
/** Tool classes this policy is asked about. Unknown/MCP tools get every policy. */
|
|
52
|
+
appliesTo: ReadonlyArray<ToolClass>;
|
|
53
|
+
/**
|
|
54
|
+
* `deny` policies block when the evidence is strong and instruct when it is
|
|
55
|
+
* moderate; `instruct` policies only ever warn.
|
|
56
|
+
*/
|
|
57
|
+
mode: "deny" | "instruct";
|
|
58
|
+
/**
|
|
59
|
+
* Whether the user's own explicit request may clear this policy. False for
|
|
60
|
+
* the few things a prompt injection most wants to be approved for.
|
|
61
|
+
*/
|
|
62
|
+
userCanOverride: boolean;
|
|
63
|
+
/** Every probe must hold for the policy to fire (conjunction). */
|
|
64
|
+
probes: ReadonlyArray<Probe>;
|
|
65
|
+
/** If this holds, the policy does not fire — the documented exceptions. */
|
|
66
|
+
exempt?: Probe;
|
|
67
|
+
/** Deterministic precondition over the facts; when false nothing is asked. */
|
|
68
|
+
precondition?: (facts: Facts) => boolean;
|
|
69
|
+
/** Shown to the agent when the policy fires. */
|
|
70
|
+
guidance: string;
|
|
71
|
+
/** The pack that declared it; absent for the compiled-in set. Attribution only. */
|
|
72
|
+
origin?: { packId: string; packVersion?: string };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface PathFact {
|
|
76
|
+
asWritten: string;
|
|
77
|
+
resolved: string;
|
|
78
|
+
relation: "inside_project" | "project_root" | "outside_project_in_home" | "home_root" | "system" | "root";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Deterministic facts computed locally, so Jev never has to count or resolve. */
|
|
82
|
+
export interface Facts {
|
|
83
|
+
toolName: string;
|
|
84
|
+
toolClass: ToolClass;
|
|
85
|
+
/** False for tools no canonical map recognised — MCP tools, skills, etc. */
|
|
86
|
+
toolIsKnown: boolean;
|
|
87
|
+
cwd: string | null;
|
|
88
|
+
projectRoot: string | null;
|
|
89
|
+
currentGitBranch: string | null;
|
|
90
|
+
paths: PathFact[];
|
|
91
|
+
permissionMode: string | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface SemanticInput {
|
|
95
|
+
eventType: string;
|
|
96
|
+
toolName: string;
|
|
97
|
+
toolInput: Record<string, unknown>;
|
|
98
|
+
cwd?: string;
|
|
99
|
+
/**
|
|
100
|
+
* The project root pinned for the session (`session-root.ts`), so a `cd`
|
|
101
|
+
* cannot move what counts as inside the project. Absent: derived from `cwd`.
|
|
102
|
+
*/
|
|
103
|
+
projectRoot?: string;
|
|
104
|
+
permissionMode?: string;
|
|
105
|
+
/** What the human typed recently, oldest first. Trusted channel only. */
|
|
106
|
+
userSaid: string[];
|
|
107
|
+
/**
|
|
108
|
+
* The agent's last visible message before the human's latest one — what a
|
|
109
|
+
* reply like "yes" or "do it" refers to. Agent-written, so it may only ever
|
|
110
|
+
* explain a human reply; it is never consent on its own.
|
|
111
|
+
*/
|
|
112
|
+
agentLastMessage?: string | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* How "did the human ask for this?" is put to Jev.
|
|
117
|
+
*
|
|
118
|
+
* - `v0`: one `user_asked` question per policy, phrased as the policy's harm,
|
|
119
|
+
* plus a global `scope` question. Measured to clear a policy 6 times in
|
|
120
|
+
* 1,332 cases.
|
|
121
|
+
* - `v1`: three task-level questions asked once per call — is this a step
|
|
122
|
+
* toward the human's task, did they ask for this exact operation, does it
|
|
123
|
+
* reach beyond what they asked — judged only against the human's own words
|
|
124
|
+
* with harness-written text removed.
|
|
125
|
+
*/
|
|
126
|
+
export type IntentMode = "v0" | "v1";
|
|
127
|
+
|
|
128
|
+
export interface PolicyOutcome {
|
|
129
|
+
policy: string;
|
|
130
|
+
mode: "deny" | "instruct";
|
|
131
|
+
/** The policy's own `userCanOverride`: false is a check no consent can clear. */
|
|
132
|
+
userCanOverride?: boolean;
|
|
133
|
+
/** The pack that declared the check, when one did (`SemanticPolicy.origin`). */
|
|
134
|
+
origin?: { packId: string; packVersion?: string };
|
|
135
|
+
/** min over probe probabilities — one weak probe spoils the evidence. */
|
|
136
|
+
evidence: number;
|
|
137
|
+
exempt: number | null;
|
|
138
|
+
userAsked: number | null;
|
|
139
|
+
targetNamedByUser: boolean;
|
|
140
|
+
/** Fired, and the request also addressed the reviewer — so it was blocked outright. */
|
|
141
|
+
escalatedByInjection: boolean;
|
|
142
|
+
verdict: "deny" | "instruct" | "overridden" | "none";
|
|
143
|
+
/** v1 only: why an override applied, or that a deny was softened to instruct. */
|
|
144
|
+
intent?: "op-requested" | "task-step" | "downgraded-task-step";
|
|
145
|
+
/**
|
|
146
|
+
* Set when a clear was withheld because the shell scan may have missed part
|
|
147
|
+
* of the command (`ScannedCommand.complete` false): what the call touches is
|
|
148
|
+
* not known locally, so no intent answer may clear or soften it.
|
|
149
|
+
*/
|
|
150
|
+
targetScanIncomplete?: true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface SemanticVerdict {
|
|
154
|
+
decision: "allow" | "deny" | "instruct";
|
|
155
|
+
reason: string | null;
|
|
156
|
+
outcomes: PolicyOutcome[];
|
|
157
|
+
injectionSuspected: number | null;
|
|
158
|
+
/** The `scope` answer: does the request stay within what the user asked? null when not asked. */
|
|
159
|
+
scopeWithinRequest: number | null;
|
|
160
|
+
/** v1 only: set when the call was flagged for reaching beyond the human's task. */
|
|
161
|
+
beyondTask?: boolean;
|
|
162
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semver PRECEDENCE — ordering two version strings — for the one question a
|
|
3
|
+
* pack manifest asks: is the CLI about to run this pack at least the version
|
|
4
|
+
* the pack says it needs?
|
|
5
|
+
*
|
|
6
|
+
* ## Why this is written out rather than reused
|
|
7
|
+
*
|
|
8
|
+
* Nothing in this repo orders two versions. `daemonVersionSkew()` compares them
|
|
9
|
+
* for EQUALITY, which is the right test there — the CLI and the daemon ship
|
|
10
|
+
* from one commit, so anything but identical is skew — and it yields no
|
|
11
|
+
* ordering at all. There is no semver dependency either: this module runs on
|
|
12
|
+
* the hook path, and the loader's rule is that nothing there may pull in a
|
|
13
|
+
* package.
|
|
14
|
+
*
|
|
15
|
+
* ## Why the obvious implementation is wrong here
|
|
16
|
+
*
|
|
17
|
+
* Splitting on dots and comparing numbers gets the release triple right and the
|
|
18
|
+
* PRERELEASE exactly backwards. This package's version is `1.0.7-beta.0`, and
|
|
19
|
+
* the beta cut from that line is the first build that understands
|
|
20
|
+
* `minCliVersion` at all — so it is the build a pack's minimum is aimed at. On
|
|
21
|
+
* numbers alone `1.0.7-beta.0` satisfies a minimum of `1.0.7`, which reads the
|
|
22
|
+
* beta as the newer build when it is the older one, and the pack installs on a
|
|
23
|
+
* CLI that may be missing the feature the minimum exists to require.
|
|
24
|
+
*
|
|
25
|
+
* So precedence follows semver §11 for real:
|
|
26
|
+
*
|
|
27
|
+
* 1.0.6 < 1.0.7-beta.0 < 1.0.7-beta.1 < 1.0.7 < 1.0.8
|
|
28
|
+
*
|
|
29
|
+
* - the release triple is compared numerically, field by field;
|
|
30
|
+
* - a version WITH a prerelease is lower than the same triple without one;
|
|
31
|
+
* - prerelease identifiers are compared left to right, numeric ones
|
|
32
|
+
* numerically and always below alphanumeric ones, and a longer identifier
|
|
33
|
+
* list wins when every shared identifier is equal;
|
|
34
|
+
* - build metadata (`+2026.09.24`) is ignored, because it carries no precedence.
|
|
35
|
+
*
|
|
36
|
+
* Anything this cannot order is reported as unorderable rather than guessed at.
|
|
37
|
+
* A caller comparing versions to decide whether to REFUSE something needs to
|
|
38
|
+
* know the difference between "older" and "I cannot tell".
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/** A version decomposed far enough to be ordered. Build metadata is dropped. */
|
|
42
|
+
export interface SemVer {
|
|
43
|
+
major: number;
|
|
44
|
+
minor: number;
|
|
45
|
+
patch: number;
|
|
46
|
+
/** Dot-separated prerelease identifiers, numeric ones as numbers. Empty for a release. */
|
|
47
|
+
prerelease: ReadonlyArray<string | number>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Strict semver, and strict on purpose. `v1.0.0`, `1.0`, `01.2.3` and
|
|
52
|
+
* `1.0.7-beta.0.` are all things people write and none of them has one
|
|
53
|
+
* unambiguous ordering, so they are refused rather than normalised — a
|
|
54
|
+
* `minCliVersion` nobody can order is a requirement nobody can check.
|
|
55
|
+
*/
|
|
56
|
+
const SEMVER_RE =
|
|
57
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
58
|
+
|
|
59
|
+
const NUMERIC_ID = /^(?:0|[1-9]\d*)$/;
|
|
60
|
+
|
|
61
|
+
/** Decompose a version, or `null` when it is not one this can order. */
|
|
62
|
+
export function parseSemver(value: unknown): SemVer | null {
|
|
63
|
+
if (typeof value !== "string") return null;
|
|
64
|
+
const m = SEMVER_RE.exec(value);
|
|
65
|
+
if (!m) return null;
|
|
66
|
+
return {
|
|
67
|
+
major: Number(m[1]),
|
|
68
|
+
minor: Number(m[2]),
|
|
69
|
+
patch: Number(m[3]),
|
|
70
|
+
prerelease: m[4] === undefined ? [] : m[4].split(".").map((id) => (NUMERIC_ID.test(id) ? Number(id) : id)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const cmp = (a: number, b: number): number => (a < b ? -1 : a > b ? 1 : 0);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Compare two prerelease identifier lists. An EMPTY list is a release, and a
|
|
78
|
+
* release outranks every prerelease of the same triple — the rule the naive
|
|
79
|
+
* implementation loses, and the one that decides this release's own case.
|
|
80
|
+
*/
|
|
81
|
+
function comparePrerelease(a: ReadonlyArray<string | number>, b: ReadonlyArray<string | number>): number {
|
|
82
|
+
if (a.length === 0 && b.length === 0) return 0;
|
|
83
|
+
if (a.length === 0) return 1;
|
|
84
|
+
if (b.length === 0) return -1;
|
|
85
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
86
|
+
const x = a[i];
|
|
87
|
+
const y = b[i];
|
|
88
|
+
if (x === y) continue;
|
|
89
|
+
const xNum = typeof x === "number";
|
|
90
|
+
const yNum = typeof y === "number";
|
|
91
|
+
// "Numeric identifiers always have lower precedence than non-numeric
|
|
92
|
+
// ones" (semver §11.4.3): `1.0.0-1` is below `1.0.0-alpha`, so an `rc.1`
|
|
93
|
+
// line and a `1` line never interleave by accident.
|
|
94
|
+
if (xNum !== yNum) return xNum ? -1 : 1;
|
|
95
|
+
if (xNum && yNum) return cmp(x as number, y as number);
|
|
96
|
+
return (x as string) < (y as string) ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
// Every shared identifier is equal, so the longer list is the later build:
|
|
99
|
+
// `1.0.0-beta` < `1.0.0-beta.1`.
|
|
100
|
+
return cmp(a.length, b.length);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** `-1`, `0` or `1` by semver precedence. */
|
|
104
|
+
export function compareSemver(a: SemVer, b: SemVer): number {
|
|
105
|
+
return cmp(a.major, b.major) || cmp(a.minor, b.minor) || cmp(a.patch, b.patch) || comparePrerelease(a.prerelease, b.prerelease);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* `-1` / `0` / `1` for two version STRINGS, or `null` when either one cannot be
|
|
110
|
+
* ordered. Null is deliberately not 0: a caller that treats "unorderable" as
|
|
111
|
+
* "equal" silently accepts the requirement it could not check.
|
|
112
|
+
*
|
|
113
|
+
* There is deliberately NO `meetsMinimum(current, minimum): boolean | null` here.
|
|
114
|
+
* It existed, and it collapsed two situations that need opposite treatment into
|
|
115
|
+
* one value: an ABSENT minimum (every pack published before today has none, so it
|
|
116
|
+
* must satisfy) and a MALFORMED one. A call site reading `=== false` would then
|
|
117
|
+
* ignore a typo'd requirement, and one reading `!== true` would refuse every
|
|
118
|
+
* pre-existing pack — and on an `enforce` pack that means denying every tool call
|
|
119
|
+
* the pack covers. Neither mistake shows up until a user hits it, so the decision
|
|
120
|
+
* is encoded once, where the field lives: `checkPackMinCliVersion` in
|
|
121
|
+
* `pack-manifest.ts`.
|
|
122
|
+
*/
|
|
123
|
+
export function compareVersions(a: unknown, b: unknown): number | null {
|
|
124
|
+
const left = parseSemver(a);
|
|
125
|
+
const right = parseSemver(b);
|
|
126
|
+
if (!left || !right) return null;
|
|
127
|
+
return compareSemver(left, right);
|
|
128
|
+
}
|
package/src/hooks/tui.ts
CHANGED
|
@@ -96,6 +96,10 @@ export interface MultiSelectOptions<T> {
|
|
|
96
96
|
|
|
97
97
|
const ESC = "\x1B";
|
|
98
98
|
|
|
99
|
+
/** How every "the key was on argv" warning opens, whichever command took it (`jev setup --token`, `config --token`). */
|
|
100
|
+
export const TOKEN_ON_ARGV =
|
|
101
|
+
"--token was on the command line: your shell history has it, and while this command ran any process of yours could read it from the process list.";
|
|
102
|
+
|
|
99
103
|
// ── glyphs ────────────────────────────────────────────────────────────────
|
|
100
104
|
// Exported so the other branded prompts (install-prompt.ts) share the exact
|
|
101
105
|
// same set instead of hand-syncing copies.
|
package/src/hooks/types.ts
CHANGED
|
@@ -1059,7 +1059,7 @@ export type HookEventType = (typeof HOOK_EVENT_TYPES)[number];
|
|
|
1059
1059
|
*
|
|
1060
1060
|
* It stays in `HOOK_EVENT_TYPES` because that list is the canonical set a
|
|
1061
1061
|
* policy may subscribe to; this one governs what we write into settings.json.
|
|
1062
|
-
* No builtin matches it (all
|
|
1062
|
+
* No builtin matches it (all 40 match only PreToolUse / PostToolUse /
|
|
1063
1063
|
* PermissionRequest / Stop), so nothing is lost by not registering.
|
|
1064
1064
|
*/
|
|
1065
1065
|
export const CLAUDE_INSTALL_EVENT_TYPES = HOOK_EVENT_TYPES.filter(
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
* evaluations could have request B's `clearPolicies()` wipe request A's
|
|
18
18
|
* in-flight registration. Serializing keeps the existing registry code
|
|
19
19
|
* correct with zero changes; a worker_threads/process pool is a valid
|
|
20
|
-
* future enhancement if this becomes a real throughput bottleneck.
|
|
20
|
+
* future enhancement if this becomes a real throughput bottleneck. The one
|
|
21
|
+
* exception is the network wait of a two-tier (Jev) review, which happens
|
|
22
|
+
* after the registry is no longer read and so does not hold the queue (see
|
|
23
|
+
* `enqueue`).
|
|
21
24
|
*/
|
|
22
25
|
import { createConnection, createServer, type Server, type Socket } from "node:net";
|
|
23
26
|
import { existsSync, unlinkSync } from "node:fs";
|
|
@@ -74,7 +77,8 @@ const TASK_DEADLINE_MS = 60_000;
|
|
|
74
77
|
|
|
75
78
|
/**
|
|
76
79
|
* Serializes every request across every connection through one chain, so
|
|
77
|
-
* `evaluateHookEvent` calls never overlap
|
|
80
|
+
* `evaluateHookEvent` calls never overlap while reading the registry (see
|
|
81
|
+
* `release` below). `.catch()` on each link keeps the
|
|
78
82
|
* chain alive even if a handler throws unexpectedly — one bad request must
|
|
79
83
|
* never wedge every request queued behind it.
|
|
80
84
|
*
|
|
@@ -97,37 +101,115 @@ const TASK_DEADLINE_MS = 60_000;
|
|
|
97
101
|
* worker whose `try_wait()` reports it gone), which costs one cold start and
|
|
98
102
|
* denies the in-flight requests — the same fail-closed outcome they were headed
|
|
99
103
|
* for anyway, but recovered on the next call instead of never.
|
|
104
|
+
*
|
|
105
|
+
* A task may hand its place in the chain back early by calling `release`:
|
|
106
|
+
* the next request starts while the task finishes on its own. Only the
|
|
107
|
+
* two-tier (Jev) path does, at the point it stops reading the registry and
|
|
108
|
+
* starts waiting on Jev's network answer (`EvaluateHookEventOptions.
|
|
109
|
+
* releaseRegistry`). Without that, every hook on the machine — PostToolUse,
|
|
110
|
+
* UserPromptSubmit, other agents' calls — would queue behind each gated call's
|
|
111
|
+
* round trip, and a slow or unreachable provider would push the calls at the
|
|
112
|
+
* back of the queue past the client's 30 s budget into fail-closed denies.
|
|
113
|
+
* A released task no longer holds the registry, so the deadline it was running
|
|
114
|
+
* under — the one that protects the QUEUE — has done its job. It gets a fresh
|
|
115
|
+
* one of its own instead of none: it is still running, its connection's later
|
|
116
|
+
* replies still queue behind it (see `handleConnection`), and a task that has
|
|
117
|
+
* not settled a minute after handing the queue back is wedged on something
|
|
118
|
+
* that is not going to finish. Its Jev wait is bounded by the configured
|
|
119
|
+
* timeout, so nothing on the intended path comes close.
|
|
120
|
+
*
|
|
121
|
+
* Released tasks therefore run concurrently with each other and with the
|
|
122
|
+
* queue: two gated calls can be waiting on Jev at once (T5's throttle shares
|
|
123
|
+
* one cache and token bucket across them, and does not merge identical
|
|
124
|
+
* in-flight requests). Replies on one connection still leave in request order
|
|
125
|
+
* (see `handleConnection`).
|
|
100
126
|
*/
|
|
101
127
|
const WEDGED_EXIT_CODE = 75;
|
|
128
|
+
/**
|
|
129
|
+
* A released task's own deadline, counted from the moment it handed the queue
|
|
130
|
+
* back. Its Jev wait is bounded by the configured timeout (at most 10 s, plus
|
|
131
|
+
* the review's own backstop), so only a task stuck on something else ever
|
|
132
|
+
* reaches this.
|
|
133
|
+
*/
|
|
134
|
+
const RELEASED_TASK_DEADLINE_MS = 60_000;
|
|
135
|
+
|
|
136
|
+
/** What a wedged task costs: this worker, so the supervisor respawns a clean one. */
|
|
137
|
+
function exitWedged(ms: number, released: boolean): void {
|
|
138
|
+
hookLogWarn(
|
|
139
|
+
`worker: a ${released ? "released " : ""}request did not settle within ${ms}ms; ` +
|
|
140
|
+
`exiting so the supervisor can respawn a clean worker`,
|
|
141
|
+
);
|
|
142
|
+
process.exit(WEDGED_EXIT_CODE);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface QueuedTaskOptions {
|
|
146
|
+
taskMs?: number;
|
|
147
|
+
releasedMs?: number;
|
|
148
|
+
onWedged?: (ms: number, released: boolean) => void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Runs one queued task under a wedge deadline, calling `settle` when the next
|
|
153
|
+
* task may start — because this one finished, or because it handed its place
|
|
154
|
+
* back early with the `release` it is given. A released task keeps running
|
|
155
|
+
* under a deadline of its own (see `RELEASED_TASK_DEADLINE_MS`); dropping the
|
|
156
|
+
* deadline at that point would have made every wedge past the release
|
|
157
|
+
* invisible.
|
|
158
|
+
*
|
|
159
|
+
* Exported for the deadlines' own tests: they are a minute long and expiry
|
|
160
|
+
* exits the process, so a test passes its own values and its own `onWedged`.
|
|
161
|
+
*/
|
|
162
|
+
export function runQueuedTask(task: (release: () => void) => Promise<void>, settle: () => void, opts: QueuedTaskOptions = {}): void {
|
|
163
|
+
const onWedged = opts.onWedged ?? exitWedged;
|
|
164
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
165
|
+
let finished = false;
|
|
166
|
+
let started = false;
|
|
167
|
+
const arm = (ms: number, released: boolean) => {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
timer = setTimeout(() => onWedged(ms, released), ms);
|
|
170
|
+
// Never let the deadline itself hold the event loop open.
|
|
171
|
+
timer.unref?.();
|
|
172
|
+
};
|
|
173
|
+
/** The next task starts once, whether this one released first or just finished. */
|
|
174
|
+
const startNext = () => {
|
|
175
|
+
if (started) return;
|
|
176
|
+
started = true;
|
|
177
|
+
settle();
|
|
178
|
+
};
|
|
179
|
+
const finish = () => {
|
|
180
|
+
finished = true;
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
startNext();
|
|
183
|
+
};
|
|
184
|
+
const release = () => {
|
|
185
|
+
if (finished) return;
|
|
186
|
+
arm(opts.releasedMs ?? RELEASED_TASK_DEADLINE_MS, true);
|
|
187
|
+
startNext();
|
|
188
|
+
};
|
|
189
|
+
arm(opts.taskMs ?? TASK_DEADLINE_MS, false);
|
|
190
|
+
void task(release).then(finish, finish);
|
|
191
|
+
}
|
|
102
192
|
|
|
103
193
|
let processingChain: Promise<void> = Promise.resolve();
|
|
104
|
-
function enqueue(task: () => Promise<void>): void {
|
|
105
|
-
processingChain = processingChain
|
|
106
|
-
.then(
|
|
107
|
-
() =>
|
|
108
|
-
new Promise<void>((settle) => {
|
|
109
|
-
const timer = setTimeout(() => {
|
|
110
|
-
hookLogWarn(
|
|
111
|
-
`worker: a request did not settle within ${TASK_DEADLINE_MS}ms; ` +
|
|
112
|
-
`exiting so the supervisor can respawn a clean worker`,
|
|
113
|
-
);
|
|
114
|
-
process.exit(WEDGED_EXIT_CODE);
|
|
115
|
-
}, TASK_DEADLINE_MS);
|
|
116
|
-
// Never let the deadline itself hold the event loop open.
|
|
117
|
-
timer.unref?.();
|
|
118
|
-
const done = () => {
|
|
119
|
-
clearTimeout(timer);
|
|
120
|
-
settle();
|
|
121
|
-
};
|
|
122
|
-
void task().then(done, done);
|
|
123
|
-
}),
|
|
124
|
-
)
|
|
125
|
-
.catch(() => {});
|
|
194
|
+
function enqueue(task: (release: () => void) => Promise<void>): void {
|
|
195
|
+
processingChain = processingChain.then(() => new Promise<void>((settle) => runQueuedTask(task, settle))).catch(() => {});
|
|
126
196
|
}
|
|
127
197
|
|
|
128
198
|
function handleConnection(socket: Socket, shutdown: () => void): void {
|
|
129
199
|
let recvBuf = Buffer.alloc(0);
|
|
130
200
|
let declaredLen: number | null = null;
|
|
201
|
+
/**
|
|
202
|
+
* Hook replies leave in the order their requests arrived on this
|
|
203
|
+
* connection. The wire carries no request id, so a client that pipelines
|
|
204
|
+
* several requests on one connection can match replies only by order — which
|
|
205
|
+
* the strictly serialized queue used to guarantee by itself. A two-tier task
|
|
206
|
+
* that releases the queue early (see `enqueue`) can finish after a request
|
|
207
|
+
* that arrived behind it, so each reply waits for every earlier one on its
|
|
208
|
+
* connection. The Rust supervisor opens one connection per request, so today
|
|
209
|
+
* this never delays anything; it keeps the ordering true for any client that
|
|
210
|
+
* does pipeline.
|
|
211
|
+
*/
|
|
212
|
+
let replies: Promise<void> = Promise.resolve();
|
|
131
213
|
|
|
132
214
|
socket.on("data", (chunk: Buffer) => {
|
|
133
215
|
recvBuf = Buffer.concat([recvBuf, chunk]);
|
|
@@ -178,15 +260,27 @@ function handleConnection(socket: Socket, shutdown: () => void): void {
|
|
|
178
260
|
}
|
|
179
261
|
const request = message;
|
|
180
262
|
|
|
181
|
-
|
|
263
|
+
let deliver!: (frame: Buffer) => void;
|
|
264
|
+
const reply = new Promise<Buffer>((resolveReply) => {
|
|
265
|
+
deliver = resolveReply;
|
|
266
|
+
});
|
|
267
|
+
replies = replies
|
|
268
|
+
.then(() => reply)
|
|
269
|
+
.then((frame) => {
|
|
270
|
+
socket.write(frame);
|
|
271
|
+
})
|
|
272
|
+
.catch(() => {});
|
|
273
|
+
|
|
274
|
+
enqueue(async (release) => {
|
|
182
275
|
try {
|
|
183
276
|
const result = await evaluateHookEvent(request.hookEvent, request.cli, request.stdin, {
|
|
184
277
|
awaitTelemetryFlush: false,
|
|
185
278
|
// Normalised here so no consumer has to know the wire spells
|
|
186
279
|
// "absent" as null.
|
|
187
280
|
fallbackCwd: request.cwd ?? undefined,
|
|
281
|
+
releaseRegistry: release,
|
|
188
282
|
});
|
|
189
|
-
|
|
283
|
+
deliver(
|
|
190
284
|
encodeFrame({
|
|
191
285
|
type: "hookResult",
|
|
192
286
|
exitCode: result.exitCode,
|
|
@@ -198,7 +292,7 @@ function handleConnection(socket: Socket, shutdown: () => void): void {
|
|
|
198
292
|
} catch (err) {
|
|
199
293
|
const msg = err instanceof Error ? err.message : String(err);
|
|
200
294
|
hookLogWarn(`worker: evaluateHookEvent threw: ${msg}`);
|
|
201
|
-
|
|
295
|
+
deliver(encodeFrame({ type: "error", message: msg }));
|
|
202
296
|
}
|
|
203
297
|
});
|
|
204
298
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export {
|
|
8
8
|
customPolicies,
|
|
9
|
+
semanticPolicies,
|
|
9
10
|
getCustomHooks,
|
|
11
|
+
getSemanticRegistrations,
|
|
10
12
|
clearCustomHooks,
|
|
11
13
|
} from "./hooks/custom-hooks-registry";
|
|
12
14
|
export { allow, deny, instruct } from "./hooks/policy-helpers";
|
|
@@ -16,4 +18,8 @@ export type {
|
|
|
16
18
|
CustomHook,
|
|
17
19
|
PolicyDecision,
|
|
18
20
|
PolicyFunction,
|
|
21
|
+
PolicyAuthority,
|
|
22
|
+
SemanticPolicyDeclaration,
|
|
23
|
+
SemanticProbeDeclaration,
|
|
24
|
+
SemanticToolClass,
|
|
19
25
|
} from "./hooks/policy-types";
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[24868,(e,t,r)=>{t.exports=e.x("fs/promises",()=>require("fs/promises"))},12714,(e,t,r)=>{t.exports=e.x("node:fs/promises",()=>require("node:fs/promises"))},46786,(e,t,r)=>{t.exports=e.x("os",()=>require("os"))},14747,(e,t,r)=>{t.exports=e.x("path",()=>require("path"))},71809,e=>{"use strict";async function t(e,t){let r=Array(e.length),i=0;async function n(){for(;i<e.length;){let t=i++;try{r[t]={status:"fulfilled",value:await e[t]()}}catch(e){r[t]={status:"rejected",reason:e}}}}let a=Array.from({length:Math.min(t,e.length)},()=>n());return await Promise.all(a),r}e.s(["batchAll",0,t])},28557,e=>{"use strict";e.s(["formatDate",0,function(e){return new Intl.DateTimeFormat("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit",hour12:!0}).format(e)}])},54214,67405,e=>{"use strict";var t=e.i(24868),r=e.i(14747),i=e.i(81580),n=e.i(56936);async function a(e,i,n,a){let o=`agent-${a}.jsonl`;for(let a of[(0,r.join)(e,i,o),(0,r.join)(e,i,n,o),(0,r.join)(e,i,n,"subagents",o)])if(!(0,r.relative)(e,a).startsWith(".."))try{return await (0,t.access)(a),a}catch(e){if("ENOENT"===e.code)continue;break}return null}var o=e.i(83534),s=e.i(71809),l=e.i(28557);function u(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let r=Math.round(e/1e3);if(r<3600){let e=Math.floor(r/60);return`${e}m ${r%60}s`}let i=Math.round(e/6e4),n=Math.floor(i/60);return`${n}h ${i%60}m`}function c(e){let t=(0,l.formatDate)(e),r=e.getMilliseconds().toString().padStart(3,"0");return`${t}.${r}`}function f(e,t,r,i){return{_source:i,uuid:e.uuid||"",parentUuid:e.parentUuid??null,timestamp:t,timestampMs:r.getTime(),timestampFormatted:c(r)}}async function m(e,t){let r=e.split("\n").filter(e=>""!==e.trim()),i=new Map,n=[],a=[],o=new Set,s=!1;for(let e=0;e<r.length;e++){let l;e>0&&e%200==0&&await new Promise(e=>setImmediate(e));let u=r[e];try{l=JSON.parse(u)}catch{continue}let c={...l,_source:t};a.push(c);let m=l.type,d=l.timestamp;if(!d)continue;let p=new Date(d),g=p.getTime();if("user"===m){let e=l.message;if(Array.isArray(e?.content)){let t=e.content;if(t.some(e=>"tool_result"===e.type)){let e=l.toolUseResult,r="string"==typeof e?.agentId?e.agentId:void 0;for(let e of(r&&/^[a-f0-9]+$/.test(r)&&o.add(r),t)){if("tool_result"!==e.type)continue;let t=e.tool_use_id;if(!t)continue;let{text:n,images:a}=function(e){let t=e.content;if("string"==typeof t)return{text:t};if(Array.isArray(t)){let e=t.filter(e=>"text"===e.type).map(e=>e.text),r=t.filter(e=>"image"===e.type).map(e=>{let t=e.source;return{base64:t.data,mediaType:t.media_type}}).filter(e=>e.base64&&e.mediaType);return{text:e.length>0?e.join("\n"):void 0,images:r.length>0?r:void 0}}return{}}(e);i.set(t,{timestamp:d,timestampMs:g,content:n,images:a,agentId:r})}continue}}let r="string"==typeof e?.content?e.content:"";n.push({type:"user",...f(l,d,p,t),message:{role:"user",content:r}});continue}if("assistant"===m){let e=l.message,r=[];if(Array.isArray(e?.content)&&(r=e.content.filter(e=>["text","tool_use","thinking"].includes(e.type)).map(e=>{if("text"===e.type)return{type:"text",text:e.text};if("tool_use"===e.type){let t=e.input;return{type:"tool_use",id:e.id,name:e.name,input:e.input??{},..."Task"===e.name&&t?{subagentType:t.subagent_type,subagentDescription:t.description}:{}}}return{type:"thinking",thinking:e.thinking,signature:e.signature}})),0===r.length)continue;n.push({type:"assistant",...f(l,d,p,t),message:{role:"assistant",content:r,model:e?.model}});continue}if("file-history-snapshot"===m||"progress"===m||"system"===m){n.push({type:m,...f(l,d,p,t),raw:{...l}});continue}if("queue-operation"===m){let e=s?"Session Resumed":"Session Started";s=!0,n.push({type:"queue-operation",...f(l,d,p,t),label:e});continue}}let l=0;for(let e of n)if("assistant"===e.type)for(let t of(++l%200==0&&await new Promise(e=>setImmediate(e)),e.message.content)){if("tool_use"!==t.type)continue;let r=i.get(t.id);if(!r)continue;let n=new Date(r.timestamp),a=Math.max(0,r.timestampMs-e.timestampMs);t.result={timestamp:r.timestamp,timestampFormatted:c(n),content:r.content,images:r.images,durationMs:a,durationFormatted:u(a)},r.agentId&&(t.subagentId=r.agentId)}return n.length>500&&await new Promise(e=>setImmediate(e)),n.sort((e,t)=>e.timestampMs-t.timestampMs),{entries:n,rawLines:a,subagentIds:Array.from(o)}}async function d(e,t="session"){return(await m(e,t)).entries}async function p(e,o){let l=(0,n.resolveProjectPath)(e),u=(0,i.getClaudeProjectsPath)(),c=(0,r.join)(l,`${o}.jsonl`),f=await (0,t.readFile)(c,"utf-8"),{entries:d,rawLines:p,subagentIds:g}=await m(f,"session");if(0===g.length)return{entries:d,rawLines:p,subagentIds:[]};let h=await (0,s.batchAll)(g.map(r=>async()=>{let i=`agent-${r}`,n=await a(u,e,o,r);if(!n)return null;let s=await (0,t.readFile)(n,"utf-8"),{entries:l,rawLines:c}=await m(s,i);return{entries:l,rawLines:c}}),4),y=[...d],w=[...p];for(let e of h)"fulfilled"===e.status&&e.value&&(y.push(...e.value.entries),w.push(...e.value.rawLines));return y.length>500&&await new Promise(e=>setImmediate(e)),y.sort((e,t)=>e.timestampMs-t.timestampMs),{entries:y,rawLines:w,subagentIds:g}}e.s(["formatDuration",0,u],67405),(0,o.runtimeCache)((e,t)=>p(e,t),60,{maxSize:50}),e.s(["baseEntry",0,f,"formatTimestamp",0,c,"parseLogContent",0,d,"parseRawLines",0,function(e,t){return e.split("\n").filter(e=>""!==e.trim()).flatMap(e=>{try{let r=JSON.parse(e);return void 0!==t&&(r._source=t),[r]}catch{return[]}})}],54214)},40911,e=>{"use strict";let t={info:0,warn:1,error:2};function r(e){return t[e]>=t.warn}function i(e){return`[failproofai${new Date().toISOString()}] ${e}`}e.s(["logActivity",0,function(e,t,n){if(!r("info"))return;let a=[`user=${e}`,`action=${t}`];n&&a.push(n),console.log(i("ACTIVITY"),a.join(" "))},"logError",0,function(e,t){void 0!==t?console.error(i("ERROR"),e,t):console.error(i("ERROR"),e)},"logWarn",0,function(e,t){r("warn")&&(void 0!==t?console.warn(i("WARN"),e,t):console.warn(i("WARN"),e))}])},81580,e=>{"use strict";var t=e.i(46786),r=e.i(14747);e.s(["decodeFolderName",0,function(e){return/^[A-Za-z]--/.test(e)?e[0]+":/"+e.slice(3).replace(/-/g,"/"):e.replace(/-/g,"/")},"encodeFolderName",0,function(e){let t=/^([A-Za-z]):[\\/](.*)$/.exec(e);return t?t[1]+"--"+t[2].replace(/[\\/]/g,"-"):e.replace(/[\\/]/g,"-")},"getClaudeProjectsPath",0,function(){let e=process.env.CLAUDE_PROJECTS_PATH;return e||(0,r.join)((0,t.homedir)(),".claude","projects")}])},56936,e=>{"use strict";var t=e.i(12714),r=e.i(50227),i=e.i(81580),n=e.i(83534),a=e.i(71809),o=e.i(40911),s=e.i(28557);let l=/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;async function u(e,r){try{return(await (0,t.stat)(e)).mtime}catch(e){return(0,o.logWarn)(`Failed to stat ${r}:`,e),new Date(0)}}async function c(e){try{if(!(await (0,t.stat)(e)).isDirectory())return null;return await (0,t.readdir)(e,{withFileTypes:!0})}catch{return null}}async function f(){try{let e=(0,i.getClaudeProjectsPath)(),t=await c(e);if(!t)return[];let n=(await (0,a.batchAll)(t.filter(e=>e.isDirectory()).map(t=>async()=>{let i=(0,r.join)(e,t.name),n=await u(i,t.name);return{name:t.name,path:i,isDirectory:!0,lastModified:n,lastModifiedFormatted:(0,s.formatDate)(n),cli:["claude"]}}),16)).filter(e=>"fulfilled"===e.status).map(e=>e.value);return n.sort((e,t)=>t.lastModified.getTime()-e.lastModified.getTime()),n}catch(e){return(0,o.logError)("Error reading Claude project folders:",e),[]}}async function m(){let[{getCodexProjects:t},{getCopilotProjects:r},{getCursorProjects:i},{getOpenCodeProjects:n},{getPiProjects:a},{getHermesProjects:s},{getOpenClawProjects:l},{getFactoryProjects:u},{getDevinProjects:c},{getAntigravityProjects:m},{getGooseProjects:d}]=await Promise.all([e.A(37352),e.A(5807),e.A(97452),e.A(82978),e.A(45093),e.A(67347),e.A(38978),e.A(62998),e.A(52400),e.A(91301),e.A(31854)]),[p,g,h,y,w,E,j,M,A,v,x,P]=await Promise.all([f(),t().catch(e=>((0,o.logError)("Error reading Codex projects:",e),[])),r().catch(e=>((0,o.logError)("Error reading Copilot projects:",e),[])),i().catch(e=>((0,o.logError)("Error reading Cursor projects:",e),[])),n().catch(e=>((0,o.logError)("Error reading OpenCode projects:",e),[])),a().catch(e=>((0,o.logError)("Error reading Pi projects:",e),[])),s().catch(e=>((0,o.logError)("Error reading Hermes projects:",e),[])),l().catch(e=>((0,o.logError)("Error reading OpenClaw projects:",e),[])),u().catch(e=>((0,o.logError)("Error reading Factory projects:",e),[])),c().catch(e=>((0,o.logError)("Error reading Devin projects:",e),[])),m().catch(e=>((0,o.logError)("Error reading Antigravity projects:",e),[])),d().catch(e=>((0,o.logError)("Error reading Goose projects:",e),[]))]);return function(...e){let t=new Map;for(let r of e)for(let e of r){let r=t.get(e.name);if(!r){t.set(e.name,{...e,cli:[...e.cli]});continue}let i=[...r.cli];for(let t of e.cli)i.includes(t)||i.push(t);let n=e.lastModified.getTime()>r.lastModified.getTime()?e:r;t.set(e.name,{...r,cli:i,lastModified:n.lastModified,lastModifiedFormatted:n.lastModifiedFormatted,sessionCount:void 0===r.sessionCount&&void 0===e.sessionCount?void 0:(r.sessionCount??0)+(e.sessionCount??0)})}let r=Array.from(t.values());return r.sort((e,t)=>t.lastModified.getTime()-e.lastModified.getTime()),r}(p,g,h,y,w,E,j,M,A,v,x,P)}function d(e){if(!e)throw RangeError("Empty project name");if(/^[/\\]/.test(e))throw RangeError("Absolute project name");let t=(0,r.resolve)((0,i.getClaudeProjectsPath)()),n=(0,r.resolve)((0,r.join)(t,e));if(!n.startsWith(t+r.sep))throw RangeError("Project path escapes root");return n}function p(e){let t=e.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);return t?t[0]:void 0}async function g(e){try{let t=await c(e);if(!t)return[];let i=t.filter(e=>e.isFile()&&e.name.endsWith(".jsonl")&&p(e.name)),n=(await (0,a.batchAll)(i.map(t=>async()=>{let i=(0,r.join)(e,t.name),n=await u(i,t.name);return{name:t.name,path:i,lastModified:n,lastModifiedFormatted:(0,s.formatDate)(n),sessionId:p(t.name),cli:"claude"}}),16)).filter(e=>"fulfilled"===e.status).map(e=>e.value);return n.sort((e,t)=>t.lastModified.getTime()-e.lastModified.getTime()),n}catch(e){return(0,o.logError)("Error reading session files:",e),[]}}(0,n.runtimeCache)(m,30),(0,n.runtimeCache)(e=>g(e),30),e.s(["UUID_RE",0,l,"resolveProjectPath",0,d,"resolveSessionFilePath",0,function(e,t){if(!l.test(t))throw RangeError("Invalid session ID");let i=d(e);return(0,r.join)(i,`${t}.jsonl`)}])},83534,e=>{"use strict";e.s(["runtimeCache",0,function(e,t,r){let i=new Map,n=new Map,a=r?.maxSize;return async(...r)=>{let o=JSON.stringify(r),s=i.get(o);if(s&&Date.now()<s.expiry)return s.data;let l=n.get(o);if(l)return l;let u=e(...r).then(e=>{if(n.delete(o),a&&i.size>=a){let e=i.keys().next().value;i.delete(e)}return i.set(o,{data:e,expiry:Date.now()+1e3*t}),e},e=>{throw n.delete(o),e});return n.set(o,u),u}}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0cuho4x._.js.map
|