failproofai 1.0.7-beta.2 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- 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 +7 -8
- 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 +5 -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 -13
- 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 +7 -8
- 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 +7 -7
- 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 +6 -7
- 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 +8 -41
- package/.next/standalone/.next/server/app/settings/page.js +9 -12
- 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]__1j-zsg5._.js → [externals]__1_bftcl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__19_pzeq._.js → [externals]__1msfs-h._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l3yhx4._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1bf34x4._.js → [root-of-the-server]__1_r2rbg._.js} +7 -5
- 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/{_1q5i8mb._.js → _1c3k-8x._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +2 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l44ual._.js → [root-of-the-server]__0bd3mje._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1mf3zp6._.js → [root-of-the-server]__0cxe_2_._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1w_5l7t._.js → _0h_douw._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1mel6y1._.js → _1-i_gzc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1v-jvrv._.js → _166t73i._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1q46vxx._.js → _1_qswah._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifp._.js → _1es2j7i._.js} +5 -5
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +3 -0
- 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 +2 -2
- 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_settings-client_tsx_20lq-mq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0w6mzq5._.js → node_modules_next_dist_0drixxt._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.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/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 +23 -56
- package/.next/standalone/.next/static/chunks/094xgi4owxaqf.js +1 -0
- package/.next/standalone/.next/static/chunks/{129ag2bw93bdh.js → 0__8a7m868fvf.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3ugmd_7dyn0id.js → 0o6qlkgubtoex.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0fqd7m_u81mi5.js → 13i7-9is-vhys.js} +1 -1
- package/.next/standalone/.next/static/chunks/1rz20_pz828f3.js +6 -0
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +1 -0
- package/.next/standalone/.next/static/chunks/{2_pltstd8-xgs.js → 2klitrtzpaoe0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3brze37td_wnc.js → 2mdh397ghgnvv.js} +1 -1
- package/.next/standalone/.next/static/chunks/2rshywgeqsyzk.css +2 -0
- package/.next/standalone/.next/static/chunks/3pzx4chkhko9k.js +1 -0
- package/.next/standalone/.next/static/chunks/3rh5o7e16irrm.js +69 -0
- package/.next/standalone/.next/static/chunks/{1qd741hzlmjbo.js → 43ufqrz8qo3h-.js} +1 -1
- package/.next/standalone/SECURITY.md +53 -0
- package/.next/standalone/app/actions/pack-actions.ts +0 -12
- package/.next/standalone/app/policies/hooks-client.tsx +0 -9
- package/.next/standalone/app/settings/page.tsx +1 -20
- package/.next/standalone/app/settings/settings-client.tsx +1 -27
- package/.next/standalone/app/settings/settings.css +0 -79
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/sdk/typescript/CHANGELOG.md +15 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +10 -30
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +3 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +4 -16
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +3 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +13 -132
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +4 -142
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +4 -0
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +1396 -1016
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +9 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +2 -2
- package/bin/failproofai.mjs +2 -115
- package/dist/cli.mjs +6543 -13872
- package/dist/index.js +1 -19
- package/dist/worker.mjs +2022 -8055
- package/package.json +10 -10
- package/pi-extension/index.ts +0 -11
- package/scripts/build-policy-pack.mjs +2 -53
- package/src/audit/features.ts +2 -3
- package/src/hooks/builtin-policies.ts +8 -177
- package/src/hooks/cloud-enrollment-cli.ts +1 -1
- package/src/hooks/cloud-managed-policies.ts +0 -22
- package/src/hooks/custom-hooks-loader.ts +7 -45
- package/src/hooks/custom-hooks-registry.ts +1 -45
- package/src/hooks/first-run-gate.ts +0 -5
- package/src/hooks/fp-home.ts +0 -23
- package/src/hooks/handler.ts +6 -265
- package/src/hooks/hook-activity-store.ts +1 -105
- package/src/hooks/hook-telemetry.ts +0 -41
- package/src/hooks/loader-utils.ts +0 -6
- package/src/hooks/manager.ts +1 -1
- package/src/hooks/pack-cli.ts +19 -412
- package/src/hooks/pack-manifest.ts +7 -479
- package/src/hooks/pack-store.ts +11 -157
- package/src/hooks/policy-catalog.ts +0 -204
- package/src/hooks/policy-evaluator.ts +796 -940
- package/src/hooks/policy-registry.ts +0 -25
- package/src/hooks/policy-types.ts +0 -126
- package/src/hooks/types.ts +1 -1
- package/src/hooks/worker-server.ts +26 -119
- package/src/index.ts +0 -6
- package/.next/standalone/.next/server/chunks/src_hooks_01frwmb._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_18qtd42._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qxnccm._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s_yomn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0soxz2z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +0 -151
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_095a_79._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_15t8kqj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_18k8rl0._.js +0 -12
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1fm2w5z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1j0zy3v._.js +0 -3
- package/.next/standalone/.next/static/chunks/043j99m8ykg__.css +0 -2
- package/.next/standalone/.next/static/chunks/2c8j9l6j_b1ci.js +0 -1
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +0 -1
- package/.next/standalone/.next/static/chunks/3-k569wzcli8q.js +0 -1
- package/.next/standalone/.next/static/chunks/3otmypm6j_xfo.js +0 -6
- package/.next/standalone/.next/static/chunks/3yxro_r2_o9ad.js +0 -69
- package/.next/standalone/PROBE-FOLLOWUP.md +0 -186
- package/.next/standalone/app/actions/get-jev-config.ts +0 -424
- package/.next/standalone/app/actions/update-jev-config.ts +0 -420
- package/.next/standalone/app/components/jev-notices.tsx +0 -96
- package/.next/standalone/app/settings/jev-panel.tsx +0 -473
- package/src/hooks/effective-reviewers.ts +0 -79
- package/src/hooks/jev-activity.ts +0 -385
- package/src/hooks/jev-cli.ts +0 -1249
- package/src/hooks/policy-authority.ts +0 -333
- package/src/hooks/policy-reviewability.ts +0 -229
- package/src/hooks/semantic/combine.ts +0 -541
- package/src/hooks/semantic/compile.ts +0 -176
- package/src/hooks/semantic/decide.ts +0 -392
- package/src/hooks/semantic/envelope.ts +0 -1296
- package/src/hooks/semantic/evaluator.ts +0 -547
- package/src/hooks/semantic/facts.ts +0 -292
- package/src/hooks/semantic/intent.ts +0 -1190
- package/src/hooks/semantic/jev-client.ts +0 -643
- package/src/hooks/semantic/jev-config.ts +0 -594
- package/src/hooks/semantic/jev-review.ts +0 -374
- package/src/hooks/semantic/jev-stats.ts +0 -289
- package/src/hooks/semantic/jev-throttle.ts +0 -421
- package/src/hooks/semantic/pack-policies.ts +0 -251
- package/src/hooks/semantic/policies.ts +0 -596
- package/src/hooks/semantic/precondition-names.ts +0 -60
- package/src/hooks/semantic/preconditions.ts +0 -58
- package/src/hooks/semantic/redact.ts +0 -2910
- package/src/hooks/semantic/types.ts +0 -145
- package/src/hooks/semver-precedence.ts +0 -128
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → PgeWCHmyVbjRznv2VO7KF}/_ssgManifest.js +0 -0
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic facts about a tool call, computed locally before Jev is asked.
|
|
3
|
-
*
|
|
4
|
-
* TypeSafe's own jaggedness notes say Jev cannot count, resolve paths or do
|
|
5
|
-
* arithmetic, and answers the question you wrote rather than the one you
|
|
6
|
-
* meant. So anything with one right answer — which directory `*` expands in
|
|
7
|
-
* after a `cd`, whether a path is inside the project, which branch is checked
|
|
8
|
-
* out — is worked out here and handed to Jev as a fact rather than a question.
|
|
9
|
-
*
|
|
10
|
-
* Everything in this file is linear in the input and never spawns a process:
|
|
11
|
-
* it runs on the hook's hot path, inside the daemon worker's serialised chain.
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
14
|
-
import { homedir } from "node:os";
|
|
15
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
16
|
-
import type { Facts, PathFact, ToolClass } from "./types";
|
|
17
|
-
|
|
18
|
-
const SHELL_TOOLS = new Set(["Bash", "BashOutput"]);
|
|
19
|
-
const WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
20
|
-
const READ_TOOLS = new Set(["Read", "Glob", "Grep", "LS"]);
|
|
21
|
-
const NETWORK_TOOLS = new Set(["WebFetch", "WebSearch"]);
|
|
22
|
-
/** Known tools with no side effects worth judging. They never cost a Jev call. */
|
|
23
|
-
const INERT_TOOLS = new Set([
|
|
24
|
-
"TodoWrite", "TodoRead", "ExitPlanMode", "EnterPlanMode", "Task", "Agent",
|
|
25
|
-
"AskUserQuestion", "ToolSearch", "KillShell", "SlashCommand", "Skill",
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
export function classifyTool(toolName: string): { toolClass: ToolClass; toolIsKnown: boolean } {
|
|
29
|
-
if (SHELL_TOOLS.has(toolName)) return { toolClass: "shell", toolIsKnown: true };
|
|
30
|
-
if (WRITE_TOOLS.has(toolName)) return { toolClass: "write", toolIsKnown: true };
|
|
31
|
-
if (READ_TOOLS.has(toolName)) return { toolClass: "read", toolIsKnown: true };
|
|
32
|
-
if (NETWORK_TOOLS.has(toolName)) return { toolClass: "network", toolIsKnown: true };
|
|
33
|
-
if (INERT_TOOLS.has(toolName)) return { toolClass: "other", toolIsKnown: true };
|
|
34
|
-
// MCP tools (`mcp__server__tool`), skills that shell out, and anything a
|
|
35
|
-
// harness names that no canonical map covers. These are exactly the calls
|
|
36
|
-
// the regex engine never sees, because every builtin opens with a toolName
|
|
37
|
-
// check.
|
|
38
|
-
return { toolClass: "other", toolIsKnown: false };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Upper bound on command text the scanner will look at. Linear, but bounded. */
|
|
42
|
-
export const MAX_SCAN_CHARS = 8_192;
|
|
43
|
-
|
|
44
|
-
export interface ScannedCommand {
|
|
45
|
-
/** Simple-command segments, split on `&&`, `||`, `;`, `|` and newlines. */
|
|
46
|
-
segments: string[][];
|
|
47
|
-
/** The command with shell comments removed (quote-aware). */
|
|
48
|
-
withoutComments: string;
|
|
49
|
-
commentsRemoved: boolean;
|
|
50
|
-
/** The removed comment text, so the injection probe can still see it. */
|
|
51
|
-
comments: string[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* A single left-to-right pass over a shell command: quote-aware tokenising,
|
|
56
|
-
* operator splitting and comment removal. It is deliberately not a full shell
|
|
57
|
-
* parser — it only needs to find words, segment boundaries and comments, and
|
|
58
|
-
* it must stay linear no matter what the agent sends.
|
|
59
|
-
*/
|
|
60
|
-
export function scanCommand(command: string): ScannedCommand {
|
|
61
|
-
const text = command.length > MAX_SCAN_CHARS ? command.slice(0, MAX_SCAN_CHARS) : command;
|
|
62
|
-
const segments: string[][] = [];
|
|
63
|
-
let tokens: string[] = [];
|
|
64
|
-
let word = "";
|
|
65
|
-
let inWord = false;
|
|
66
|
-
let quote: "'" | '"' | null = null;
|
|
67
|
-
let out = "";
|
|
68
|
-
let commentsRemoved = false;
|
|
69
|
-
const comments: string[] = [];
|
|
70
|
-
|
|
71
|
-
const endWord = () => {
|
|
72
|
-
if (inWord) tokens.push(word);
|
|
73
|
-
word = "";
|
|
74
|
-
inWord = false;
|
|
75
|
-
};
|
|
76
|
-
const endSegment = () => {
|
|
77
|
-
endWord();
|
|
78
|
-
if (tokens.length > 0) segments.push(tokens);
|
|
79
|
-
tokens = [];
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
for (let i = 0; i < text.length; i++) {
|
|
83
|
-
const c = text[i];
|
|
84
|
-
if (quote) {
|
|
85
|
-
out += c;
|
|
86
|
-
if (c === quote) {
|
|
87
|
-
quote = null;
|
|
88
|
-
} else if (c === "\\" && quote === '"' && i + 1 < text.length) {
|
|
89
|
-
word += text[++i];
|
|
90
|
-
out += text[i];
|
|
91
|
-
} else {
|
|
92
|
-
word += c;
|
|
93
|
-
}
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (c === "'" || c === '"') {
|
|
97
|
-
quote = c;
|
|
98
|
-
inWord = true;
|
|
99
|
-
out += c;
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
if (c === "\\" && i + 1 < text.length) {
|
|
103
|
-
word += text[i + 1];
|
|
104
|
-
inWord = true;
|
|
105
|
-
out += c + text[++i];
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (c === "#" && !inWord) {
|
|
109
|
-
// A comment runs to end of line. Classic place to hide "approved by
|
|
110
|
-
// security" in a command that is about to be judged by a language model.
|
|
111
|
-
const nl = text.indexOf("\n", i);
|
|
112
|
-
commentsRemoved = true;
|
|
113
|
-
comments.push(text.slice(i, nl === -1 ? text.length : nl));
|
|
114
|
-
if (nl === -1) break;
|
|
115
|
-
i = nl - 1;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (c === "\n" || c === ";") {
|
|
119
|
-
endSegment();
|
|
120
|
-
out += c;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (c === "&" || c === "|") {
|
|
124
|
-
endSegment();
|
|
125
|
-
out += c;
|
|
126
|
-
if (text[i + 1] === c) out += text[++i];
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (c === " " || c === "\t") {
|
|
130
|
-
endWord();
|
|
131
|
-
out += c;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
word += c;
|
|
135
|
-
inWord = true;
|
|
136
|
-
out += c;
|
|
137
|
-
}
|
|
138
|
-
endSegment();
|
|
139
|
-
return { segments, withoutComments: out.trimEnd(), commentsRemoved, comments };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const PATH_LIKE = /^(?:\/|~|\.\.?(?:\/|$)|[^:]*\/)/;
|
|
143
|
-
const GLOB_CHARS = /[*?[]/;
|
|
144
|
-
|
|
145
|
-
function expandHome(token: string, home: string): string {
|
|
146
|
-
if (token === "~") return home;
|
|
147
|
-
if (token.startsWith("~/")) return home + token.slice(1);
|
|
148
|
-
if (token.startsWith("$HOME/")) return home + token.slice(5);
|
|
149
|
-
if (token === "$HOME") return home;
|
|
150
|
-
return token;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function relationOf(resolved: string, projectRoot: string | null, home: string): PathFact["relation"] {
|
|
154
|
-
if (resolved === "/") return "root";
|
|
155
|
-
if (projectRoot) {
|
|
156
|
-
if (resolved === projectRoot) return "project_root";
|
|
157
|
-
if (resolved.startsWith(projectRoot + "/")) return "inside_project";
|
|
158
|
-
}
|
|
159
|
-
if (resolved === home) return "home_root";
|
|
160
|
-
if (resolved.startsWith(home + "/")) return "outside_project_in_home";
|
|
161
|
-
return "system";
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function pathFact(token: string, base: string, projectRoot: string | null, home: string): PathFact | null {
|
|
165
|
-
if (token.includes("://")) return null;
|
|
166
|
-
let t = expandHome(token, home);
|
|
167
|
-
// A glob names the contents of its directory: `*` after `cd /` is the root.
|
|
168
|
-
if (GLOB_CHARS.test(t)) {
|
|
169
|
-
const slash = t.lastIndexOf("/");
|
|
170
|
-
t = slash === -1 ? "." : slash === 0 ? "/" : t.slice(0, slash);
|
|
171
|
-
}
|
|
172
|
-
const resolved = isAbsolute(t) ? resolve(t) : resolve(base, t);
|
|
173
|
-
return { asWritten: token, resolved, relation: relationOf(resolved, projectRoot, home) };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const MAX_PATHS = 12;
|
|
177
|
-
|
|
178
|
-
/** Paths a tool call touches, resolved against the cwd and any `cd` along the way. */
|
|
179
|
-
export function extractPaths(
|
|
180
|
-
toolInput: Record<string, unknown>,
|
|
181
|
-
cwd: string | null,
|
|
182
|
-
projectRoot: string | null,
|
|
183
|
-
scanned: ScannedCommand | null,
|
|
184
|
-
home: string = homedir(),
|
|
185
|
-
): PathFact[] {
|
|
186
|
-
const base0 = cwd ?? home;
|
|
187
|
-
const facts: PathFact[] = [];
|
|
188
|
-
const seen = new Set<string>();
|
|
189
|
-
const add = (f: PathFact | null) => {
|
|
190
|
-
if (!f || seen.has(f.resolved + "\0" + f.asWritten) || facts.length >= MAX_PATHS) return;
|
|
191
|
-
seen.add(f.resolved + "\0" + f.asWritten);
|
|
192
|
-
facts.push(f);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
for (const key of ["file_path", "path", "notebook_path"]) {
|
|
196
|
-
const v = toolInput[key];
|
|
197
|
-
if (typeof v === "string" && v.length > 0) add(pathFact(v, base0, projectRoot, home));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (scanned) {
|
|
201
|
-
let base = base0;
|
|
202
|
-
for (const seg of scanned.segments) {
|
|
203
|
-
if (seg[0] === "cd") {
|
|
204
|
-
// A `cd` does two things and only one of them used to be recorded. Its
|
|
205
|
-
// target rebases every relative path after it — that is the `base`
|
|
206
|
-
// assignment, unchanged — but it is also a directory the call names in
|
|
207
|
-
// its own right. Consuming it silently left `facts.paths` empty on
|
|
208
|
-
// `cd ../other-repo && ./node_modules/.bin/tsc`, while the regex
|
|
209
|
-
// partner reads the same text, resolves the target against the session
|
|
210
|
-
// cwd and denies it. A deny whose paired question was never asked can
|
|
211
|
-
// never clear, so the target is emitted as a path too.
|
|
212
|
-
const written = seg[1];
|
|
213
|
-
const f = pathFact(written ?? "~", base, projectRoot, home);
|
|
214
|
-
if (!f) continue;
|
|
215
|
-
base = f.resolved;
|
|
216
|
-
// Only when the agent wrote a target. A bare `cd` (or `cd -`) leaves
|
|
217
|
-
// the partner no token to deny, so there is no verdict to pair with,
|
|
218
|
-
// and naming a directory nobody wrote would ask about it for nothing.
|
|
219
|
-
if (written !== undefined && !written.startsWith("-")) add(f);
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
// Skip argv[0]: `/usr/local/bin/kubectl` is the program, not its target.
|
|
223
|
-
for (const tok of seg.slice(1)) {
|
|
224
|
-
if (tok.startsWith("-")) continue;
|
|
225
|
-
if (tok === "*" || tok === "." || tok === ".." || PATH_LIKE.test(tok) || tok.startsWith("$HOME")) {
|
|
226
|
-
add(pathFact(tok, base, projectRoot, home));
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return facts;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function findGitDir(start: string): string | null {
|
|
235
|
-
let dir = start;
|
|
236
|
-
for (let i = 0; i < 40; i++) {
|
|
237
|
-
const candidate = resolve(dir, ".git");
|
|
238
|
-
if (existsSync(candidate)) return candidate;
|
|
239
|
-
const parent = dirname(dir);
|
|
240
|
-
if (parent === dir) return null;
|
|
241
|
-
dir = parent;
|
|
242
|
-
}
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export function findProjectRoot(cwd: string | null): string | null {
|
|
247
|
-
if (!cwd) return null;
|
|
248
|
-
const gitDir = findGitDir(cwd);
|
|
249
|
-
return gitDir ? dirname(gitDir) : cwd;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/** Current branch from `.git/HEAD`, following a worktree's `gitdir:` pointer. No subprocess. */
|
|
253
|
-
export function readCurrentBranch(cwd: string | null): string | null {
|
|
254
|
-
if (!cwd) return null;
|
|
255
|
-
try {
|
|
256
|
-
const gitPath = findGitDir(cwd);
|
|
257
|
-
if (!gitPath) return null;
|
|
258
|
-
let headFile = resolve(gitPath, "HEAD");
|
|
259
|
-
if (statSync(gitPath).isFile()) {
|
|
260
|
-
const pointer = readFileSync(gitPath, "utf8").trim();
|
|
261
|
-
const m = /^gitdir:\s*(.+)$/.exec(pointer);
|
|
262
|
-
if (!m) return null;
|
|
263
|
-
headFile = resolve(dirname(gitPath), m[1], "HEAD");
|
|
264
|
-
}
|
|
265
|
-
const head = readFileSync(headFile, "utf8").trim();
|
|
266
|
-
const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
|
|
267
|
-
return ref ? ref[1] : null;
|
|
268
|
-
} catch {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export function computeFacts(
|
|
274
|
-
toolName: string,
|
|
275
|
-
toolInput: Record<string, unknown>,
|
|
276
|
-
cwd: string | null,
|
|
277
|
-
permissionMode: string | null,
|
|
278
|
-
scanned: ScannedCommand | null,
|
|
279
|
-
): Facts {
|
|
280
|
-
const { toolClass, toolIsKnown } = classifyTool(toolName);
|
|
281
|
-
const projectRoot = findProjectRoot(cwd);
|
|
282
|
-
return {
|
|
283
|
-
toolName,
|
|
284
|
-
toolClass,
|
|
285
|
-
toolIsKnown,
|
|
286
|
-
cwd,
|
|
287
|
-
projectRoot,
|
|
288
|
-
currentGitBranch: readCurrentBranch(cwd),
|
|
289
|
-
paths: extractPaths(toolInput, cwd, projectRoot, scanned),
|
|
290
|
-
permissionMode,
|
|
291
|
-
};
|
|
292
|
-
}
|