failproofai 1.0.7-beta.2 → 1.0.8-beta.0
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.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.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 +5 -5
- 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 +4 -4
- 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.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 +6 -7
- 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.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 +2 -2
- 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.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 +18 -7
- package/.next/standalone/.next/server/app/settings/page.js +9 -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]__1j-zsg5._.js → [externals]__20kzpkf._.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]__1ttrwnd._.js +22 -0
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +20 -20
- 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 +9 -9
- package/.next/standalone/.next/server/chunks/_1q5i8mb._.js +1 -1
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[externals]__0ohnuzs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0qxnccm._.js → [root-of-the-server]__06pflha._.js} +2 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0soxz2z._.js → [root-of-the-server]__12e7nhs._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l44ual._.js → [root-of-the-server]__14o3ek1._.js} +2 -2
- 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]__1m_svbe._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1v-jvrv._.js → _0o4xkpl._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1mel6y1._.js → _1-7sqrb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0aiy-os._.js +1 -1
- 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 +12 -0
- 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 +33 -22
- package/.next/standalone/.next/static/chunks/078gnymqoh3r4.js +1 -0
- package/.next/standalone/.next/static/chunks/{3brze37td_wnc.js → 0ahfwmbkpgfw4.js} +1 -1
- package/.next/standalone/.next/static/chunks/{043j99m8ykg__.css → 0bhidk90e-07f.css} +1 -1
- package/.next/standalone/.next/static/chunks/1bu2-nv59ed6i.js +1 -0
- package/.next/standalone/.next/static/chunks/{3ugmd_7dyn0id.js → 1v_tp3hm8wyhe.js} +1 -1
- package/.next/standalone/.next/static/chunks/2a405_e1o26ol.js +1 -0
- package/.next/standalone/.next/static/chunks/{3yxro_r2_o9ad.js → 2vo7qbdbvnc0o.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0fqd7m_u81mi5.js → 2z7i-yg59w4pn.js} +1 -1
- package/.next/standalone/.next/static/chunks/36uhh9el_oz8e.js +1 -0
- package/.next/standalone/.next/static/chunks/{129ag2bw93bdh.js → 3bgot5v6f6s3c.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3otmypm6j_xfo.js → 3o3f1ibfci0p7.js} +2 -2
- package/.next/standalone/SECURITY.md +53 -0
- package/.next/standalone/app/actions/get-jev-config.ts +194 -14
- package/.next/standalone/app/actions/update-jev-config.ts +160 -11
- package/.next/standalone/app/policies/hooks-client.tsx +5 -3
- package/.next/standalone/app/settings/jev-panel.tsx +169 -12
- 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 +9 -9
- 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 +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/sdk/typescript/scripts/release.mjs +30 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +1 -0
- package/bin/failproofai.mjs +37 -6
- package/dist/cli.mjs +2861 -1145
- package/dist/worker.mjs +815 -197
- package/package.json +9 -9
- package/src/hooks/builtin-policies.ts +24 -6
- package/src/hooks/cloud-connection.ts +190 -1
- package/src/hooks/cloud-enrollment-cli.ts +56 -7
- package/src/hooks/cloud-introspect.ts +6 -0
- package/src/hooks/configure-wizard.ts +26 -6
- package/src/hooks/custom-hooks-loader.ts +58 -8
- package/src/hooks/effective-reviewers.ts +172 -8
- package/src/hooks/flush-cli.ts +35 -8
- package/src/hooks/fp-config.ts +266 -1
- package/src/hooks/fp-reset.ts +22 -4
- package/src/hooks/handler.ts +44 -7
- package/src/hooks/hook-activity-store.ts +13 -5
- package/src/hooks/jev-cli.ts +1009 -86
- package/src/hooks/jev-cloud-connection.ts +392 -0
- package/src/hooks/manager.ts +44 -6
- package/src/hooks/pack-cli.ts +175 -37
- package/src/hooks/pack-failclosed.ts +3 -0
- package/src/hooks/pack-manifest.ts +11 -1
- package/src/hooks/pack-store.ts +47 -10
- package/src/hooks/policy-authority.ts +73 -20
- package/src/hooks/policy-catalog.ts +10 -8
- package/src/hooks/policy-evaluator.ts +31 -8
- package/src/hooks/policy-registry.ts +3 -2
- package/src/hooks/policy-reviewability.ts +41 -12
- package/src/hooks/policy-types.ts +5 -3
- package/src/hooks/semantic/combine.ts +124 -84
- package/src/hooks/semantic/decide.ts +16 -0
- package/src/hooks/semantic/envelope.ts +18 -59
- package/src/hooks/semantic/evaluator.ts +11 -2
- package/src/hooks/semantic/facts.ts +7 -1
- package/src/hooks/semantic/intent.ts +8 -8
- package/src/hooks/semantic/jev-client.ts +540 -28
- package/src/hooks/semantic/jev-config.ts +586 -38
- package/src/hooks/semantic/jev-review.ts +26 -11
- package/src/hooks/semantic/jev-throttle.ts +8 -2
- package/src/hooks/semantic/pack-policies.ts +58 -15
- package/src/hooks/semantic/policies.ts +4 -3
- package/src/hooks/semantic/redact.ts +28 -1
- package/src/hooks/semantic/session-root.ts +116 -0
- package/src/hooks/semantic/types.ts +11 -0
- package/src/hooks/tui.ts +4 -0
- package/src/hooks/worker-server.ts +2 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bf34x4._.js +0 -22
- 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]__0s_yomn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_1w_5l7t._.js +0 -3
- 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/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +0 -3
- package/.next/standalone/.next/static/chunks/1qd741hzlmjbo.js +0 -1
- package/.next/standalone/.next/static/chunks/2_pltstd8-xgs.js +0 -1
- package/.next/standalone/.next/static/chunks/2c8j9l6j_b1ci.js +0 -1
- package/.next/standalone/.next/static/chunks/3-k569wzcli8q.js +0 -1
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → O_b5R1axf5cb4NxIXq2kS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → O_b5R1axf5cb4NxIXq2kS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{gbEOjBgZAxF2UIUwZVHNu → O_b5R1axf5cb4NxIXq2kS}/_ssgManifest.js +0 -0
package/dist/worker.mjs
CHANGED
|
@@ -378,7 +378,7 @@ function failproofaiHome(home) {
|
|
|
378
378
|
return resolve(home, ".failproofai");
|
|
379
379
|
return process.env.FAILPROOFAI_HOME || resolve(homedir(), ".failproofai");
|
|
380
380
|
}
|
|
381
|
-
var LAYOUT_VERSION = 4, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), jevConfigFile = (home) => atHome(home, "jev.json"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), packsDir = (home) => resolve(policiesDir(home), "packs"), packsInstalledFile = (home) => resolve(packsDir(home), "installed.json"), hookActivityDir = (home) => atHome(home, "hook-activity"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), sessionPauseDir = () => resolve(stateDir(), "sessions"), semanticDir = () => resolve(stateDir(), "semantic"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
|
|
381
|
+
var LAYOUT_VERSION = 4, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), jevConfigFile = (home) => atHome(home, "jev.json"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), packsDir = (home) => resolve(policiesDir(home), "packs"), packsInstalledFile = (home) => resolve(packsDir(home), "installed.json"), hookActivityDir = (home) => atHome(home, "hook-activity"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), sessionPauseDir = () => resolve(stateDir(), "sessions"), semanticDir = () => resolve(stateDir(), "semantic"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
|
|
382
382
|
var init_fp_home = __esm(() => {
|
|
383
383
|
customPoliciesDir = policiesDir;
|
|
384
384
|
legacy = {
|
|
@@ -1162,7 +1162,7 @@ function instruct(reason) {
|
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
// package.json
|
|
1165
|
-
var version = "1.0.
|
|
1165
|
+
var version = "1.0.8-beta.0";
|
|
1166
1166
|
var init_package = () => {};
|
|
1167
1167
|
|
|
1168
1168
|
// src/hooks/policy-types.ts
|
|
@@ -1179,7 +1179,7 @@ function effectiveAuthority(p) {
|
|
|
1179
1179
|
|
|
1180
1180
|
// src/hooks/policy-authority.ts
|
|
1181
1181
|
import { existsSync as existsSync3 } from "node:fs";
|
|
1182
|
-
function resolvePolicyAuthority(decl, knownReviewers = SEMANTIC_REVIEWER_NAMES) {
|
|
1182
|
+
function resolvePolicyAuthority(decl, knownReviewers = SEMANTIC_REVIEWER_NAMES, contested) {
|
|
1183
1183
|
const declaredReviewable = decl?.authority === "reviewable";
|
|
1184
1184
|
if (!decl || effectiveAuthority(decl) === "hard") {
|
|
1185
1185
|
if (!declaredReviewable)
|
|
@@ -1192,13 +1192,21 @@ function resolvePolicyAuthority(decl, knownReviewers = SEMANTIC_REVIEWER_NAMES)
|
|
|
1192
1192
|
}
|
|
1193
1193
|
const unknown = names.filter((n) => !knownReviewers.has(n));
|
|
1194
1194
|
if (unknown.length > 0) {
|
|
1195
|
-
return {
|
|
1196
|
-
authority: "hard",
|
|
1197
|
-
downgraded: `reviewedBy names ${unknown.map((n) => JSON.stringify(n)).join(", ")}, ` + `which ${unknown.length === 1 ? "is not a semantic policy" : "are not semantic policies"} in this build`
|
|
1198
|
-
};
|
|
1195
|
+
return { authority: "hard", downgraded: `reviewedBy names ${unknown.map((n) => whyUnknown(n, knownReviewers, contested)).join("; ")}` };
|
|
1199
1196
|
}
|
|
1200
1197
|
return { authority: "reviewable", reviewedBy: [...new Set(names)] };
|
|
1201
1198
|
}
|
|
1199
|
+
function whyUnknown(name, known, contested) {
|
|
1200
|
+
const quoted = JSON.stringify(name);
|
|
1201
|
+
const claimants = contested?.get(name);
|
|
1202
|
+
if (claimants)
|
|
1203
|
+
return `${quoted}, which packs ${claimants.join(" and ")} declare differently, so it is asked for neither`;
|
|
1204
|
+
if (known === SEMANTIC_REVIEWER_NAMES)
|
|
1205
|
+
return `${quoted}, which is not a semantic policy in this build`;
|
|
1206
|
+
const sorted = [...known].sort();
|
|
1207
|
+
const listed = sorted.length > 6 ? `${sorted.slice(0, 6).join(", ")} and ${sorted.length - 6} more` : sorted.join(", ");
|
|
1208
|
+
return `${quoted}, which is not among the Jev checks it is judged against (${listed})`;
|
|
1209
|
+
}
|
|
1202
1210
|
function whyHard(decl) {
|
|
1203
1211
|
if (decl.alwaysOn === true)
|
|
1204
1212
|
return "it is alwaysOn, and the self-protection guard is never reviewable";
|
|
@@ -1217,22 +1225,22 @@ function authorityFieldsOf(raw) {
|
|
|
1217
1225
|
}
|
|
1218
1226
|
return out;
|
|
1219
1227
|
}
|
|
1220
|
-
function withMergedAuthority(record, declarations) {
|
|
1221
|
-
const resolved = declarations.map((d) => resolvePolicyAuthority(d));
|
|
1228
|
+
function withMergedAuthority(record, declarations, knownReviewers = SEMANTIC_REVIEWER_NAMES) {
|
|
1229
|
+
const resolved = declarations.map((d) => resolvePolicyAuthority(d, knownReviewers));
|
|
1222
1230
|
const allReviewable = resolved.length > 0 && resolved.every((r) => r.authority === "reviewable");
|
|
1223
|
-
const
|
|
1231
|
+
const decided = declarations[resolved.findIndex((r) => r.authority !== "reviewable")] ?? {};
|
|
1232
|
+
const fields = allReviewable ? { authority: "reviewable", reviewedBy: [...new Set(resolved.flatMap((r) => r.reviewedBy ?? []))] } : decided.authority === undefined ? {} : { authority: "hard" };
|
|
1224
1233
|
const overruled = !allReviewable && resolved.some((r) => r.authority === "reviewable");
|
|
1234
|
+
const refused = resolved.find((r) => r.downgraded !== undefined)?.downgraded;
|
|
1235
|
+
const extra = refused === undefined ? {} : { refused };
|
|
1225
1236
|
const same = record.authority === fields.authority && JSON.stringify(record.reviewedBy) === JSON.stringify(fields.reviewedBy);
|
|
1226
1237
|
if (same)
|
|
1227
|
-
return { merged: record, overruled };
|
|
1238
|
+
return { merged: record, overruled, ...extra };
|
|
1228
1239
|
const { authority: _a, reviewedBy: _r, ...rest } = record;
|
|
1229
|
-
return { merged: { ...rest, ...fields }, overruled };
|
|
1240
|
+
return { merged: { ...rest, ...fields }, overruled, ...extra };
|
|
1230
1241
|
}
|
|
1231
|
-
function
|
|
1232
|
-
return {
|
|
1233
|
-
...d.authority !== undefined ? { authority: d.authority } : {},
|
|
1234
|
-
...d.reviewedBy !== undefined ? { reviewedBy: d.reviewedBy } : {}
|
|
1235
|
-
};
|
|
1242
|
+
function refusedAuthorityWarning(subject, reason) {
|
|
1243
|
+
return `${subject} asks to be reviewable, but ${reason} — it stays hard`;
|
|
1236
1244
|
}
|
|
1237
1245
|
function warnAuthority(message) {
|
|
1238
1246
|
try {
|
|
@@ -1563,7 +1571,7 @@ function checkPackMinCliVersion(packId, declared, current = version) {
|
|
|
1563
1571
|
if (ordered < 0) {
|
|
1564
1572
|
return {
|
|
1565
1573
|
kind: "too-old",
|
|
1566
|
-
reason: `pack ${packId} needs failproofai ${String(declared)} or newer and this is ${current} — ` +
|
|
1574
|
+
reason: `pack ${packId} needs failproofai ${String(declared)} or newer and this is ${current} — ` + `run \`npm i -g "failproofai@>=${String(declared)}" && failproofai update\``
|
|
1567
1575
|
};
|
|
1568
1576
|
}
|
|
1569
1577
|
return { kind: "satisfied", declared };
|
|
@@ -1811,6 +1819,7 @@ function readInstalledPacks() {
|
|
|
1811
1819
|
reason: errText(err),
|
|
1812
1820
|
effect: declaredEffect,
|
|
1813
1821
|
...Array.isArray(rec?.policies) ? { declared: safeDeclared(rec.policies) } : {},
|
|
1822
|
+
...Array.isArray(rec?.policies) && rec.policies.length === 0 ? { semanticOnly: true } : {},
|
|
1814
1823
|
...Array.isArray(rec?.clis) && rec.clis.every((c) => typeof c === "string") ? { clis: rec.clis } : {}
|
|
1815
1824
|
});
|
|
1816
1825
|
}
|
|
@@ -1853,25 +1862,80 @@ var init_pack_manifest = __esm(() => {
|
|
|
1853
1862
|
});
|
|
1854
1863
|
|
|
1855
1864
|
// src/hooks/effective-reviewers.ts
|
|
1865
|
+
function jevPacks(packs, cli) {
|
|
1866
|
+
return packs.filter((p) => p.effect !== "observe" && !(cli && Array.isArray(p.clis) && p.clis.length > 0 && !p.clis.includes(cli)));
|
|
1867
|
+
}
|
|
1868
|
+
function canonical(value) {
|
|
1869
|
+
if (Array.isArray(value))
|
|
1870
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
1871
|
+
if (value !== null && typeof value === "object") {
|
|
1872
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
1873
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
|
|
1874
|
+
}
|
|
1875
|
+
return JSON.stringify(value) ?? "null";
|
|
1876
|
+
}
|
|
1877
|
+
function contestedSemanticNames(packs) {
|
|
1878
|
+
const claims = new Map;
|
|
1879
|
+
const contested = new Map;
|
|
1880
|
+
for (const pack of packs) {
|
|
1881
|
+
for (const entry of pack.semantic ?? []) {
|
|
1882
|
+
if (isReservedClaim(pack, entry.name))
|
|
1883
|
+
continue;
|
|
1884
|
+
const form = canonical(entry);
|
|
1885
|
+
const claim = claims.get(entry.name);
|
|
1886
|
+
if (claim === undefined) {
|
|
1887
|
+
claims.set(entry.name, { form, ids: [pack.id] });
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
claim.ids.push(pack.id);
|
|
1891
|
+
if (claim.form !== form)
|
|
1892
|
+
contested.set(entry.name, claim.ids);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
return contested;
|
|
1896
|
+
}
|
|
1897
|
+
function isFirstPartyPack(pack) {
|
|
1898
|
+
return /^github:FailproofAI\//i.test(pack.source ?? "");
|
|
1899
|
+
}
|
|
1900
|
+
function isReservedClaim(pack, name) {
|
|
1901
|
+
return SEMANTIC_REVIEWER_NAMES.has(name) && !isFirstPartyPack(pack);
|
|
1902
|
+
}
|
|
1856
1903
|
function effectiveReviewerNames() {
|
|
1857
1904
|
if (cached)
|
|
1858
1905
|
return cached;
|
|
1859
1906
|
let names = SEMANTIC_REVIEWER_NAMES;
|
|
1907
|
+
cachedContested = new Map;
|
|
1860
1908
|
try {
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1909
|
+
const packs = jevPacks(readInstalledPacks().packs, reviewerCli);
|
|
1910
|
+
names = reviewerNamesFor(packs);
|
|
1911
|
+
cachedContested = contestedSemanticNames(packs);
|
|
1864
1912
|
} catch {}
|
|
1865
1913
|
cached = names;
|
|
1866
1914
|
return names;
|
|
1867
1915
|
}
|
|
1868
|
-
function
|
|
1916
|
+
function contestedReviewerNames() {
|
|
1917
|
+
effectiveReviewerNames();
|
|
1918
|
+
return cachedContested;
|
|
1919
|
+
}
|
|
1920
|
+
function replacesBuiltinChecks(packs) {
|
|
1921
|
+
return packs.some((p) => isFirstPartyPack(p) && (p.semantic ?? []).length > 0);
|
|
1922
|
+
}
|
|
1923
|
+
function reviewerNamesFor(packs) {
|
|
1924
|
+
const contested = contestedSemanticNames(packs);
|
|
1925
|
+
const declared = packs.flatMap((p) => (p.semantic ?? []).filter((s) => !isReservedClaim(p, s.name)).map((s) => s.name)).filter((name) => !contested.has(name));
|
|
1926
|
+
if (declared.length === 0)
|
|
1927
|
+
return SEMANTIC_REVIEWER_NAMES;
|
|
1928
|
+
return new Set(replacesBuiltinChecks(packs) ? declared : [...SEMANTIC_REVIEWER_NAMES, ...declared]);
|
|
1929
|
+
}
|
|
1930
|
+
function forgetEffectiveReviewerNames(cli) {
|
|
1869
1931
|
cached = null;
|
|
1932
|
+
reviewerCli = cli;
|
|
1870
1933
|
}
|
|
1871
|
-
var cached = null;
|
|
1934
|
+
var cached = null, cachedContested, reviewerCli;
|
|
1872
1935
|
var init_effective_reviewers = __esm(() => {
|
|
1873
1936
|
init_pack_manifest();
|
|
1874
1937
|
init_policy_authority();
|
|
1938
|
+
cachedContested = new Map;
|
|
1875
1939
|
});
|
|
1876
1940
|
|
|
1877
1941
|
// src/hooks/policy-registry.ts
|
|
@@ -1937,11 +2001,11 @@ function getPoliciesForEvent(eventType, toolName) {
|
|
|
1937
2001
|
cache.set(key, result);
|
|
1938
2002
|
return result;
|
|
1939
2003
|
}
|
|
1940
|
-
function clearPolicies() {
|
|
2004
|
+
function clearPolicies(cli) {
|
|
1941
2005
|
const g = globalThis;
|
|
1942
2006
|
g[REGISTRY_KEY] = [];
|
|
1943
2007
|
setIndexCache(null);
|
|
1944
|
-
forgetEffectiveReviewerNames();
|
|
2008
|
+
forgetEffectiveReviewerNames(cli);
|
|
1945
2009
|
}
|
|
1946
2010
|
var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
|
|
1947
2011
|
var init_policy_registry = __esm(() => {
|
|
@@ -2713,6 +2777,9 @@ function protectEnvVars(ctx) {
|
|
|
2713
2777
|
if (EXPORT_RE.test(cmd)) {
|
|
2714
2778
|
return deny("Command exports environment variable");
|
|
2715
2779
|
}
|
|
2780
|
+
if (ENV_DUMP_RE.test(cmd) || SCRIPT_INTERP_RE.test(cmd) && ENV_OBJECT_RE.test(cmd)) {
|
|
2781
|
+
return deny("Command dumps environment variables");
|
|
2782
|
+
}
|
|
2716
2783
|
if (PS_ENV_VAR_RE.test(cmd)) {
|
|
2717
2784
|
return deny("Command reads environment variable via PowerShell");
|
|
2718
2785
|
}
|
|
@@ -3007,8 +3074,12 @@ function extractAbsolutePaths(command) {
|
|
|
3007
3074
|
let m;
|
|
3008
3075
|
while ((m = pathRe.exec(s)) !== null) {
|
|
3009
3076
|
let p = m[0];
|
|
3010
|
-
if (/^\/{2,}$/.test(p))
|
|
3011
|
-
|
|
3077
|
+
if (/^\/{2,}$/.test(p)) {
|
|
3078
|
+
const nl = s.lastIndexOf(`
|
|
3079
|
+
`, m.index - 1);
|
|
3080
|
+
if (nl !== -1 && s[nl - 1] !== "\\" && /^[ \t]*$/.test(s.slice(nl + 1, m.index)))
|
|
3081
|
+
continue;
|
|
3082
|
+
}
|
|
3012
3083
|
if (p === "~")
|
|
3013
3084
|
p = homedir3();
|
|
3014
3085
|
else if (p.startsWith("~/"))
|
|
@@ -3668,7 +3739,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
3668
3739
|
}
|
|
3669
3740
|
}
|
|
3670
3741
|
}
|
|
3671
|
-
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_STATE_PATH_RE, STATE_READ_COMMANDS, SHELL_KEYWORD_PREFIXES, LOOP_HEADER_RE, COPY_COMMANDS, OPERAND_WRITE_COMMANDS, SECOND_OPERAND_WRITERS, OUTPUT_FLAG_COMMANDS, FIND_WRITE_ACTIONS, SAFE_EXEC_COMMANDS, GIT_DESTRUCTIVE_SUBCOMMANDS, GIT_FLAGS_WITH_OPERANDS, STATE_MENTION_COMMANDS, FIND_EXEC_ACTIONS, PIPELINE_SEPARATORS, VAR_REFERENCE_RE, STATE_GLOB_CANDIDATES, GLOB_DECOYS, BRACE_EXPANSION_LIMIT = 4096, BRACE_EXPANSION_ROUNDS = 24, GLOB_METACHAR_RE, REDIRECT_TARGET_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_CMD_RE, SHORT_FLAG_CLUSTER_RE, GIT_CLEAN_FORCE_WAIVED_RE, GIT_CLEAN_DESTRUCTIVE_FLAGS, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, POLICY_IMPLEMENTATIONS, BUILTIN_POLICIES;
|
|
3742
|
+
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, ENV_DUMP_RE, SCRIPT_INTERP_RE, ENV_OBJECT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_STATE_PATH_RE, STATE_READ_COMMANDS, SHELL_KEYWORD_PREFIXES, LOOP_HEADER_RE, COPY_COMMANDS, OPERAND_WRITE_COMMANDS, SECOND_OPERAND_WRITERS, OUTPUT_FLAG_COMMANDS, FIND_WRITE_ACTIONS, SAFE_EXEC_COMMANDS, GIT_DESTRUCTIVE_SUBCOMMANDS, GIT_FLAGS_WITH_OPERANDS, STATE_MENTION_COMMANDS, FIND_EXEC_ACTIONS, PIPELINE_SEPARATORS, VAR_REFERENCE_RE, STATE_GLOB_CANDIDATES, GLOB_DECOYS, BRACE_EXPANSION_LIMIT = 4096, BRACE_EXPANSION_ROUNDS = 24, GLOB_METACHAR_RE, REDIRECT_TARGET_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_CMD_RE, SHORT_FLAG_CLUSTER_RE, GIT_CLEAN_FORCE_WAIVED_RE, GIT_CLEAN_DESTRUCTIVE_FLAGS, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, POLICY_IMPLEMENTATIONS, BUILTIN_POLICIES;
|
|
3672
3743
|
var init_builtin_policies = __esm(() => {
|
|
3673
3744
|
init_policy_catalog();
|
|
3674
3745
|
init_policy_registry();
|
|
@@ -3708,6 +3779,9 @@ var init_builtin_policies = __esm(() => {
|
|
|
3708
3779
|
ENV_PRINTENV_RE = /(?:^|\s|;|&&|\|\|)(?:env|printenv)(?:\s|$|;|&&|\|)/;
|
|
3709
3780
|
ECHO_ENV_RE = /echo\s+.*\$\{?[A-Za-z_]/;
|
|
3710
3781
|
EXPORT_RE = /(?:^|\s|;|&&|\|\|)export\s+\w+/;
|
|
3782
|
+
ENV_DUMP_RE = /(?:^|[;&|({\n`'"]|\$\()\s*(?:set|export(?:\s+-p)?)\s*(?:$|[|;&>)\n])|\b(?:declare|typeset)\s+-[A-Za-z]*[xp]|\bcompgen\s+-[A-Za-z]*[ev]|\/proc\/(?:self|thread-self|\d+|\$\$|\$BASHPID)\/environ/;
|
|
3783
|
+
SCRIPT_INTERP_RE = /\b(?:python[\d.]*|node|bun|deno)\b/;
|
|
3784
|
+
ENV_OBJECT_RE = /\bos\.environ(?!\s*\[|\.(?:get|setdefault|pop|update)\b)|\bprocess\.env(?![.\[\w])/;
|
|
3711
3785
|
PS_ENV_VAR_RE = /\$env:[A-Za-z_]/i;
|
|
3712
3786
|
PS_CHILDITEM_ENV_RE = /(?:Get-ChildItem|dir|gci|ls)\s+Env:/i;
|
|
3713
3787
|
DOTNET_GETENV_RE = /\[Environment\]::GetEnvironment/i;
|
|
@@ -4036,6 +4110,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
4036
4110
|
var MAX_ARTIFACT_BYTES, CORE_SOURCE = "FailproofAI/policies", RETIRED_CORE_ALIASES;
|
|
4037
4111
|
var init_pack_store = __esm(() => {
|
|
4038
4112
|
init_pack_manifest();
|
|
4113
|
+
init_effective_reviewers();
|
|
4039
4114
|
MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
|
|
4040
4115
|
RETIRED_CORE_ALIASES = new Set(["core", "failproofai", "official"]);
|
|
4041
4116
|
});
|
|
@@ -4240,55 +4315,74 @@ function combineTwoTier(verdicts, review, mode) {
|
|
|
4240
4315
|
const wholePicture = review.injectionAsked && !review.injected && !review.requestCut;
|
|
4241
4316
|
const asked = new Set(review.asked);
|
|
4242
4317
|
const notDenied = new Set(review.notDenied);
|
|
4243
|
-
const cleared = wholePicture ? verdicts.filter((v) => clears(v, asked, notDenied)).map((v) => v.policyName) : [];
|
|
4318
|
+
const cleared = wholePicture && !review.unclearableWarned ? verdicts.filter((v) => clears(v, asked, notDenied)).map((v) => v.policyName) : [];
|
|
4319
|
+
const jevEntry = { policyName: review.policyName, reason: review.reason ?? `Flagged by semantic review (${review.policyName})` };
|
|
4320
|
+
const enforced = resolveEnforce(verdicts, cleared, review.decision, jevEntry);
|
|
4244
4321
|
const activity = {
|
|
4245
4322
|
evaluator: review.requestCut ? "jev-fallback" : "jev",
|
|
4246
4323
|
...review.requestCut ? { jevFallbackReason: JEV_REASON_REQUEST_CUT } : {},
|
|
4247
4324
|
jevDecision: review.decision,
|
|
4248
|
-
...cleared.length > 0 ? { jevCleared: cleared } : {},
|
|
4325
|
+
...cleared.length > 0 && RANK[enforced.final.decision] < RANK[legacy.decision] ? { jevCleared: cleared } : {},
|
|
4249
4326
|
...review.latencyMs !== null ? { jevLatencyMs: review.latencyMs } : {},
|
|
4250
4327
|
...review.model ? { jevModel: review.model } : {},
|
|
4251
4328
|
jevMode: mode
|
|
4252
4329
|
};
|
|
4253
|
-
if (mode === "shadow")
|
|
4254
|
-
|
|
4330
|
+
if (mode === "shadow") {
|
|
4331
|
+
const shadowVerdict = review.decision === "deny" || review.decision === "instruct" ? {
|
|
4332
|
+
policyName: jevEntry.policyName,
|
|
4333
|
+
decision: review.decision,
|
|
4334
|
+
reason: jevEntry.reason,
|
|
4335
|
+
version: review.model && JEV_MODEL_RE.test(review.model) ? review.model : "jev"
|
|
4336
|
+
} : undefined;
|
|
4337
|
+
return { final: legacy, cleared, decidedByJev: false, activity, ...shadowVerdict ? { shadowVerdict } : {} };
|
|
4338
|
+
}
|
|
4339
|
+
return { ...enforced, cleared, activity };
|
|
4340
|
+
}
|
|
4341
|
+
function resolveEnforce(verdicts, cleared, decision, jevEntry) {
|
|
4255
4342
|
const clearedSet = new Set(cleared);
|
|
4256
4343
|
const remaining = verdicts.filter((v) => !clearedSet.has(v.policyName));
|
|
4257
|
-
const jevEntry = { policyName: review.policyName, reason: review.reason ?? `Flagged by semantic review (${review.policyName})` };
|
|
4258
4344
|
const regexDeny = remaining.find((v) => v.decision === "deny");
|
|
4259
4345
|
if (regexDeny) {
|
|
4260
4346
|
return {
|
|
4261
4347
|
final: { decision: "deny", entries: [{ policyName: regexDeny.policyName, reason: regexDeny.reason ?? "" }] },
|
|
4262
|
-
|
|
4263
|
-
decidedByJev: false,
|
|
4264
|
-
activity
|
|
4348
|
+
decidedByJev: false
|
|
4265
4349
|
};
|
|
4266
4350
|
}
|
|
4267
|
-
if (
|
|
4268
|
-
return { final: { decision: "deny", entries: [jevEntry] },
|
|
4351
|
+
if (decision === "deny") {
|
|
4352
|
+
return { final: { decision: "deny", entries: [jevEntry] }, decidedByJev: true };
|
|
4269
4353
|
}
|
|
4270
4354
|
const instructs = remaining.filter((v) => v.decision === "instruct").map((v) => ({ policyName: v.policyName, reason: v.reason ?? "" }));
|
|
4271
|
-
if (
|
|
4355
|
+
if (decision === "instruct")
|
|
4272
4356
|
instructs.push(jevEntry);
|
|
4273
4357
|
if (instructs.length > 0) {
|
|
4274
|
-
return {
|
|
4275
|
-
final: { decision: "instruct", entries: instructs },
|
|
4276
|
-
cleared,
|
|
4277
|
-
decidedByJev: instructs[0] === jevEntry,
|
|
4278
|
-
activity
|
|
4279
|
-
};
|
|
4358
|
+
return { final: { decision: "instruct", entries: instructs }, decidedByJev: instructs[0] === jevEntry };
|
|
4280
4359
|
}
|
|
4281
|
-
return { final: regexOnly(remaining),
|
|
4360
|
+
return { final: regexOnly(remaining), decidedByJev: false };
|
|
4282
4361
|
}
|
|
4362
|
+
var RANK;
|
|
4283
4363
|
var init_combine = __esm(() => {
|
|
4284
4364
|
init_jev_activity();
|
|
4365
|
+
RANK = { allow: 0, instruct: 1, deny: 2 };
|
|
4285
4366
|
});
|
|
4286
4367
|
|
|
4287
4368
|
// src/posthog-key.ts
|
|
4288
4369
|
var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
|
|
4289
4370
|
|
|
4290
4371
|
// src/hooks/fp-config.ts
|
|
4291
|
-
import {
|
|
4372
|
+
import {
|
|
4373
|
+
chmodSync,
|
|
4374
|
+
closeSync,
|
|
4375
|
+
constants as fsConstants,
|
|
4376
|
+
existsSync as existsSync6,
|
|
4377
|
+
fstatSync,
|
|
4378
|
+
mkdirSync as mkdirSync3,
|
|
4379
|
+
openSync,
|
|
4380
|
+
readFileSync as readFileSync4,
|
|
4381
|
+
readSync,
|
|
4382
|
+
statSync as statSync4,
|
|
4383
|
+
writeFileSync as writeFileSync2
|
|
4384
|
+
} from "node:fs";
|
|
4385
|
+
import { dirname as dirname2 } from "node:path";
|
|
4292
4386
|
function detectLayout() {
|
|
4293
4387
|
const raw = readVersionFile();
|
|
4294
4388
|
if (raw) {
|
|
@@ -4407,7 +4501,147 @@ function projectConfig(parsed) {
|
|
|
4407
4501
|
};
|
|
4408
4502
|
}
|
|
4409
4503
|
}
|
|
4410
|
-
|
|
4504
|
+
function projectCredentials(parsed) {
|
|
4505
|
+
{
|
|
4506
|
+
const cloud = parsed.cloud;
|
|
4507
|
+
const ingest = parsed.ingest;
|
|
4508
|
+
const auth = parsed.auth;
|
|
4509
|
+
const out = {};
|
|
4510
|
+
if (cloud && typeof cloud.url === "string" && typeof cloud.machine_id === "string" && typeof cloud.token === "string" && cloud.url && cloud.token) {
|
|
4511
|
+
const machineLabel = typeof cloud.machine_label === "string" && cloud.machine_label ? cloud.machine_label : undefined;
|
|
4512
|
+
out.cloud = { url: cloud.url, machineId: cloud.machine_id, token: cloud.token, machineLabel };
|
|
4513
|
+
}
|
|
4514
|
+
const org = parsed.org;
|
|
4515
|
+
if (org) {
|
|
4516
|
+
const pick = (v) => typeof v === "string" && v ? v : undefined;
|
|
4517
|
+
const built = { id: pick(org.id), slug: pick(org.slug), name: pick(org.name) };
|
|
4518
|
+
if (built.id || built.slug || built.name)
|
|
4519
|
+
out.org = built;
|
|
4520
|
+
}
|
|
4521
|
+
if (ingest && typeof ingest.url === "string" && typeof ingest.key === "string" && ingest.url && ingest.key) {
|
|
4522
|
+
out.ingest = { url: ingest.url, key: ingest.key };
|
|
4523
|
+
}
|
|
4524
|
+
const jev = projectJevSlot(parsed.jev);
|
|
4525
|
+
if (jev)
|
|
4526
|
+
out.jev = jev;
|
|
4527
|
+
if (auth) {
|
|
4528
|
+
out.auth = {
|
|
4529
|
+
baseUrl: typeof auth.base_url === "string" ? auth.base_url : undefined,
|
|
4530
|
+
sessionToken: typeof auth.session_token === "string" ? auth.session_token : undefined,
|
|
4531
|
+
expiresAt: typeof auth.expires_at === "number" ? auth.expires_at : undefined,
|
|
4532
|
+
email: typeof auth.email === "string" ? auth.email : undefined
|
|
4533
|
+
};
|
|
4534
|
+
}
|
|
4535
|
+
return out;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function projectJevSlot(raw) {
|
|
4539
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
4540
|
+
return null;
|
|
4541
|
+
const { url, key } = raw;
|
|
4542
|
+
return typeof url === "string" && url && typeof key === "string" && key ? { url, key } : null;
|
|
4543
|
+
}
|
|
4544
|
+
function readJevCloudCredential() {
|
|
4545
|
+
const path = credentialsFile();
|
|
4546
|
+
let fd;
|
|
4547
|
+
try {
|
|
4548
|
+
fd = openSync(path, credentialsOpenFlags());
|
|
4549
|
+
} catch (err) {
|
|
4550
|
+
const code = err.code;
|
|
4551
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
4552
|
+
return { status: "absent", path, connected: false };
|
|
4553
|
+
return { status: "refused", path, mode: null, reason: "unreadable", problem: `cannot open ${path} (${code ?? "error"})` };
|
|
4554
|
+
}
|
|
4555
|
+
let text;
|
|
4556
|
+
try {
|
|
4557
|
+
const st = fstatSync(fd);
|
|
4558
|
+
if (!st.isFile())
|
|
4559
|
+
return { status: "refused", path, mode: null, reason: "unreadable", problem: `${path} is not a regular file` };
|
|
4560
|
+
const meaningful = process.platform !== "win32";
|
|
4561
|
+
const mode = st.mode & 511;
|
|
4562
|
+
if (meaningful && (mode & 63) !== 0) {
|
|
4563
|
+
return {
|
|
4564
|
+
status: "refused",
|
|
4565
|
+
path,
|
|
4566
|
+
mode,
|
|
4567
|
+
reason: "too-open",
|
|
4568
|
+
problem: `${path} is ${mode.toString(8).padStart(4, "0")}; it holds the key Jev sends to FailproofAI Cloud, ` + `so it must be owner-only (chmod 600 ${path})`,
|
|
4569
|
+
fix: `chmod 600 ${path}`
|
|
4570
|
+
};
|
|
4571
|
+
}
|
|
4572
|
+
if (meaningful) {
|
|
4573
|
+
let dirMode = null;
|
|
4574
|
+
try {
|
|
4575
|
+
const m = statSync4(dirname2(path)).mode & 511;
|
|
4576
|
+
if ((m & DIR_WRITABLE_BY_OTHERS) !== 0)
|
|
4577
|
+
dirMode = m;
|
|
4578
|
+
} catch {}
|
|
4579
|
+
if (dirMode !== null) {
|
|
4580
|
+
return {
|
|
4581
|
+
status: "refused",
|
|
4582
|
+
path,
|
|
4583
|
+
mode,
|
|
4584
|
+
reason: "too-open",
|
|
4585
|
+
problem: `its directory ${dirname2(path)} is ${dirMode.toString(8).padStart(4, "0")} — other users can write there, ` + `so they can put their own credentials in its place; it must be owner-only (chmod 700 ${dirname2(path)})`,
|
|
4586
|
+
fix: `chmod 700 ${dirname2(path)}`
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
if (st.size > MAX_CREDENTIALS_BYTES) {
|
|
4591
|
+
return { status: "refused", path, mode, reason: "too-large", problem: `${path} is larger than ${MAX_CREDENTIALS_BYTES} bytes` };
|
|
4592
|
+
}
|
|
4593
|
+
const buf = Buffer.alloc(st.size);
|
|
4594
|
+
let off = 0;
|
|
4595
|
+
while (off < buf.length) {
|
|
4596
|
+
const n = readSync(fd, buf, off, buf.length - off, off);
|
|
4597
|
+
if (n === 0)
|
|
4598
|
+
break;
|
|
4599
|
+
off += n;
|
|
4600
|
+
}
|
|
4601
|
+
text = buf.subarray(0, off).toString("utf8");
|
|
4602
|
+
} catch (err) {
|
|
4603
|
+
return { status: "refused", path, mode: null, reason: "unreadable", problem: `cannot read ${path} (${err.code ?? "error"})` };
|
|
4604
|
+
} finally {
|
|
4605
|
+
try {
|
|
4606
|
+
closeSync(fd);
|
|
4607
|
+
} catch {}
|
|
4608
|
+
}
|
|
4609
|
+
let parsed;
|
|
4610
|
+
try {
|
|
4611
|
+
parsed = JSON.parse(text);
|
|
4612
|
+
} catch {
|
|
4613
|
+
return { status: "refused", path, mode: null, reason: "not-json", problem: `${path} is not valid JSON` };
|
|
4614
|
+
}
|
|
4615
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4616
|
+
return { status: "refused", path, mode: null, reason: "not-json", problem: `${path} does not hold a JSON object` };
|
|
4617
|
+
}
|
|
4618
|
+
const fields = parsed;
|
|
4619
|
+
const others = projectCredentials(fields);
|
|
4620
|
+
const connections = [
|
|
4621
|
+
others.cloud ? { origin: originOrNull(others.cloud.url), key: others.cloud.token } : null,
|
|
4622
|
+
others.ingest ? { origin: originOrNull(others.ingest.url), key: others.ingest.key } : null
|
|
4623
|
+
].filter((c) => c !== null && c.origin !== null);
|
|
4624
|
+
const connected = connections.length > 0;
|
|
4625
|
+
const credential = projectJevSlot(fields.jev);
|
|
4626
|
+
if (!credential)
|
|
4627
|
+
return { status: "absent", path, connected };
|
|
4628
|
+
const slotOrigin = originOrNull(credential.url);
|
|
4629
|
+
const backed = slotOrigin !== null && connections.some((c) => c.origin === slotOrigin && c.key === credential.key);
|
|
4630
|
+
if (!backed)
|
|
4631
|
+
return { status: "absent", path, connected, orphaned: true };
|
|
4632
|
+
return { status: "ok", path, credential };
|
|
4633
|
+
}
|
|
4634
|
+
function originOrNull(url) {
|
|
4635
|
+
if (!url)
|
|
4636
|
+
return null;
|
|
4637
|
+
try {
|
|
4638
|
+
const origin = new URL(url).origin;
|
|
4639
|
+
return origin === "null" ? null : origin;
|
|
4640
|
+
} catch {
|
|
4641
|
+
return null;
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG, MAX_CREDENTIALS_BYTES, credentialsOpenFlags = () => fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0), DIR_WRITABLE_BY_OTHERS = 18;
|
|
4411
4645
|
var init_fp_config = __esm(() => {
|
|
4412
4646
|
init_package();
|
|
4413
4647
|
init_fp_home();
|
|
@@ -4424,6 +4658,7 @@ var init_fp_config = __esm(() => {
|
|
|
4424
4658
|
telemetry: { enabled: true },
|
|
4425
4659
|
audit: { auto: false, intervalDays: DEFAULT_AUDIT_INTERVAL_DAYS }
|
|
4426
4660
|
};
|
|
4661
|
+
MAX_CREDENTIALS_BYTES = 64 * 1024;
|
|
4427
4662
|
});
|
|
4428
4663
|
|
|
4429
4664
|
// lib/telemetry-enabled.ts
|
|
@@ -4679,8 +4914,13 @@ async function evaluatePolicies(eventType, payload, session, config, twoTier) {
|
|
|
4679
4914
|
hookLogInfo(combined.activity.jevFallbackReason === JEV_REASON_REQUEST_CUT ? "jev could not be shown the whole call; it cleared nothing, and the call was not allowed on a partial read" : `jev unavailable (${combined.activity.jevFallbackReason}); the regex result decided this call`);
|
|
4680
4915
|
}
|
|
4681
4916
|
return {
|
|
4682
|
-
...formatVerdict(eventType, session, toolName, combined.final),
|
|
4683
|
-
twoTier: {
|
|
4917
|
+
...formatVerdict(eventType, session, toolName, combined.final, combined.decidedByJev),
|
|
4918
|
+
twoTier: {
|
|
4919
|
+
activity: combined.activity,
|
|
4920
|
+
decidedByJev: combined.decidedByJev,
|
|
4921
|
+
...combined.decidedByJev && review.kind === "answered" && review.origin ? { jevOrigin: review.origin } : {},
|
|
4922
|
+
...combined.shadowVerdict ? { shadowVerdict: combined.shadowVerdict } : {}
|
|
4923
|
+
}
|
|
4684
4924
|
};
|
|
4685
4925
|
}
|
|
4686
4926
|
async function collectVerdicts(eventType, payload, session, config, policies, twoTier) {
|
|
@@ -4746,10 +4986,10 @@ async function collectVerdicts(eventType, payload, session, config, policies, tw
|
|
|
4746
4986
|
}
|
|
4747
4987
|
return { verdicts, hardDenied: false };
|
|
4748
4988
|
}
|
|
4749
|
-
function formatVerdict(eventType, session, toolName, final) {
|
|
4989
|
+
function formatVerdict(eventType, session, toolName, final, byJev = false) {
|
|
4750
4990
|
if (final.decision === "deny") {
|
|
4751
4991
|
const [decider] = final.entries;
|
|
4752
|
-
return formatDeny(eventType, session, toolName, decider.policyName, decider.reason);
|
|
4992
|
+
return formatDeny(eventType, session, toolName, decider.policyName, decider.reason, byJev);
|
|
4753
4993
|
}
|
|
4754
4994
|
if (final.decision === "instruct")
|
|
4755
4995
|
return formatInstruct(eventType, session, final.entries);
|
|
@@ -4757,7 +4997,7 @@ function formatVerdict(eventType, session, toolName, final) {
|
|
|
4757
4997
|
return formatAllow(eventType, session, final.entries);
|
|
4758
4998
|
return { exitCode: 0, stdout: "", stderr: "", policyName: null, reason: null, decision: "allow" };
|
|
4759
4999
|
}
|
|
4760
|
-
function formatDeny(eventType, session, toolName, policyName, reason) {
|
|
5000
|
+
function formatDeny(eventType, session, toolName, policyName, reason, byJev = false) {
|
|
4761
5001
|
let displayTool;
|
|
4762
5002
|
if (toolName) {
|
|
4763
5003
|
displayTool = toolName;
|
|
@@ -4772,7 +5012,7 @@ function formatDeny(eventType, session, toolName, policyName, reason) {
|
|
|
4772
5012
|
} else {
|
|
4773
5013
|
displayTool = "operation";
|
|
4774
5014
|
}
|
|
4775
|
-
const blockedMessage = `Blocked ${displayTool} by failproofai because: ${reason}, as per the policy configured by the user`;
|
|
5015
|
+
const blockedMessage = byJev ? `Blocked ${displayTool} by failproofai because: ${reason.replace(/[.\s]+$/, "")}, as flagged by Jev semantic review` : `Blocked ${displayTool} by failproofai because: ${reason}, as per the policy configured by the user`;
|
|
4776
5016
|
if (session?.cli === "cursor") {
|
|
4777
5017
|
if (eventType === "Stop" || eventType === "SubagentStop") {
|
|
4778
5018
|
const reasonText = `MANDATORY ACTION REQUIRED from failproofai (policy: ${policyName}): ${reason}
|
|
@@ -5001,7 +5241,7 @@ You MUST complete the above action NOW. Do NOT ask the user for confirmation —
|
|
|
5001
5241
|
hookEventName: eventType,
|
|
5002
5242
|
decision: {
|
|
5003
5243
|
behavior: "deny",
|
|
5004
|
-
message:
|
|
5244
|
+
message: blockedMessage
|
|
5005
5245
|
}
|
|
5006
5246
|
}
|
|
5007
5247
|
};
|
|
@@ -5028,7 +5268,7 @@ You MUST complete the above action NOW. Do NOT ask the user for confirmation —
|
|
|
5028
5268
|
const response = {
|
|
5029
5269
|
hookSpecificOutput: {
|
|
5030
5270
|
hookEventName: eventType,
|
|
5031
|
-
additionalContext:
|
|
5271
|
+
additionalContext: blockedMessage
|
|
5032
5272
|
}
|
|
5033
5273
|
};
|
|
5034
5274
|
return {
|
|
@@ -5446,9 +5686,18 @@ function getRegistry2() {
|
|
|
5446
5686
|
g[REGISTRY_KEY2] = [];
|
|
5447
5687
|
return g[REGISTRY_KEY2];
|
|
5448
5688
|
}
|
|
5689
|
+
function getSemanticRegistry() {
|
|
5690
|
+
const g = globalThis;
|
|
5691
|
+
if (!Array.isArray(g[SEMANTIC_KEY]))
|
|
5692
|
+
g[SEMANTIC_KEY] = [];
|
|
5693
|
+
return g[SEMANTIC_KEY];
|
|
5694
|
+
}
|
|
5449
5695
|
function getCustomHooks() {
|
|
5450
5696
|
return getRegistry2();
|
|
5451
5697
|
}
|
|
5698
|
+
function getSemanticRegistrations() {
|
|
5699
|
+
return getSemanticRegistry();
|
|
5700
|
+
}
|
|
5452
5701
|
function clearCustomHooks() {
|
|
5453
5702
|
const g = globalThis;
|
|
5454
5703
|
g[REGISTRY_KEY2] = [];
|
|
@@ -5466,7 +5715,7 @@ var init_custom_hooks_registry = __esm(() => {
|
|
|
5466
5715
|
// src/hooks/loader-utils.ts
|
|
5467
5716
|
import { readFile as readFile2, writeFile as writeFile2, unlink, access, mkdir, lstat } from "fs/promises";
|
|
5468
5717
|
import { tmpdir } from "os";
|
|
5469
|
-
import { resolve as resolve6, dirname as
|
|
5718
|
+
import { resolve as resolve6, dirname as dirname3, relative as relative2, join as join3 } from "path";
|
|
5470
5719
|
import { pathToFileURL } from "url";
|
|
5471
5720
|
import { createHash as createHash3 } from "crypto";
|
|
5472
5721
|
function isTmpArtifact(name) {
|
|
@@ -5507,7 +5756,7 @@ async function findDistIndex() {
|
|
|
5507
5756
|
return candidate;
|
|
5508
5757
|
}
|
|
5509
5758
|
const candidates = [
|
|
5510
|
-
resolve6(
|
|
5759
|
+
resolve6(dirname3(process.execPath), "..", "assets", "dist", "index.js"),
|
|
5511
5760
|
resolve6(process.cwd(), "dist", "index.js"),
|
|
5512
5761
|
resolve6(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
|
|
5513
5762
|
];
|
|
@@ -5598,7 +5847,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SU
|
|
|
5598
5847
|
if (distIndex) {
|
|
5599
5848
|
code = code.replace(/require\s*\(\s*(['"])(?:claudeye|failproofai)\1\s*\)/g, `require('${distIndex.replace(/\\/g, "\\\\")}')`);
|
|
5600
5849
|
}
|
|
5601
|
-
const dir =
|
|
5850
|
+
const dir = dirname3(filePath);
|
|
5602
5851
|
const rewrites = new Map;
|
|
5603
5852
|
for (const re of [LOCAL_IMPORT_RE, LOCAL_REQUIRE_RE]) {
|
|
5604
5853
|
const freshRe = new RegExp(re.source, re.flags);
|
|
@@ -5714,7 +5963,13 @@ async function loadSingleFile(absPath, opts) {
|
|
|
5714
5963
|
const entryTmp = absPath + tmpSuffix;
|
|
5715
5964
|
const fileUrl = pathToFileURL2(entryTmp).href;
|
|
5716
5965
|
const hooksBefore = getCustomHooks().length;
|
|
5966
|
+
const semanticBefore = getSemanticRegistrations().length;
|
|
5717
5967
|
await importWithDeadline(fileUrl);
|
|
5968
|
+
const ignored = getSemanticRegistrations().slice(semanticBefore).map((d) => d?.name);
|
|
5969
|
+
if (opts?.outsidePack && ignored.length > 0 && !warnedSemanticOutsidePack.has(absPath)) {
|
|
5970
|
+
warnedSemanticOutsidePack.add(absPath);
|
|
5971
|
+
hookLogWarn(`${basename(absPath)}: semanticPolicies.add only takes effect in a pack published with \`failproofai publish\`; ` + `${ignored.join(", ")} ${ignored.length === 1 ? "is" : "are"} never asked here`);
|
|
5972
|
+
}
|
|
5718
5973
|
if (policyModuleCache.size >= POLICY_MODULE_CACHE_MAX_ENTRIES)
|
|
5719
5974
|
policyModuleCache.clear();
|
|
5720
5975
|
policyModuleCache.set(absPath, {
|
|
@@ -5792,20 +6047,27 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5792
6047
|
continue;
|
|
5793
6048
|
}
|
|
5794
6049
|
hookLogWarn(`cloud-managed policies ${existing.id} and ${policy.id} have identical source, so they share ` + `one artifact and load as one policy; enforcing it if either asks to enforce`);
|
|
6050
|
+
const knownReviewers = effectiveReviewerNames();
|
|
5795
6051
|
const winner = existing.effect !== "enforce" && policy.effect === "enforce" ? policy : existing;
|
|
5796
|
-
const { merged, overruled } = withMergedAuthority(winner, [existing, policy]);
|
|
6052
|
+
const { merged, overruled, refused } = withMergedAuthority(winner, [existing, policy], knownReviewers);
|
|
5797
6053
|
if (overruled) {
|
|
5798
6054
|
warnAuthority(`cloud-managed policies ${existing.id} and ${policy.id} share one artifact and do not all declare it ` + `reviewable, so it stays hard`);
|
|
5799
6055
|
}
|
|
6056
|
+
if (refused) {
|
|
6057
|
+
warnAuthority(refusedAuthorityWarning(`cloud-managed policies ${existing.id} and ${policy.id}`, refused));
|
|
6058
|
+
}
|
|
5800
6059
|
cloudManagedByPath.set(key, merged);
|
|
5801
6060
|
}
|
|
5802
6061
|
const unionSelection = (a, b) => a === null || b === null ? null : [...new Set([...a, ...b])];
|
|
5803
|
-
const unionCatalog = (winner, other) => {
|
|
6062
|
+
const unionCatalog = (winner, other, knownReviewers) => {
|
|
5804
6063
|
const overruled = [];
|
|
6064
|
+
const refused = [];
|
|
5805
6065
|
const harden = (entry, peer) => {
|
|
5806
|
-
const r = withMergedAuthority(entry, [entry, peer ?? {}]);
|
|
6066
|
+
const r = withMergedAuthority(entry, [entry, peer ?? {}], knownReviewers);
|
|
5807
6067
|
if (r.overruled)
|
|
5808
6068
|
overruled.push(entry.name);
|
|
6069
|
+
if (r.refused)
|
|
6070
|
+
refused.push(refusedAuthorityWarning(entry.name, r.refused));
|
|
5809
6071
|
return r.merged;
|
|
5810
6072
|
};
|
|
5811
6073
|
return {
|
|
@@ -5813,7 +6075,8 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5813
6075
|
...winner.map((w) => harden(w, other.find((p) => p.name === w.name))),
|
|
5814
6076
|
...other.filter((p) => !winner.some((w) => w.name === p.name)).map((p) => harden(p, undefined))
|
|
5815
6077
|
],
|
|
5816
|
-
overruled
|
|
6078
|
+
overruled,
|
|
6079
|
+
refused
|
|
5817
6080
|
};
|
|
5818
6081
|
};
|
|
5819
6082
|
const packByPath = new Map;
|
|
@@ -5829,7 +6092,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5829
6092
|
hookLogWarn(`packs ${existing.id} and ${pack.id} have identical source, so they share one artifact ` + `and load as one pack; enforcing it if either asks to enforce, and taking ` + `the union of what each has enabled and of the agents each guards`);
|
|
5830
6093
|
const winner = existing.effect !== "enforce" && pack.effect === "enforce" ? pack : existing;
|
|
5831
6094
|
const other = winner === existing ? pack : existing;
|
|
5832
|
-
const catalog = unionCatalog(winner.policies, other.policies);
|
|
6095
|
+
const catalog = unionCatalog(winner.policies, other.policies, effectiveReviewerNames());
|
|
5833
6096
|
packByPath.set(key, {
|
|
5834
6097
|
...winner,
|
|
5835
6098
|
policies: catalog.policies,
|
|
@@ -5839,6 +6102,8 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5839
6102
|
if (catalog.overruled.length > 0) {
|
|
5840
6103
|
warnAuthority(`packs ${existing.id} and ${pack.id} share one artifact and do not all declare ` + `${catalog.overruled.join(", ")} reviewable, so ${catalog.overruled.length === 1 ? "it stays" : "they stay"} hard`);
|
|
5841
6104
|
}
|
|
6105
|
+
for (const message of catalog.refused)
|
|
6106
|
+
warnAuthority(message);
|
|
5842
6107
|
}
|
|
5843
6108
|
for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
|
|
5844
6109
|
const absPath = resolve7(projectRoot, customPoliciesPath);
|
|
@@ -5849,7 +6114,8 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5849
6114
|
loadedPaths.add(absPath);
|
|
5850
6115
|
const hooksBefore = getCustomHooks().length;
|
|
5851
6116
|
const failure = await loadSingleFile(absPath, {
|
|
5852
|
-
verifyEntrySha: cloudManaged?.sha256 ?? pack?.sha256
|
|
6117
|
+
verifyEntrySha: cloudManaged?.sha256 ?? pack?.sha256,
|
|
6118
|
+
outsidePack: !pack
|
|
5853
6119
|
});
|
|
5854
6120
|
if (failure && pack) {
|
|
5855
6121
|
for (const id of packIdsByPath.get(absPath) ?? [pack.id])
|
|
@@ -5890,7 +6156,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5890
6156
|
for (const file of projectFiles) {
|
|
5891
6157
|
loadedPaths.add(file);
|
|
5892
6158
|
const hooksBefore = getCustomHooks().length;
|
|
5893
|
-
await loadSingleFile(file, { conventionScope: projectScope });
|
|
6159
|
+
await loadSingleFile(file, { conventionScope: projectScope, outsidePack: true });
|
|
5894
6160
|
const newHooks = getCustomHooks().slice(hooksBefore);
|
|
5895
6161
|
for (const hook of newHooks) {
|
|
5896
6162
|
hook.__policyId = conventionPolicyId(projectScope, basename(file), hook.name);
|
|
@@ -5912,7 +6178,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5912
6178
|
for (const file of userFiles) {
|
|
5913
6179
|
loadedPaths.add(file);
|
|
5914
6180
|
const hooksBefore = getCustomHooks().length;
|
|
5915
|
-
await loadSingleFile(file, { conventionScope: "user" });
|
|
6181
|
+
await loadSingleFile(file, { conventionScope: "user", outsidePack: true });
|
|
5916
6182
|
const newHooks = getCustomHooks().slice(hooksBefore);
|
|
5917
6183
|
for (const hook of newHooks) {
|
|
5918
6184
|
hook.__policyId = conventionPolicyId("user", basename(file), hook.name);
|
|
@@ -5955,7 +6221,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
5955
6221
|
}
|
|
5956
6222
|
return { hooks: allHooks, conventionSources, packFailures, packAliases };
|
|
5957
6223
|
}
|
|
5958
|
-
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE;
|
|
6224
|
+
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE, warnedSemanticOutsidePack;
|
|
5959
6225
|
var init_custom_hooks_loader = __esm(() => {
|
|
5960
6226
|
init_hook_logger();
|
|
5961
6227
|
init_custom_hooks_registry();
|
|
@@ -5964,10 +6230,12 @@ var init_custom_hooks_loader = __esm(() => {
|
|
|
5964
6230
|
init_hook_telemetry();
|
|
5965
6231
|
init_telemetry_id();
|
|
5966
6232
|
init_fp_home();
|
|
6233
|
+
init_effective_reviewers();
|
|
5967
6234
|
init_policy_authority();
|
|
5968
6235
|
CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
|
|
5969
6236
|
policyModuleCache = new Map;
|
|
5970
6237
|
LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
|
|
6238
|
+
warnedSemanticOutsidePack = new Set;
|
|
5971
6239
|
});
|
|
5972
6240
|
|
|
5973
6241
|
// src/hooks/hook-activity-store.ts
|
|
@@ -7297,17 +7565,17 @@ var init_pi_projects = __esm(() => {
|
|
|
7297
7565
|
// lib/hermes-profiles.ts
|
|
7298
7566
|
import { existsSync as existsSync10, readdirSync as readdirSync3 } from "node:fs";
|
|
7299
7567
|
import { homedir as homedir10 } from "node:os";
|
|
7300
|
-
import { basename as basename2, dirname as
|
|
7568
|
+
import { basename as basename2, dirname as dirname4, join as join11, resolve as resolve8 } from "node:path";
|
|
7301
7569
|
function hermesRoot() {
|
|
7302
7570
|
const env = (process.env.HERMES_HOME || "").trim();
|
|
7303
7571
|
if (env) {
|
|
7304
7572
|
let home = resolve8(env);
|
|
7305
|
-
const parent =
|
|
7573
|
+
const parent = dirname4(home);
|
|
7306
7574
|
if (basename2(parent) === PROFILES_DIR)
|
|
7307
|
-
home =
|
|
7575
|
+
home = dirname4(parent);
|
|
7308
7576
|
const base = basename2(home);
|
|
7309
7577
|
if (base === STANDARD_HOME || base.startsWith(SIBLING_HOME_PREFIX)) {
|
|
7310
|
-
return join11(
|
|
7578
|
+
return join11(dirname4(home), STANDARD_HOME);
|
|
7311
7579
|
}
|
|
7312
7580
|
return home;
|
|
7313
7581
|
}
|
|
@@ -7341,7 +7609,7 @@ function listHermesProfiles() {
|
|
|
7341
7609
|
if (basename2(root) !== STANDARD_HOME)
|
|
7342
7610
|
return out;
|
|
7343
7611
|
let siblings = [];
|
|
7344
|
-
const parent =
|
|
7612
|
+
const parent = dirname4(root);
|
|
7345
7613
|
try {
|
|
7346
7614
|
siblings = readdirSync3(parent, { withFileTypes: true }).filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && entry.name.startsWith(SIBLING_HOME_PREFIX) && entry.name.length > SIBLING_HOME_PREFIX.length && existsSync10(join11(parent, entry.name, "config.yaml"))).map((entry) => ({
|
|
7347
7615
|
name: entry.name.slice(SIBLING_HOME_PREFIX.length),
|
|
@@ -9901,7 +10169,7 @@ var init_log_entries = __esm(() => {
|
|
|
9901
10169
|
// lib/codex-sessions.ts
|
|
9902
10170
|
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync12, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync10 } from "node:fs";
|
|
9903
10171
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
9904
|
-
import { dirname as
|
|
10172
|
+
import { dirname as dirname5, join as join24 } from "node:path";
|
|
9905
10173
|
import { homedir as homedir16 } from "node:os";
|
|
9906
10174
|
function readCache() {
|
|
9907
10175
|
try {
|
|
@@ -9914,7 +10182,7 @@ function readCache() {
|
|
|
9914
10182
|
}
|
|
9915
10183
|
function writeCacheEntry(sessionId, path) {
|
|
9916
10184
|
try {
|
|
9917
|
-
mkdirSync5(
|
|
10185
|
+
mkdirSync5(dirname5(CACHE_PATH), { recursive: true });
|
|
9918
10186
|
const cache = readCache();
|
|
9919
10187
|
cache[sessionId] = path;
|
|
9920
10188
|
writeFileSync4(CACHE_PATH, JSON.stringify(cache), "utf-8");
|
|
@@ -11011,6 +11279,8 @@ function missingGuards(input) {
|
|
|
11011
11279
|
continue;
|
|
11012
11280
|
if (outOfScope(err.clis, input.cli))
|
|
11013
11281
|
continue;
|
|
11282
|
+
if (err.semanticOnly)
|
|
11283
|
+
continue;
|
|
11014
11284
|
const declared = err.declared ?? [];
|
|
11015
11285
|
out.push({
|
|
11016
11286
|
packId: err.id ?? "(unnamed pack)",
|
|
@@ -11207,8 +11477,8 @@ var init_fp_reset = __esm(() => {
|
|
|
11207
11477
|
});
|
|
11208
11478
|
|
|
11209
11479
|
// src/hooks/semantic/jev-config.ts
|
|
11210
|
-
import { closeSync, constants as
|
|
11211
|
-
import { dirname as
|
|
11480
|
+
import { closeSync as closeSync2, constants as fsConstants2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, statSync as statSync15 } from "node:fs";
|
|
11481
|
+
import { dirname as dirname6 } from "node:path";
|
|
11212
11482
|
function jevConfigPath() {
|
|
11213
11483
|
return jevConfigFile();
|
|
11214
11484
|
}
|
|
@@ -11229,6 +11499,19 @@ function validateApiKey(key) {
|
|
|
11229
11499
|
return "the API key must be one line of visible ASCII with no spaces (at most 4096 characters)";
|
|
11230
11500
|
return null;
|
|
11231
11501
|
}
|
|
11502
|
+
function credentialQueryProblem(url) {
|
|
11503
|
+
for (const [name, value] of url.searchParams) {
|
|
11504
|
+
const shown = QUOTABLE_PARAM_RE.test(name) ? `?${name}=` : "one of its query parameters";
|
|
11505
|
+
const flat = name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
11506
|
+
if (CREDENTIAL_PARAM_PARTS.some((part) => flat.includes(part)) || CREDENTIAL_PARAM_NAMES.has(flat)) {
|
|
11507
|
+
return `baseUrl must not carry a credential in its query string, and ${shown} is one; put the key in the key field`;
|
|
11508
|
+
}
|
|
11509
|
+
if (looksLikeCredential(value)) {
|
|
11510
|
+
return `baseUrl's ${shown} value is shaped like a credential (not repeated here); put the key in the key field`;
|
|
11511
|
+
}
|
|
11512
|
+
}
|
|
11513
|
+
return null;
|
|
11514
|
+
}
|
|
11232
11515
|
function validateBaseUrl(raw) {
|
|
11233
11516
|
if (typeof raw !== "string" || raw.trim() === "")
|
|
11234
11517
|
return { ok: false, problem: "baseUrl must be a non-empty string" };
|
|
@@ -11245,6 +11528,9 @@ function validateBaseUrl(raw) {
|
|
|
11245
11528
|
return { ok: false, problem: "baseUrl must not carry credentials; put the key in the key field" };
|
|
11246
11529
|
if (url.hash)
|
|
11247
11530
|
return { ok: false, problem: "baseUrl must not have a #fragment" };
|
|
11531
|
+
const credentialQuery = credentialQueryProblem(url);
|
|
11532
|
+
if (credentialQuery)
|
|
11533
|
+
return { ok: false, problem: credentialQuery };
|
|
11248
11534
|
url.pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
11249
11535
|
return { ok: true, value: url.toString() };
|
|
11250
11536
|
}
|
|
@@ -11255,6 +11541,16 @@ function isPlainHttp(url) {
|
|
|
11255
11541
|
return false;
|
|
11256
11542
|
}
|
|
11257
11543
|
}
|
|
11544
|
+
function originOf(raw) {
|
|
11545
|
+
try {
|
|
11546
|
+
const url = new URL(raw);
|
|
11547
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname)))
|
|
11548
|
+
return null;
|
|
11549
|
+
return url.origin;
|
|
11550
|
+
} catch {
|
|
11551
|
+
return null;
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11258
11554
|
function looksLikeCredential(s) {
|
|
11259
11555
|
if (CREDENTIAL_PREFIX_RE.test(s))
|
|
11260
11556
|
return true;
|
|
@@ -11279,7 +11575,7 @@ function validateModel(raw) {
|
|
|
11279
11575
|
}
|
|
11280
11576
|
return { ok: true, value: raw };
|
|
11281
11577
|
}
|
|
11282
|
-
function validateJevConfig(raw, envKey) {
|
|
11578
|
+
function validateJevConfig(raw, envKey, cloud) {
|
|
11283
11579
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
11284
11580
|
return { ok: false, problem: "the file must hold a JSON object" };
|
|
11285
11581
|
const o = raw;
|
|
@@ -11289,7 +11585,25 @@ function validateJevConfig(raw, envKey) {
|
|
|
11289
11585
|
}
|
|
11290
11586
|
const kind = provider;
|
|
11291
11587
|
let apiKey;
|
|
11292
|
-
if (
|
|
11588
|
+
if (kind === JEV_CLOUD_PROVIDER) {
|
|
11589
|
+
if (o.apiKey !== undefined) {
|
|
11590
|
+
return {
|
|
11591
|
+
ok: false,
|
|
11592
|
+
problem: "provider failproofai takes its key from this machine's FailproofAI Cloud connection (credentials.json), " + "never from jev.json — remove apiKey from the file (not repeated here)"
|
|
11593
|
+
};
|
|
11594
|
+
}
|
|
11595
|
+
if (!cloud) {
|
|
11596
|
+
return {
|
|
11597
|
+
ok: false,
|
|
11598
|
+
problem: "this machine is not connected to FailproofAI Cloud with a key that carries jev:evaluate — " + "connect it with: failproofai config --token <key>",
|
|
11599
|
+
notConnected: true
|
|
11600
|
+
};
|
|
11601
|
+
}
|
|
11602
|
+
const bad = validateApiKey(cloud.key);
|
|
11603
|
+
if (bad)
|
|
11604
|
+
return { ok: false, problem: `the FailproofAI Cloud key in credentials.json is unusable: ${bad}` };
|
|
11605
|
+
apiKey = cloud.key;
|
|
11606
|
+
} else if (o.apiKey !== undefined) {
|
|
11293
11607
|
const bad = validateApiKey(o.apiKey);
|
|
11294
11608
|
if (bad)
|
|
11295
11609
|
return { ok: false, problem: bad };
|
|
@@ -11312,8 +11626,22 @@ function validateJevConfig(raw, envKey) {
|
|
|
11312
11626
|
if (!r.ok)
|
|
11313
11627
|
return r;
|
|
11314
11628
|
cfg.baseUrl = r.value;
|
|
11315
|
-
} else if (kind === "custom") {
|
|
11316
|
-
return { ok: false, problem:
|
|
11629
|
+
} else if (kind === "custom" || kind === JEV_CLOUD_PROVIDER) {
|
|
11630
|
+
return { ok: false, problem: `provider ${kind} needs a baseUrl` };
|
|
11631
|
+
}
|
|
11632
|
+
if (kind === JEV_CLOUD_PROVIDER && cloud) {
|
|
11633
|
+
const credentialOrigin = originOf(cloud.url);
|
|
11634
|
+
const baseOrigin = originOf(cfg.baseUrl);
|
|
11635
|
+
if (credentialOrigin === null) {
|
|
11636
|
+
return { ok: false, problem: "the FailproofAI Cloud credential in credentials.json names no usable origin — reconnect: failproofai config --token <key>" };
|
|
11637
|
+
}
|
|
11638
|
+
if (baseOrigin !== credentialOrigin) {
|
|
11639
|
+
return {
|
|
11640
|
+
ok: false,
|
|
11641
|
+
problem: `baseUrl is on ${baseOrigin ?? "an unusable origin"}, but this machine's FailproofAI Cloud key was issued for ${credentialOrigin} — ` + "the key is only ever sent to the origin it was issued for. Point jev.json back at it: failproofai jev setup --provider failproofai"
|
|
11642
|
+
};
|
|
11643
|
+
}
|
|
11644
|
+
cfg.credentialOrigin = credentialOrigin;
|
|
11317
11645
|
}
|
|
11318
11646
|
if (kind === "cloudflare") {
|
|
11319
11647
|
if (typeof o.accountId !== "string" || !CLOUDFLARE_ACCOUNT_ID_RE.test(o.accountId)) {
|
|
@@ -11337,18 +11665,38 @@ function validateJevConfig(raw, envKey) {
|
|
|
11337
11665
|
cfg.timeoutMs = t;
|
|
11338
11666
|
}
|
|
11339
11667
|
if (o.mode !== undefined) {
|
|
11340
|
-
if (o.mode !== "shadow" && o.mode !== "enforce")
|
|
11341
|
-
return { ok: false, problem: 'mode must be "shadow" or "enforce"' };
|
|
11668
|
+
if (o.mode !== "off" && o.mode !== "shadow" && o.mode !== "enforce")
|
|
11669
|
+
return { ok: false, problem: 'mode must be "off", "shadow" or "enforce"' };
|
|
11342
11670
|
cfg.mode = o.mode;
|
|
11343
11671
|
}
|
|
11344
|
-
if (cfg.baseUrl !== undefined && isPlainHttp(cfg.baseUrl) && cfg.mode
|
|
11672
|
+
if (cfg.baseUrl !== undefined && isPlainHttp(cfg.baseUrl) && cfg.mode === "enforce") {
|
|
11345
11673
|
return {
|
|
11346
11674
|
ok: false,
|
|
11347
|
-
problem: "plain http (to localhost) is accepted only with mode shadow: in enforce mode Jev's answers can clear a deny, " + "and while the local proxy is down any process on this machine could take its port and answer. Use https, or mode shadow"
|
|
11675
|
+
problem: "plain http (to localhost) is accepted only with mode shadow: in enforce mode Jev's answers can clear a deny, " + "and while the local proxy is down any process on this machine could take its port and answer. " + (cfg.provider === JEV_CLOUD_PROVIDER ? "Reconnect to an https FailproofAI Cloud URL (failproofai config --token <key> --url https://…), or keep mode shadow" : "Use https, or mode shadow")
|
|
11348
11676
|
};
|
|
11349
11677
|
}
|
|
11350
11678
|
return { ok: true, value: cfg };
|
|
11351
11679
|
}
|
|
11680
|
+
function validateLoadedJevConfig(cfg) {
|
|
11681
|
+
if (cfg.provider !== JEV_CLOUD_PROVIDER)
|
|
11682
|
+
return validateJevConfig(cfg);
|
|
11683
|
+
const { apiKey, credentialOrigin, ...rest } = cfg;
|
|
11684
|
+
if (typeof credentialOrigin !== "string" || credentialOrigin === "") {
|
|
11685
|
+
return {
|
|
11686
|
+
ok: false,
|
|
11687
|
+
problem: "this FailproofAI Cloud config names no credential origin — it did not come from the loader, so its key is not sent anywhere"
|
|
11688
|
+
};
|
|
11689
|
+
}
|
|
11690
|
+
return validateJevConfig(rest, null, { url: credentialOrigin, key: apiKey });
|
|
11691
|
+
}
|
|
11692
|
+
function namesNoKey(text) {
|
|
11693
|
+
try {
|
|
11694
|
+
const o = JSON.parse(text);
|
|
11695
|
+
return o !== null && typeof o === "object" && !Array.isArray(o) && o.apiKey === undefined;
|
|
11696
|
+
} catch {
|
|
11697
|
+
return false;
|
|
11698
|
+
}
|
|
11699
|
+
}
|
|
11352
11700
|
function readEnvKey() {
|
|
11353
11701
|
const v = process.env[JEV_API_KEY_ENV];
|
|
11354
11702
|
return v === undefined || v === "" ? null : v;
|
|
@@ -11357,8 +11705,8 @@ function looseConfigDirMode(path) {
|
|
|
11357
11705
|
if (!modesAreMeaningful())
|
|
11358
11706
|
return null;
|
|
11359
11707
|
try {
|
|
11360
|
-
const mode = statSync15(
|
|
11361
|
-
return (mode &
|
|
11708
|
+
const mode = statSync15(dirname6(path)).mode & 511;
|
|
11709
|
+
return (mode & DIR_WRITABLE_BY_OTHERS2) !== 0 ? mode : null;
|
|
11362
11710
|
} catch {
|
|
11363
11711
|
return null;
|
|
11364
11712
|
}
|
|
@@ -11370,7 +11718,7 @@ function inspectJevConfig() {
|
|
|
11370
11718
|
const path = jevConfigPath();
|
|
11371
11719
|
let fd;
|
|
11372
11720
|
try {
|
|
11373
|
-
fd =
|
|
11721
|
+
fd = openSync2(path, OPEN_FLAGS);
|
|
11374
11722
|
} catch (err) {
|
|
11375
11723
|
const code = err.code;
|
|
11376
11724
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -11380,18 +11728,35 @@ function inspectJevConfig() {
|
|
|
11380
11728
|
let text;
|
|
11381
11729
|
let mode = null;
|
|
11382
11730
|
try {
|
|
11383
|
-
const st =
|
|
11731
|
+
const st = fstatSync2(fd);
|
|
11384
11732
|
if (!st.isFile())
|
|
11385
11733
|
return { status: "refused", path, mode: null, reason: "unreadable", problem: "it is not a regular file" };
|
|
11386
11734
|
mode = st.mode & 511;
|
|
11735
|
+
const readText = () => {
|
|
11736
|
+
const buf = Buffer.alloc(st.size);
|
|
11737
|
+
let off = 0;
|
|
11738
|
+
while (off < buf.length) {
|
|
11739
|
+
const n = readSync2(fd, buf, off, buf.length - off, off);
|
|
11740
|
+
if (n === 0)
|
|
11741
|
+
break;
|
|
11742
|
+
off += n;
|
|
11743
|
+
}
|
|
11744
|
+
return buf.subarray(0, off).toString("utf8");
|
|
11745
|
+
};
|
|
11387
11746
|
if (modesAreMeaningful() && (mode & 63) !== 0) {
|
|
11747
|
+
let keyless = false;
|
|
11748
|
+
try {
|
|
11749
|
+
keyless = st.size <= MAX_CONFIG_BYTES && namesNoKey(readText());
|
|
11750
|
+
} catch {}
|
|
11751
|
+
const perms = mode.toString(8).padStart(4, "0");
|
|
11388
11752
|
return {
|
|
11389
11753
|
status: "refused",
|
|
11390
11754
|
path,
|
|
11391
11755
|
mode,
|
|
11392
11756
|
reason: "too-open",
|
|
11393
|
-
problem: `its permissions are ${
|
|
11394
|
-
fix: `chmod 600 ${path}
|
|
11757
|
+
problem: !keyless ? `its permissions are ${perms}; it holds a key, so it must be owner-only (chmod 600 ${path})` : (mode & 18) !== 0 ? `its permissions are ${perms}; other users could change it, and with it where Jev sends tool calls and prompts, so it must be owner-only (chmod 600 ${path})` : `its permissions are ${perms}; it must be owner-only (chmod 600 ${path})`,
|
|
11758
|
+
fix: `chmod 600 ${path}`,
|
|
11759
|
+
...keyless ? { keyless: true } : {}
|
|
11395
11760
|
};
|
|
11396
11761
|
}
|
|
11397
11762
|
const dirMode = looseConfigDirMode(path);
|
|
@@ -11401,26 +11766,18 @@ function inspectJevConfig() {
|
|
|
11401
11766
|
path,
|
|
11402
11767
|
mode,
|
|
11403
11768
|
reason: "too-open",
|
|
11404
|
-
problem: `its directory ${
|
|
11405
|
-
fix: `chmod 700 ${
|
|
11769
|
+
problem: `its directory ${dirname6(path)} is ${dirMode.toString(8).padStart(4, "0")} — other users can write there, ` + `so they can put their own config in its place; it must be owner-only (chmod 700 ${dirname6(path)})`,
|
|
11770
|
+
fix: `chmod 700 ${dirname6(path)}`
|
|
11406
11771
|
};
|
|
11407
11772
|
}
|
|
11408
11773
|
if (st.size > MAX_CONFIG_BYTES)
|
|
11409
11774
|
return { status: "refused", path, mode, reason: "too-large", problem: `it is larger than ${MAX_CONFIG_BYTES} bytes` };
|
|
11410
|
-
|
|
11411
|
-
let off = 0;
|
|
11412
|
-
while (off < buf.length) {
|
|
11413
|
-
const n = readSync(fd, buf, off, buf.length - off, off);
|
|
11414
|
-
if (n === 0)
|
|
11415
|
-
break;
|
|
11416
|
-
off += n;
|
|
11417
|
-
}
|
|
11418
|
-
text = buf.subarray(0, off).toString("utf8");
|
|
11775
|
+
text = readText();
|
|
11419
11776
|
} catch (err) {
|
|
11420
11777
|
return { status: "refused", path, mode, reason: "unreadable", problem: `cannot read it (${err.code ?? "error"})` };
|
|
11421
11778
|
} finally {
|
|
11422
11779
|
try {
|
|
11423
|
-
|
|
11780
|
+
closeSync2(fd);
|
|
11424
11781
|
} catch {}
|
|
11425
11782
|
}
|
|
11426
11783
|
let parsed;
|
|
@@ -11429,24 +11786,52 @@ function inspectJevConfig() {
|
|
|
11429
11786
|
} catch {
|
|
11430
11787
|
return { status: "refused", path, mode, reason: "not-json", problem: "it is not valid JSON" };
|
|
11431
11788
|
}
|
|
11789
|
+
const isObject = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
|
|
11790
|
+
const fields = isObject ? parsed : null;
|
|
11791
|
+
if (fields?.mode === "off") {
|
|
11792
|
+
const r = validateJevConfig(parsed, KEY_STAND_IN, standInCloudCredential(fields));
|
|
11793
|
+
if (!r.ok)
|
|
11794
|
+
return { status: "refused", path, mode, reason: "invalid", problem: r.problem };
|
|
11795
|
+
return { status: "off", path, mode, routing: routingOf(r.value) };
|
|
11796
|
+
}
|
|
11797
|
+
if (fields?.provider === JEV_CLOUD_PROVIDER) {
|
|
11798
|
+
const credential = readJevCloudCredential();
|
|
11799
|
+
if (credential.status === "refused") {
|
|
11800
|
+
return {
|
|
11801
|
+
status: "refused",
|
|
11802
|
+
path,
|
|
11803
|
+
mode,
|
|
11804
|
+
reason: credential.reason === "too-open" ? "too-open" : "unreadable",
|
|
11805
|
+
problem: `the FailproofAI Cloud credential was refused: ${credential.problem}`,
|
|
11806
|
+
...credential.fix ? { fix: credential.fix } : {},
|
|
11807
|
+
credentialsMode: credential.mode
|
|
11808
|
+
};
|
|
11809
|
+
}
|
|
11810
|
+
const r = validateJevConfig(parsed, null, credential.status === "ok" ? credential.credential : null);
|
|
11811
|
+
if (r.ok)
|
|
11812
|
+
return { status: "ok", path, mode, keySource: "cloud", config: r.value };
|
|
11813
|
+
if (r.notConnected === true) {
|
|
11814
|
+
const rest = validateJevConfig(parsed, null, standInCloudCredential(fields));
|
|
11815
|
+
if (!rest.ok)
|
|
11816
|
+
return { status: "refused", path, mode, reason: "invalid", problem: rest.problem };
|
|
11817
|
+
if (credential.status === "absent" && credential.connected) {
|
|
11818
|
+
return { status: "key-lacks-jev", path, mode, routing: routingOf(rest.value), problem: KEY_LACKS_JEV_PROBLEM };
|
|
11819
|
+
}
|
|
11820
|
+
return { status: "not-connected", path, mode, routing: routingOf(rest.value), problem: r.problem };
|
|
11821
|
+
}
|
|
11822
|
+
return { status: "refused", path, mode, reason: "invalid", problem: r.problem };
|
|
11823
|
+
}
|
|
11432
11824
|
const envKey = readEnvKey();
|
|
11433
11825
|
const r = validateJevConfig(parsed, envKey);
|
|
11434
11826
|
if (!r.ok) {
|
|
11435
11827
|
if (r.missingKey === true) {
|
|
11436
11828
|
const rest = validateJevConfig(parsed, KEY_STAND_IN);
|
|
11437
11829
|
if (rest.ok) {
|
|
11438
|
-
const routing = { provider: rest.value.provider, mode: rest.value.mode, timeoutMs: rest.value.timeoutMs };
|
|
11439
|
-
if (rest.value.baseUrl !== undefined)
|
|
11440
|
-
routing.baseUrl = rest.value.baseUrl;
|
|
11441
|
-
if (rest.value.accountId !== undefined)
|
|
11442
|
-
routing.accountId = rest.value.accountId;
|
|
11443
|
-
if (rest.value.model !== undefined)
|
|
11444
|
-
routing.model = rest.value.model;
|
|
11445
11830
|
return {
|
|
11446
11831
|
status: "key-missing",
|
|
11447
11832
|
path,
|
|
11448
11833
|
mode,
|
|
11449
|
-
routing,
|
|
11834
|
+
routing: routingOf(rest.value),
|
|
11450
11835
|
problem: `it carries no apiKey, so the key comes from ${JEV_API_KEY_ENV} — which is not set in this environment`
|
|
11451
11836
|
};
|
|
11452
11837
|
}
|
|
@@ -11457,33 +11842,56 @@ function inspectJevConfig() {
|
|
|
11457
11842
|
const keySource = parsed.apiKey !== undefined ? "file" : "env";
|
|
11458
11843
|
return { status: "ok", path, mode, keySource, config: r.value };
|
|
11459
11844
|
}
|
|
11845
|
+
function routingOf(cfg) {
|
|
11846
|
+
const routing = { provider: cfg.provider, mode: cfg.mode, timeoutMs: cfg.timeoutMs };
|
|
11847
|
+
if (cfg.baseUrl !== undefined)
|
|
11848
|
+
routing.baseUrl = cfg.baseUrl;
|
|
11849
|
+
if (cfg.accountId !== undefined)
|
|
11850
|
+
routing.accountId = cfg.accountId;
|
|
11851
|
+
if (cfg.model !== undefined)
|
|
11852
|
+
routing.model = cfg.model;
|
|
11853
|
+
return routing;
|
|
11854
|
+
}
|
|
11855
|
+
function standInCloudCredential(fields) {
|
|
11856
|
+
let url = "https://stand-in.invalid";
|
|
11857
|
+
if (typeof fields.baseUrl === "string") {
|
|
11858
|
+
try {
|
|
11859
|
+
url = new URL(fields.baseUrl.trim()).origin;
|
|
11860
|
+
} catch {}
|
|
11861
|
+
}
|
|
11862
|
+
return { url, key: KEY_STAND_IN };
|
|
11863
|
+
}
|
|
11460
11864
|
function loadJevConfig() {
|
|
11461
11865
|
try {
|
|
11462
11866
|
const r = inspectJevConfig();
|
|
11463
|
-
return r.status === "ok" ? r.config : null;
|
|
11867
|
+
return r.status === "ok" && r.config.mode !== "off" ? r.config : null;
|
|
11464
11868
|
} catch {
|
|
11465
11869
|
return null;
|
|
11466
11870
|
}
|
|
11467
11871
|
}
|
|
11468
|
-
var DEFAULT_JEV_MODE = "enforce", JEV_PROVIDER_KINDS, JEV_API_KEY_ENV = "FAILPROOFAI_JEV_API_KEY", JEV_CONFIG_DEFAULT_TIMEOUT_MS = 3000, MIN_JEV_TIMEOUT_MS = 100, MAX_JEV_TIMEOUT_MS = 1e4, JEV_CALIBRATED_FAMILY, MAX_CONFIG_BYTES, OPEN_FLAGS, VERSIONED_MODEL_RE, CLOUDFLARE_ACCOUNT_ID_RE, API_KEY_RE, MODEL_RE, LOOPBACK_HOSTS, CREDENTIAL_PREFIX_RE, KEY_STAND_IN = "supplied-by-the-environment",
|
|
11872
|
+
var DEFAULT_JEV_MODE = "enforce", JEV_PROVIDER_KINDS, JEV_CLOUD_PROVIDER = "failproofai", JEV_API_KEY_ENV = "FAILPROOFAI_JEV_API_KEY", JEV_CONFIG_DEFAULT_TIMEOUT_MS = 3000, MIN_JEV_TIMEOUT_MS = 100, MAX_JEV_TIMEOUT_MS = 1e4, JEV_CALIBRATED_FAMILY, MAX_CONFIG_BYTES, OPEN_FLAGS, VERSIONED_MODEL_RE, CLOUDFLARE_ACCOUNT_ID_RE, API_KEY_RE, MODEL_RE, LOOPBACK_HOSTS, CREDENTIAL_PARAM_PARTS, CREDENTIAL_PARAM_NAMES, QUOTABLE_PARAM_RE, CREDENTIAL_PREFIX_RE, KEY_STAND_IN = "supplied-by-the-environment", DIR_WRITABLE_BY_OTHERS2 = 18, KEY_LACKS_JEV_PROBLEM = "this machine is connected to FailproofAI Cloud, but no Jev key is stored for that connection (its key lacks jev:evaluate, or the connect could not confirm it)";
|
|
11469
11873
|
var init_jev_config = __esm(() => {
|
|
11874
|
+
init_fp_config();
|
|
11470
11875
|
init_fp_home();
|
|
11471
|
-
JEV_PROVIDER_KINDS = ["typesafe", "openrouter", "vercel", "cloudflare", "custom"];
|
|
11876
|
+
JEV_PROVIDER_KINDS = ["typesafe", "openrouter", "vercel", "cloudflare", "custom", "failproofai"];
|
|
11472
11877
|
JEV_CALIBRATED_FAMILY = { major: 1, minor: 13 };
|
|
11473
11878
|
MAX_CONFIG_BYTES = 64 * 1024;
|
|
11474
|
-
OPEN_FLAGS =
|
|
11879
|
+
OPEN_FLAGS = fsConstants2.O_RDONLY | (fsConstants2.O_NONBLOCK ?? 0);
|
|
11475
11880
|
VERSIONED_MODEL_RE = /^(?:(?:typesafe|typesafe-ai)\/)?jev-(\d{1,4})\.(\d{1,4})(?:\.(\d{1,6}))?(?:-(\d{8}))?$/;
|
|
11476
11881
|
CLOUDFLARE_ACCOUNT_ID_RE = /^[0-9a-f]{32}$/;
|
|
11477
11882
|
API_KEY_RE = /^[\x21-\x7e]{1,4096}$/;
|
|
11478
11883
|
MODEL_RE = /^[A-Za-z0-9._:/@~+-]{1,200}$/;
|
|
11479
11884
|
LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
11885
|
+
CREDENTIAL_PARAM_PARTS = ["key", "token", "secret", "password", "passwd", "credential", "auth", "signature"];
|
|
11886
|
+
CREDENTIAL_PARAM_NAMES = new Set(["pw", "pass", "sig", "sid", "jwt", "bearer", "session", "code", "access"]);
|
|
11887
|
+
QUOTABLE_PARAM_RE = /^[A-Za-z0-9._-]{1,40}$/;
|
|
11480
11888
|
CREDENTIAL_PREFIX_RE = /^(?:(?:sk|rk)[-_]|gh[pousr]_|github_pat_|glpat-|xox[abprs]-|AKIA[0-9A-Z]{12}|AIza[0-9A-Za-z_-]{20}|hf_[A-Za-z0-9]{16}|vck_)/;
|
|
11481
11889
|
});
|
|
11482
11890
|
|
|
11483
11891
|
// src/hooks/semantic/facts.ts
|
|
11484
11892
|
import { existsSync as existsSync18, readFileSync as readFileSync15, statSync as statSync16 } from "node:fs";
|
|
11485
11893
|
import { homedir as homedir20 } from "node:os";
|
|
11486
|
-
import { dirname as
|
|
11894
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3, resolve as resolve14 } from "node:path";
|
|
11487
11895
|
function classifyTool(toolName) {
|
|
11488
11896
|
if (SHELL_TOOLS.has(toolName))
|
|
11489
11897
|
return { toolClass: "shell", toolIsKnown: true };
|
|
@@ -11662,7 +12070,7 @@ function findGitDir(start) {
|
|
|
11662
12070
|
const candidate = resolve14(dir, ".git");
|
|
11663
12071
|
if (existsSync18(candidate))
|
|
11664
12072
|
return candidate;
|
|
11665
|
-
const parent =
|
|
12073
|
+
const parent = dirname7(dir);
|
|
11666
12074
|
if (parent === dir)
|
|
11667
12075
|
return null;
|
|
11668
12076
|
dir = parent;
|
|
@@ -11673,7 +12081,7 @@ function findProjectRoot(cwd) {
|
|
|
11673
12081
|
if (!cwd)
|
|
11674
12082
|
return null;
|
|
11675
12083
|
const gitDir = findGitDir(cwd);
|
|
11676
|
-
return gitDir ?
|
|
12084
|
+
return gitDir ? dirname7(gitDir) : cwd;
|
|
11677
12085
|
}
|
|
11678
12086
|
function readCurrentBranch(cwd) {
|
|
11679
12087
|
if (!cwd)
|
|
@@ -11688,7 +12096,7 @@ function readCurrentBranch(cwd) {
|
|
|
11688
12096
|
const m = /^gitdir:\s*(.+)$/.exec(pointer);
|
|
11689
12097
|
if (!m)
|
|
11690
12098
|
return null;
|
|
11691
|
-
headFile = resolve14(
|
|
12099
|
+
headFile = resolve14(dirname7(gitPath), m[1], "HEAD");
|
|
11692
12100
|
}
|
|
11693
12101
|
const head = readFileSync15(headFile, "utf8").trim();
|
|
11694
12102
|
const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
|
|
@@ -11697,9 +12105,9 @@ function readCurrentBranch(cwd) {
|
|
|
11697
12105
|
return null;
|
|
11698
12106
|
}
|
|
11699
12107
|
}
|
|
11700
|
-
function computeFacts(toolName, toolInput, cwd, permissionMode, scanned) {
|
|
12108
|
+
function computeFacts(toolName, toolInput, cwd, permissionMode, scanned, pinnedProjectRoot = null) {
|
|
11701
12109
|
const { toolClass, toolIsKnown } = classifyTool(toolName);
|
|
11702
|
-
const projectRoot = findProjectRoot(cwd);
|
|
12110
|
+
const projectRoot = pinnedProjectRoot ?? findProjectRoot(cwd);
|
|
11703
12111
|
return {
|
|
11704
12112
|
toolName,
|
|
11705
12113
|
toolClass,
|
|
@@ -11750,6 +12158,15 @@ function targetTokens(toolInput) {
|
|
|
11750
12158
|
out.add(t);
|
|
11751
12159
|
}
|
|
11752
12160
|
}
|
|
12161
|
+
if (out.size === 0) {
|
|
12162
|
+
for (const piece of command.split(/[;&|\n]+/)) {
|
|
12163
|
+
for (const tok of piece.trim().split(/\s+/).slice(1)) {
|
|
12164
|
+
if (!tok.startsWith("-"))
|
|
12165
|
+
for (const t of tokensOf(tok))
|
|
12166
|
+
out.add(t);
|
|
12167
|
+
}
|
|
12168
|
+
}
|
|
12169
|
+
}
|
|
11753
12170
|
return out;
|
|
11754
12171
|
}
|
|
11755
12172
|
for (const [key, value] of Object.entries(toolInput)) {
|
|
@@ -11790,6 +12207,8 @@ function decide(selected, answers, toolInput, userSaid, thresholds = DEFAULT_THR
|
|
|
11790
12207
|
const base = {
|
|
11791
12208
|
policy: p.name,
|
|
11792
12209
|
mode: p.mode,
|
|
12210
|
+
userCanOverride: p.userCanOverride,
|
|
12211
|
+
...p.origin ? { origin: p.origin } : {},
|
|
11793
12212
|
evidence,
|
|
11794
12213
|
exempt,
|
|
11795
12214
|
userAsked,
|
|
@@ -11869,6 +12288,8 @@ function decideV1(selected, answers, toolInput, userSaid, agentLastMessage, opts
|
|
|
11869
12288
|
const base = {
|
|
11870
12289
|
policy: p.name,
|
|
11871
12290
|
mode: p.mode,
|
|
12291
|
+
userCanOverride: p.userCanOverride,
|
|
12292
|
+
...p.origin ? { origin: p.origin } : {},
|
|
11872
12293
|
evidence,
|
|
11873
12294
|
exempt,
|
|
11874
12295
|
userAsked: op,
|
|
@@ -12187,7 +12608,7 @@ var init_policies = __esm(() => {
|
|
|
12187
12608
|
}
|
|
12188
12609
|
}
|
|
12189
12610
|
],
|
|
12190
|
-
guidance: "This sends secrets or private files to a remote destination
|
|
12611
|
+
guidance: "This sends secrets or private files to a remote destination, which the user cannot approve. Do not send them."
|
|
12191
12612
|
},
|
|
12192
12613
|
{
|
|
12193
12614
|
name: "remote-code-execution",
|
|
@@ -13306,6 +13727,14 @@ function literalValue(text, at, opts) {
|
|
|
13306
13727
|
}
|
|
13307
13728
|
return opts.stop.at > at ? { quote: "", from: at, to: opts.stop.at } : null;
|
|
13308
13729
|
}
|
|
13730
|
+
function looksRandomB64(t) {
|
|
13731
|
+
if (!/[+/]/.test(t))
|
|
13732
|
+
return false;
|
|
13733
|
+
const bare = t.replace(/=+$/, "");
|
|
13734
|
+
if (bare.split(/[+/]/).some((seg) => /^[A-Z]?[a-z]{3,}$/.test(seg) || /^[A-Z]{4,}$/.test(seg)))
|
|
13735
|
+
return false;
|
|
13736
|
+
return looksRandomToken(bare.replace(/[+/]/g, ""));
|
|
13737
|
+
}
|
|
13309
13738
|
function nameComponents(name) {
|
|
13310
13739
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
13311
13740
|
}
|
|
@@ -13707,6 +14136,7 @@ function redactSecretsDetailed(text, opts = {}) {
|
|
|
13707
14136
|
if (blunt)
|
|
13708
14137
|
out = redactCredentialArguments(out, c);
|
|
13709
14138
|
out = redactNamedSecrets(out, c);
|
|
14139
|
+
out = replaceCounting(out, LONG_B64_TOKEN_RE, (m, _g, offset, whole) => looksRandomB64(m) && !insideDigest(whole, offset) ? marker("high-entropy token") : null, c, "skip");
|
|
13710
14140
|
out = replaceCounting(out, LONG_TOKEN_RE, (m, _g, offset, whole) => looksRandomToken(m) && !insideDigest(whole, offset) ? marker("high-entropy token") : null, c, "skip");
|
|
13711
14141
|
return { text: out, count: c.n, found: c.found, weak: c.weak };
|
|
13712
14142
|
}
|
|
@@ -13757,7 +14187,7 @@ function redactNameValue(text, re, c, isSecret) {
|
|
|
13757
14187
|
re.lastIndex = 0;
|
|
13758
14188
|
return last === 0 ? text : out + text.slice(last);
|
|
13759
14189
|
}
|
|
13760
|
-
var marker = (label) => `<redacted:${label}>`, MAX_DELIMITED_RUN = 256, NOT_MID_WORD = "(?<![A-Za-z0-9_])", NOT_MID_HYPHENATED_WORD = "(?:(?<![A-Za-z0-9_-])|(?<=(?:^|[^A-Za-z0-9_-])-))", OPEN_ENDED, SHARED_RULES, SHARED_PATTERN_EXTENDED, PEM_ARMOUR_HEAD, PEM_HEADER_RE, PEM_BODY_RUN_RE, PEM_TO_CUT, PEM_TO_CUT_RE, PEM_CUT_FRAGMENT_RE, MIN_KEY_LINE = 16, KEY_BODY_MARK, PEM_LINE_BREAK_RE, isKeyMaterialLine = (line) => line.length >= MIN_KEY_LINE && isBase64Run(line), PEM_ARMOUR_LINE_RE, PEM_FOOTER_RE, BASE64_CHAR, VENDOR_RULES, WEBHOOK_RULES, URL_CREDENTIALS_RE, URL_TOKEN_USERINFO_RE, CREDENTIAL_HEADER_NAMES, CREDENTIAL_HEADER_RE, CREDENTIAL_FIELD_RE, AUTH_MARKER_HEAD = "<redacted:", AUTH_MARKER_RE, withoutMarkers = (s) => s.replace(AUTH_MARKER_RE, ""), CONTRACTION_TAIL_RE, VALUE_CLOSED_BY, MAX_CONTINUATION_LINES = 32, NOT_CREDENTIAL_CHARS_RE, NOT_CREDENTIAL_START_RE, DOTTED_IDENTIFIER_RE, BEARER_RE, CREDENTIAL_FLAGS, GATED_CREDENTIAL_FLAGS, BASIC_AUTH_FLAGS, BASIC_AUTH_COMMANDS, COMMAND_LOOKBACK = 120, FLAG_TOKEN_RE, WORD_BEFORE_RE, WORD_AFTER_RE, GATING_WORDS, CONFIG_SET_RE, ASSIGNMENT_NAME_RE, FLAG_NAME_RE, UNQUOTED_VALUE_STOP_RE, LONG_TOKEN_RE, STRONG_LAST_WORDS, STRONG_KEY_QUALIFIERS, WEAK_LAST_WORDS, NOT_SECRET_QUALIFIERS, META_SUFFIXES, SECRET_NAME_HINTS, NON_VALUE_WORDS, TYPED_ARRAY_RE, TYPE_WORDS, REFERENCE_START_RE, envSecretSource = null, envSecretCache = null, scrubbable = (secret) => secret.length >= 16 || secret.length >= 8 && tokenLike(secret), NO_SECRETS;
|
|
14190
|
+
var marker = (label) => `<redacted:${label}>`, MAX_DELIMITED_RUN = 256, NOT_MID_WORD = "(?<![A-Za-z0-9_])", NOT_MID_HYPHENATED_WORD = "(?:(?<![A-Za-z0-9_-])|(?<=(?:^|[^A-Za-z0-9_-])-))", OPEN_ENDED, SHARED_RULES, SHARED_PATTERN_EXTENDED, PEM_ARMOUR_HEAD, PEM_HEADER_RE, PEM_BODY_RUN_RE, PEM_TO_CUT, PEM_TO_CUT_RE, PEM_CUT_FRAGMENT_RE, MIN_KEY_LINE = 16, KEY_BODY_MARK, PEM_LINE_BREAK_RE, isKeyMaterialLine = (line) => line.length >= MIN_KEY_LINE && isBase64Run(line), PEM_ARMOUR_LINE_RE, PEM_FOOTER_RE, BASE64_CHAR, VENDOR_RULES, WEBHOOK_RULES, URL_CREDENTIALS_RE, URL_TOKEN_USERINFO_RE, CREDENTIAL_HEADER_NAMES, CREDENTIAL_HEADER_RE, CREDENTIAL_FIELD_RE, AUTH_MARKER_HEAD = "<redacted:", AUTH_MARKER_RE, withoutMarkers = (s) => s.replace(AUTH_MARKER_RE, ""), CONTRACTION_TAIL_RE, VALUE_CLOSED_BY, MAX_CONTINUATION_LINES = 32, NOT_CREDENTIAL_CHARS_RE, NOT_CREDENTIAL_START_RE, DOTTED_IDENTIFIER_RE, BEARER_RE, CREDENTIAL_FLAGS, GATED_CREDENTIAL_FLAGS, BASIC_AUTH_FLAGS, BASIC_AUTH_COMMANDS, COMMAND_LOOKBACK = 120, FLAG_TOKEN_RE, WORD_BEFORE_RE, WORD_AFTER_RE, GATING_WORDS, CONFIG_SET_RE, ASSIGNMENT_NAME_RE, FLAG_NAME_RE, UNQUOTED_VALUE_STOP_RE, LONG_TOKEN_RE, LONG_B64_TOKEN_RE, STRONG_LAST_WORDS, STRONG_KEY_QUALIFIERS, WEAK_LAST_WORDS, NOT_SECRET_QUALIFIERS, META_SUFFIXES, SECRET_NAME_HINTS, NON_VALUE_WORDS, TYPED_ARRAY_RE, TYPE_WORDS, REFERENCE_START_RE, envSecretSource = null, envSecretCache = null, scrubbable = (secret) => secret.length >= 16 || secret.length >= 8 && tokenLike(secret), NO_SECRETS;
|
|
13761
14191
|
var init_redact = __esm(() => {
|
|
13762
14192
|
init_builtin_policies();
|
|
13763
14193
|
OPEN_ENDED = /^(?:(\+)|(\*)|\{(\d+),\})/;
|
|
@@ -13874,6 +14304,7 @@ var init_redact = __esm(() => {
|
|
|
13874
14304
|
FLAG_NAME_RE = /(^|\\[nrt]|[^A-Za-z0-9_.-])(--?[A-Za-z][A-Za-z0-9_-]*)([ \t]+)/g;
|
|
13875
14305
|
UNQUOTED_VALUE_STOP_RE = /[\s"'`<>(){}[\],;&|\\]/;
|
|
13876
14306
|
LONG_TOKEN_RE = /[A-Za-z0-9_-]{32,}/g;
|
|
14307
|
+
LONG_B64_TOKEN_RE = /[A-Za-z0-9+/_-]{32,}={0,2}/g;
|
|
13877
14308
|
STRONG_LAST_WORDS = new Set([
|
|
13878
14309
|
"secret",
|
|
13879
14310
|
"password",
|
|
@@ -14373,7 +14804,7 @@ function scrubDeep(value, acc, scrubber) {
|
|
|
14373
14804
|
}
|
|
14374
14805
|
return Object.fromEntries(out);
|
|
14375
14806
|
}
|
|
14376
|
-
function buildEnvelope(toolInput, userSaid, facts,
|
|
14807
|
+
function buildEnvelope(toolInput, userSaid, facts, _scanned, opts = {}) {
|
|
14377
14808
|
const limits = opts.limits ?? DEFAULT_ENVELOPE_LIMITS;
|
|
14378
14809
|
const acc = {
|
|
14379
14810
|
redactions: 0,
|
|
@@ -14389,7 +14820,6 @@ function buildEnvelope(toolInput, userSaid, facts, scanned, opts = {}) {
|
|
|
14389
14820
|
const turns = Array.isArray(userSaid) ? userSaid : [];
|
|
14390
14821
|
const f = facts && typeof facts === "object" ? facts : {};
|
|
14391
14822
|
const rawCommand = typeof input0.command === "string" ? input0.command : null;
|
|
14392
|
-
const scanIncomplete = rawCommand !== null && rawCommand.length > MAX_SCAN_CHARS;
|
|
14393
14823
|
openBudget(acc, limits.contextChars);
|
|
14394
14824
|
enter(acc, "facts");
|
|
14395
14825
|
const agentLastRaw = asText(opts.agentLastMessage);
|
|
@@ -14430,10 +14860,8 @@ function buildEnvelope(toolInput, userSaid, facts, scanned, opts = {}) {
|
|
|
14430
14860
|
const agentLastSent = agentLast === null ? null : cleanString(agentLast, limits.messageChars, acc);
|
|
14431
14861
|
openBudget(acc, limits.requestChars);
|
|
14432
14862
|
enter(acc, "request");
|
|
14433
|
-
const stripped = scanned && rawCommand !== null && !scanIncomplete ? asText(scanned.withoutComments) : null;
|
|
14434
|
-
const judged = stripped ?? rawCommand;
|
|
14435
14863
|
shell(acc, true);
|
|
14436
|
-
const command =
|
|
14864
|
+
const command = rawCommand === null ? null : cleanString(rawCommand, limits.stringChars, acc);
|
|
14437
14865
|
shell(acc, false);
|
|
14438
14866
|
const toolForRequest = factStringIn(f.toolName, limits.factChars, acc);
|
|
14439
14867
|
const readable = entriesOf(input0);
|
|
@@ -14442,10 +14870,6 @@ function buildEnvelope(toolInput, userSaid, facts, scanned, opts = {}) {
|
|
|
14442
14870
|
const rest = (readable ?? []).filter(([k]) => !(k === "command" && command !== null));
|
|
14443
14871
|
shell(acc, f.toolIsKnown !== true);
|
|
14444
14872
|
const input = buildObject(rest, acc, limits, 0);
|
|
14445
|
-
shell(acc, false);
|
|
14446
|
-
shell(acc, true);
|
|
14447
|
-
const removedComments = scanned?.commentsRemoved && !scanIncomplete ? cleanString((scanned.comments ?? []).join(`
|
|
14448
|
-
`), limits.stringChars, acc) : null;
|
|
14449
14873
|
shell(acc, false);
|
|
14450
14874
|
const state = {
|
|
14451
14875
|
how_to_read: howToRead,
|
|
@@ -14455,8 +14879,6 @@ function buildEnvelope(toolInput, userSaid, facts, scanned, opts = {}) {
|
|
|
14455
14879
|
agent_request: {
|
|
14456
14880
|
tool: toolForRequest,
|
|
14457
14881
|
input: command === null ? input : { command, ...input },
|
|
14458
|
-
...removedComments !== null ? { shell_comments_removed: true, removed_shell_comments: removedComments } : {},
|
|
14459
|
-
...scanIncomplete ? { shell_comments_not_removed: true } : {},
|
|
14460
14882
|
...acc.requestCut ? { request_was_cut: true } : {}
|
|
14461
14883
|
}
|
|
14462
14884
|
};
|
|
@@ -14483,7 +14905,6 @@ function buildEnvelope(toolInput, userSaid, facts, scanned, opts = {}) {
|
|
|
14483
14905
|
}
|
|
14484
14906
|
var MAX_STRING_CHARS = 128000, MAX_AGENT_REQUEST_CHARS = 128000, MAX_CONTEXT_CHARS = 32000, MAX_USER_MESSAGE_CHARS = 6000, MAX_USER_MESSAGES = 3, MAX_FACT_CHARS = 2000, MAX_KEY_CHARS = 256, MAX_DEPTH = 64, STATE_OVERHEAD = 1024, MAX_STATE_CHARS, DEFAULT_ENVELOPE_LIMITS, OMITTED = "…", TOO_DEEP = "<nested value omitted>", UNREPRESENTABLE = "<value omitted>", SHELL_METACHARACTERS, NEEDS_SANITISING, EMPTY_STRING_COST = 2, SEPARATOR_COST = 1, CONTAINER_COST = 2, CUT_STOP, CUT_SNAP_MAX = 256, isEscapeLetter = (c) => c === "n" || c === "r" || c === "t";
|
|
14485
14907
|
var init_envelope = __esm(() => {
|
|
14486
|
-
init_facts();
|
|
14487
14908
|
init_redact();
|
|
14488
14909
|
init_redact();
|
|
14489
14910
|
MAX_STATE_CHARS = MAX_AGENT_REQUEST_CHARS + MAX_CONTEXT_CHARS + STATE_OVERHEAD;
|
|
@@ -14520,18 +14941,18 @@ __export(exports_intent, {
|
|
|
14520
14941
|
});
|
|
14521
14942
|
import {
|
|
14522
14943
|
chmodSync as chmodSync2,
|
|
14523
|
-
closeSync as
|
|
14944
|
+
closeSync as closeSync3,
|
|
14524
14945
|
mkdirSync as mkdirSync8,
|
|
14525
|
-
openSync as
|
|
14946
|
+
openSync as openSync3,
|
|
14526
14947
|
readdirSync as readdirSync13,
|
|
14527
14948
|
readFileSync as readFileSync16,
|
|
14528
|
-
readSync as
|
|
14949
|
+
readSync as readSync3,
|
|
14529
14950
|
renameSync as renameSync4,
|
|
14530
14951
|
statSync as statSync17,
|
|
14531
14952
|
unlinkSync as unlinkSync2,
|
|
14532
14953
|
writeFileSync as writeFileSync6
|
|
14533
14954
|
} from "node:fs";
|
|
14534
|
-
import { dirname as
|
|
14955
|
+
import { dirname as dirname8, resolve as resolve15 } from "node:path";
|
|
14535
14956
|
function modesAreMeaningful2() {
|
|
14536
14957
|
return process.platform !== "win32";
|
|
14537
14958
|
}
|
|
@@ -14543,7 +14964,7 @@ function storeDirChain() {
|
|
|
14543
14964
|
chain.push(dir);
|
|
14544
14965
|
if (dir === home)
|
|
14545
14966
|
break;
|
|
14546
|
-
const parent =
|
|
14967
|
+
const parent = dirname8(dir);
|
|
14547
14968
|
if (parent === dir)
|
|
14548
14969
|
break;
|
|
14549
14970
|
dir = parent;
|
|
@@ -14560,11 +14981,11 @@ function looseIntentStoreDir() {
|
|
|
14560
14981
|
} catch {
|
|
14561
14982
|
continue;
|
|
14562
14983
|
}
|
|
14563
|
-
if ((mode &
|
|
14984
|
+
if ((mode & DIR_WRITABLE_BY_OTHERS3) === 0)
|
|
14564
14985
|
continue;
|
|
14565
14986
|
try {
|
|
14566
|
-
chmodSync2(dir, mode & ~
|
|
14567
|
-
if ((statSync17(dir).mode &
|
|
14987
|
+
chmodSync2(dir, mode & ~DIR_WRITABLE_BY_OTHERS3);
|
|
14988
|
+
if ((statSync17(dir).mode & DIR_WRITABLE_BY_OTHERS3) !== 0)
|
|
14568
14989
|
return dir;
|
|
14569
14990
|
} catch {
|
|
14570
14991
|
return dir;
|
|
@@ -14929,7 +15350,7 @@ function visitLinesBackwards(transcriptPath, maxBytes, visit) {
|
|
|
14929
15350
|
const st = statSync17(path);
|
|
14930
15351
|
if (!st.isFile() || st.size === 0)
|
|
14931
15352
|
return;
|
|
14932
|
-
fd =
|
|
15353
|
+
fd = openSync3(path, "r");
|
|
14933
15354
|
let end = st.size;
|
|
14934
15355
|
let consumed = 0;
|
|
14935
15356
|
let partial = Buffer.alloc(0);
|
|
@@ -14937,7 +15358,7 @@ function visitLinesBackwards(transcriptPath, maxBytes, visit) {
|
|
|
14937
15358
|
const len = Math.min(TAIL_CHUNK_BYTES, end, maxBytes - consumed);
|
|
14938
15359
|
const start = end - len;
|
|
14939
15360
|
const chunk = Buffer.alloc(len);
|
|
14940
|
-
|
|
15361
|
+
readSync3(fd, chunk, 0, len, start);
|
|
14941
15362
|
consumed += len;
|
|
14942
15363
|
end = start;
|
|
14943
15364
|
const buf = partial.length > 0 ? Buffer.concat([chunk, partial]) : chunk;
|
|
@@ -14958,13 +15379,13 @@ function visitLinesBackwards(transcriptPath, maxBytes, visit) {
|
|
|
14958
15379
|
}
|
|
14959
15380
|
if (end > 0 && partial.length > 0) {
|
|
14960
15381
|
const before = Buffer.alloc(1);
|
|
14961
|
-
if (
|
|
15382
|
+
if (readSync3(fd, before, 0, 1, end - 1) === 1 && before[0] === 10)
|
|
14962
15383
|
visit(partial);
|
|
14963
15384
|
}
|
|
14964
15385
|
} catch {} finally {
|
|
14965
15386
|
if (fd !== undefined) {
|
|
14966
15387
|
try {
|
|
14967
|
-
|
|
15388
|
+
closeSync3(fd);
|
|
14968
15389
|
} catch {}
|
|
14969
15390
|
}
|
|
14970
15391
|
}
|
|
@@ -14999,7 +15420,7 @@ function readIntent(sessionId, now = Date.now()) {
|
|
|
14999
15420
|
const agent = latest && typeof latest.agent === "string" && latest.agent.length > 0 ? latest.agent : null;
|
|
15000
15421
|
return { userSaid: prompts.map((p) => p.text), agentLastMessage: agent };
|
|
15001
15422
|
}
|
|
15002
|
-
var MAX_RECORDED_PROMPTS = 5, INTENT_MAX_AGE_MS, MAX_CLOCK_SKEW_MS, SESSION_ID_RE2, sessionsDir = () => resolve15(semanticDir(), "sessions"), intentFile = (sessionId) => resolve15(sessionsDir(), `${sessionId}.json`),
|
|
15423
|
+
var MAX_RECORDED_PROMPTS = 5, INTENT_MAX_AGE_MS, MAX_CLOCK_SKEW_MS, SESSION_ID_RE2, sessionsDir = () => resolve15(semanticDir(), "sessions"), intentFile = (sessionId) => resolve15(sessionsDir(), `${sessionId}.json`), DIR_WRITABLE_BY_OTHERS3 = 18, SESSION_FILE_RE3, NON_HUMAN_PREFIXES, CONTINUATION_PREFIX = "This session is being continued from a previous conversation", IDE_MACHINE_OPENERS, IDE_AMBIGUOUS_OPENERS, IDE_REQUEST_HEADINGS, obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : undefined, CLAUDE_MACHINE_SOURCES, claudeMachineTurn = (payload) => typeof payload.source === "string" && CLAUDE_MACHINE_SOURCES.has(payload.source), piMachineTurn = (payload) => payload.input_source === "extension" || payload.source === "extension", openclawMachineRun = (payload) => {
|
|
15003
15424
|
const meta = obj(payload.openclaw);
|
|
15004
15425
|
if (!meta)
|
|
15005
15426
|
return false;
|
|
@@ -15082,26 +15503,37 @@ var init_intent = __esm(() => {
|
|
|
15082
15503
|
});
|
|
15083
15504
|
|
|
15084
15505
|
// src/hooks/semantic/jev-client.ts
|
|
15085
|
-
function
|
|
15506
|
+
function printable(s) {
|
|
15507
|
+
return s.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ");
|
|
15508
|
+
}
|
|
15509
|
+
function providerErrorDetail(body, secret) {
|
|
15086
15510
|
const b = body;
|
|
15087
15511
|
let detail = "";
|
|
15088
15512
|
if (b && typeof b === "object") {
|
|
15089
15513
|
if (Array.isArray(b.errors)) {
|
|
15090
15514
|
detail = b.errors.map((e) => typeof e?.message === "string" ? e.message : "").filter(Boolean).join("; ");
|
|
15091
15515
|
}
|
|
15092
|
-
if (!detail && b.error && typeof b.
|
|
15093
|
-
|
|
15094
|
-
if (typeof m === "string")
|
|
15095
|
-
detail = m;
|
|
15516
|
+
if (!detail && typeof b.error === "string" && b.error && typeof b.message === "string" && b.message) {
|
|
15517
|
+
detail = `${b.error}: ${b.message}`;
|
|
15096
15518
|
}
|
|
15097
|
-
if (!detail
|
|
15098
|
-
detail = b.error;
|
|
15519
|
+
if (!detail)
|
|
15520
|
+
detail = messageOf(b.error);
|
|
15099
15521
|
if (!detail && typeof b.message === "string")
|
|
15100
15522
|
detail = b.message;
|
|
15101
|
-
if (!detail
|
|
15102
|
-
detail = b.detail;
|
|
15523
|
+
if (!detail)
|
|
15524
|
+
detail = messageOf(b.detail);
|
|
15525
|
+
}
|
|
15526
|
+
return printable(scrubSecret(detail, secret)).slice(0, MAX_ERROR_DETAIL);
|
|
15527
|
+
}
|
|
15528
|
+
function messageOf(value) {
|
|
15529
|
+
if (typeof value === "string")
|
|
15530
|
+
return value;
|
|
15531
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
15532
|
+
const m = value.message;
|
|
15533
|
+
if (typeof m === "string")
|
|
15534
|
+
return m;
|
|
15103
15535
|
}
|
|
15104
|
-
return
|
|
15536
|
+
return "";
|
|
15105
15537
|
}
|
|
15106
15538
|
function scrubSecret(text, secret) {
|
|
15107
15539
|
return secret.length >= 4 ? text.split(secret).join("[key]") : text;
|
|
@@ -15109,10 +15541,25 @@ function scrubSecret(text, secret) {
|
|
|
15109
15541
|
function paymentRequiredCode(detail) {
|
|
15110
15542
|
return MODEL_EXECUTION_402_RE.test(detail) ? JEV_REASON_PROVIDER_REFUSED : "out-of-credits";
|
|
15111
15543
|
}
|
|
15544
|
+
function isSystemOneUrl(url) {
|
|
15545
|
+
try {
|
|
15546
|
+
return /\/systemone$/i.test(new URL(url).pathname);
|
|
15547
|
+
} catch {
|
|
15548
|
+
return false;
|
|
15549
|
+
}
|
|
15550
|
+
}
|
|
15551
|
+
function httpFailureMessage(status, url, detail, derivedBase = false) {
|
|
15552
|
+
if (status === 404 && derivedBase)
|
|
15553
|
+
return `nothing is served at ${displayEndpoint(url)}${detail ? ` (${detail})` : ""}`;
|
|
15554
|
+
if (status === 404 && isSystemOneUrl(url)) {
|
|
15555
|
+
return `nothing is served at ${displayEndpoint(url)}${detail ? ` (${detail})` : ""}. ` + "`/systemone` is appended to the base URL you configured, and every Jev route serves it at the provider's version root — " + "so this is a base URL that is wrong, not a provider that is down.";
|
|
15556
|
+
}
|
|
15557
|
+
return detail || `HTTP ${status}`;
|
|
15558
|
+
}
|
|
15112
15559
|
function isRedirect(res) {
|
|
15113
15560
|
return res.type === "opaqueredirect" || res.status >= 300 && res.status < 400;
|
|
15114
15561
|
}
|
|
15115
|
-
async function postJson(url, bearer, body, signal) {
|
|
15562
|
+
async function postJson(url, bearer, body, signal, derivedBase = false) {
|
|
15116
15563
|
let res;
|
|
15117
15564
|
try {
|
|
15118
15565
|
res = await fetch(url, {
|
|
@@ -15134,6 +15581,7 @@ async function postJson(url, bearer, body, signal) {
|
|
|
15134
15581
|
const status = res.status >= 300 && res.status < 400 ? String(res.status) : "3xx";
|
|
15135
15582
|
throw new JevError(`http-${status}`, `HTTP ${status}: the endpoint answered with a redirect, which is never followed`);
|
|
15136
15583
|
}
|
|
15584
|
+
const retryAfter = res.status === 429 || res.status === 503 ? res.headers.get("retry-after") : null;
|
|
15137
15585
|
let parsed;
|
|
15138
15586
|
try {
|
|
15139
15587
|
parsed = await res.json();
|
|
@@ -15143,15 +15591,15 @@ async function postJson(url, bearer, body, signal) {
|
|
|
15143
15591
|
if (res.status === 402)
|
|
15144
15592
|
throw new JevError("out-of-credits", "HTTP 402: the account is out of credits");
|
|
15145
15593
|
if (!res.ok)
|
|
15146
|
-
throw new JevError(`http-${res.status}`,
|
|
15594
|
+
throw new JevError(`http-${res.status}`, httpFailureMessage(res.status, url, "", derivedBase), { retryAfter });
|
|
15147
15595
|
throw new JevError("malformed", "response body is not JSON");
|
|
15148
15596
|
}
|
|
15149
15597
|
if (res.status === 402) {
|
|
15150
|
-
const detail =
|
|
15598
|
+
const detail = providerErrorDetail(parsed, bearer);
|
|
15151
15599
|
throw new JevError(paymentRequiredCode(detail), detail || "HTTP 402: the account is out of credits");
|
|
15152
15600
|
}
|
|
15153
15601
|
if (!res.ok) {
|
|
15154
|
-
throw new JevError(`http-${res.status}`,
|
|
15602
|
+
throw new JevError(`http-${res.status}`, httpFailureMessage(res.status, url, providerErrorDetail(parsed, bearer), derivedBase), { retryAfter });
|
|
15155
15603
|
}
|
|
15156
15604
|
return parsed;
|
|
15157
15605
|
}
|
|
@@ -15165,7 +15613,7 @@ function normalizeNative(body, sentModel, opts) {
|
|
|
15165
15613
|
if (typeof b.answers !== "object" || b.answers === null) {
|
|
15166
15614
|
if (b.error !== undefined) {
|
|
15167
15615
|
const code = typeof b.error === "object" && b.error !== null ? b.error.code : undefined;
|
|
15168
|
-
const detail =
|
|
15616
|
+
const detail = providerErrorDetail(body, opts.apiKey) || "the provider reported an error";
|
|
15169
15617
|
if (code === 402 || code === "402")
|
|
15170
15618
|
throw new JevError(paymentRequiredCode(detail), detail);
|
|
15171
15619
|
if (typeof code === "number" && (code === 429 || code >= 500))
|
|
@@ -15196,7 +15644,7 @@ function nativeTransport(opts) {
|
|
|
15196
15644
|
return async (request, signal) => {
|
|
15197
15645
|
const model = opts.model ?? request.model;
|
|
15198
15646
|
const body = { ...request, model, ...opts.extraBody ?? {} };
|
|
15199
|
-
return normalizeNative(await postJson(opts.url, opts.apiKey, body, signal), model, opts);
|
|
15647
|
+
return normalizeNative(await postJson(opts.url, opts.apiKey, body, signal, opts.derivedBase), model, opts);
|
|
15200
15648
|
};
|
|
15201
15649
|
}
|
|
15202
15650
|
function cloudflareTransport(token, accountId, opts = {}) {
|
|
@@ -15271,7 +15719,7 @@ function nativeEndpoint(baseUrl) {
|
|
|
15271
15719
|
return url.toString();
|
|
15272
15720
|
}
|
|
15273
15721
|
function validated(cfg) {
|
|
15274
|
-
const v =
|
|
15722
|
+
const v = validateLoadedJevConfig(cfg);
|
|
15275
15723
|
if (!v.ok)
|
|
15276
15724
|
throw new JevError("config", v.problem);
|
|
15277
15725
|
return v.value;
|
|
@@ -15286,9 +15734,49 @@ function jevRoute(input) {
|
|
|
15286
15734
|
}
|
|
15287
15735
|
const base = cfg.baseUrl ?? defaults.baseUrl;
|
|
15288
15736
|
if (!base)
|
|
15289
|
-
throw new JevError("config",
|
|
15737
|
+
throw new JevError("config", `provider ${cfg.provider} needs a baseUrl`);
|
|
15290
15738
|
return { via: cfg.provider, endpoint: nativeEndpoint(base), model, modelIsDefault };
|
|
15291
15739
|
}
|
|
15740
|
+
function displayEndpoint(endpoint) {
|
|
15741
|
+
try {
|
|
15742
|
+
const url = new URL(endpoint);
|
|
15743
|
+
const shown = `${url.origin}${url.pathname}`;
|
|
15744
|
+
return url.search ? `${shown}?…` : shown;
|
|
15745
|
+
} catch {
|
|
15746
|
+
return "(invalid URL)";
|
|
15747
|
+
}
|
|
15748
|
+
}
|
|
15749
|
+
function retryAfterMs(header, nowEpochMs = Date.now()) {
|
|
15750
|
+
const raw = header?.trim() ?? "";
|
|
15751
|
+
let ms;
|
|
15752
|
+
if (/^\d{1,10}$/.test(raw)) {
|
|
15753
|
+
ms = Number(raw) * 1000;
|
|
15754
|
+
} else if (IMF_FIXDATE_RE.test(raw) && Number.isFinite(Date.parse(raw))) {
|
|
15755
|
+
ms = Date.parse(raw) - nowEpochMs;
|
|
15756
|
+
} else {
|
|
15757
|
+
return JEV_CLOUD_RETRY_AFTER_DEFAULT_MS;
|
|
15758
|
+
}
|
|
15759
|
+
return Math.min(JEV_CLOUD_RETRY_AFTER_CAP_MS, Math.max(0, ms));
|
|
15760
|
+
}
|
|
15761
|
+
function cloudRetryAfter(endpoint, transport) {
|
|
15762
|
+
return async (request, signal) => {
|
|
15763
|
+
const now = performance.now();
|
|
15764
|
+
if (cloudCooldown.endpoint === endpoint && now < cloudCooldown.until) {
|
|
15765
|
+
const seconds = Math.max(1, Math.ceil((cloudCooldown.until - now) / 1000));
|
|
15766
|
+
throw new JevError(cloudCooldown.code, cloudCooldown.code === "http-503" ? `FailproofAI Cloud could not serve Jev; asking again in ${seconds}s, so this one was not sent` : `FailproofAI Cloud asked for no Jev requests for ${seconds}s more (Retry-After), so this one was not sent`);
|
|
15767
|
+
}
|
|
15768
|
+
try {
|
|
15769
|
+
return await transport(request, signal);
|
|
15770
|
+
} catch (err) {
|
|
15771
|
+
if (err instanceof JevError && (err.code === "http-429" || err.code === "http-503")) {
|
|
15772
|
+
cloudCooldown.endpoint = endpoint;
|
|
15773
|
+
cloudCooldown.code = err.code;
|
|
15774
|
+
cloudCooldown.until = performance.now() + (err.code === "http-503" && err.retryAfter === null ? JEV_CLOUD_RETRY_AFTER_CAP_MS : retryAfterMs(err.retryAfter));
|
|
15775
|
+
}
|
|
15776
|
+
throw err;
|
|
15777
|
+
}
|
|
15778
|
+
};
|
|
15779
|
+
}
|
|
15292
15780
|
function transportForConfig(input) {
|
|
15293
15781
|
const cfg = validated(input);
|
|
15294
15782
|
const route = jevRoute(cfg);
|
|
@@ -15340,9 +15828,21 @@ function transportForConfig(input) {
|
|
|
15340
15828
|
via: "custom",
|
|
15341
15829
|
model: route.model
|
|
15342
15830
|
};
|
|
15831
|
+
case "failproofai":
|
|
15832
|
+
return {
|
|
15833
|
+
transport: cloudRetryAfter(route.endpoint, nativeTransport({
|
|
15834
|
+
url: route.endpoint,
|
|
15835
|
+
apiKey: cfg.apiKey,
|
|
15836
|
+
model: route.model,
|
|
15837
|
+
allowUnreported: false,
|
|
15838
|
+
derivedBase: true
|
|
15839
|
+
})),
|
|
15840
|
+
via: "failproofai",
|
|
15841
|
+
model: route.model
|
|
15842
|
+
};
|
|
15343
15843
|
}
|
|
15344
15844
|
}
|
|
15345
|
-
var CLOUDFLARE_JEV_MODEL = "typesafe/jev", VERCEL_JEV_MODEL = "typesafe-ai/jev", cloudflareRunEndpoint = (accountId, apiBase = JEV_PROVIDER_DEFAULTS.cloudflare.baseUrl) => `${apiBase.replace(/\/+$/, "")}/accounts/${accountId}/ai/run`, JEV_USD_PER_INPUT_TOKEN, JEV_PROVIDER_DEFAULTS, OPENROUTER_PROVIDER_PREFS, JevError, MAX_ERROR_DETAIL = 300, MODEL_EXECUTION_402_RE;
|
|
15845
|
+
var CLOUDFLARE_JEV_MODEL = "typesafe/jev", VERCEL_JEV_MODEL = "typesafe-ai/jev", cloudflareRunEndpoint = (accountId, apiBase = JEV_PROVIDER_DEFAULTS.cloudflare.baseUrl) => `${apiBase.replace(/\/+$/, "")}/accounts/${accountId}/ai/run`, JEV_USD_PER_INPUT_TOKEN, JEV_PROVIDER_DEFAULTS, OPENROUTER_PROVIDER_PREFS, JevError, MAX_ERROR_DETAIL = 300, MODEL_EXECUTION_402_RE, JEV_CLOUD_RETRY_AFTER_CAP_MS = 60000, JEV_CLOUD_RETRY_AFTER_DEFAULT_MS = 5000, cloudCooldown, IMF_FIXDATE_RE;
|
|
15346
15846
|
var init_jev_client = __esm(() => {
|
|
15347
15847
|
init_jev_activity();
|
|
15348
15848
|
init_jev_config();
|
|
@@ -15352,18 +15852,23 @@ var init_jev_client = __esm(() => {
|
|
|
15352
15852
|
openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "typesafe/jev-1.13" },
|
|
15353
15853
|
vercel: { baseUrl: "https://ai-gateway.vercel.sh/typesafe/v1", model: VERCEL_JEV_MODEL },
|
|
15354
15854
|
cloudflare: { baseUrl: "https://api.cloudflare.com/client/v4", model: CLOUDFLARE_JEV_MODEL },
|
|
15355
|
-
custom: { baseUrl: null, model: "jev-1.13.0" }
|
|
15855
|
+
custom: { baseUrl: null, model: "jev-1.13.0" },
|
|
15856
|
+
failproofai: { baseUrl: null, model: "jev-1.13.0" }
|
|
15356
15857
|
};
|
|
15357
15858
|
OPENROUTER_PROVIDER_PREFS = { zdr: true, data_collection: "deny", allow_fallbacks: false };
|
|
15358
15859
|
JevError = class JevError extends Error {
|
|
15359
15860
|
code;
|
|
15360
|
-
|
|
15361
|
-
|
|
15861
|
+
retryAfter;
|
|
15862
|
+
constructor(code, message, opts = {}) {
|
|
15863
|
+
super(printable(message));
|
|
15362
15864
|
this.name = "JevError";
|
|
15363
15865
|
this.code = code;
|
|
15866
|
+
this.retryAfter = opts.retryAfter ?? null;
|
|
15364
15867
|
}
|
|
15365
15868
|
};
|
|
15366
15869
|
MODEL_EXECUTION_402_RE = /model execution failed/i;
|
|
15870
|
+
cloudCooldown = { endpoint: "", until: Number.NEGATIVE_INFINITY, code: "http-429" };
|
|
15871
|
+
IMF_FIXDATE_RE = /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
15367
15872
|
});
|
|
15368
15873
|
|
|
15369
15874
|
// src/hooks/semantic/preconditions.ts
|
|
@@ -15401,7 +15906,7 @@ function questionChars(entry) {
|
|
|
15401
15906
|
}
|
|
15402
15907
|
return JSON.stringify(questions).length;
|
|
15403
15908
|
}
|
|
15404
|
-
function toSemanticPolicy(entry) {
|
|
15909
|
+
function toSemanticPolicy(entry, pack) {
|
|
15405
15910
|
const name = entry.precondition;
|
|
15406
15911
|
if (name !== undefined && !isPackPreconditionName(name)) {
|
|
15407
15912
|
throw new Error(`precondition ${JSON.stringify(name)} is not a name this build has`);
|
|
@@ -15416,19 +15921,34 @@ function toSemanticPolicy(entry) {
|
|
|
15416
15921
|
probes: entry.probes,
|
|
15417
15922
|
...entry.exempt ? { exempt: entry.exempt } : {},
|
|
15418
15923
|
...precondition ? { precondition } : {},
|
|
15419
|
-
guidance: entry.guidance
|
|
15924
|
+
guidance: entry.guidance,
|
|
15925
|
+
origin: { packId: pack.id, ...pack.version ? { packVersion: pack.version } : {} }
|
|
15420
15926
|
};
|
|
15421
15927
|
}
|
|
15422
15928
|
function semanticPoliciesFromPacks(packs) {
|
|
15423
15929
|
const declared = packs.filter((p) => packSemantic(p).length > 0);
|
|
15424
15930
|
if (declared.length === 0)
|
|
15425
15931
|
return { policies: SEMANTIC_POLICIES, fromPack: false, errors: [] };
|
|
15426
|
-
const
|
|
15932
|
+
const replace = replacesBuiltinChecks(declared);
|
|
15933
|
+
const ordered = [...declared.filter((p) => isFirstPartyPack(p)), ...declared.filter((p) => !isFirstPartyPack(p))];
|
|
15934
|
+
const policies = replace ? [] : [...SEMANTIC_POLICIES];
|
|
15427
15935
|
const errors = [];
|
|
15428
|
-
const seen = new Set;
|
|
15429
|
-
|
|
15430
|
-
|
|
15936
|
+
const seen = new Set(policies.map((p) => p.name));
|
|
15937
|
+
const contested = contestedSemanticNames(declared);
|
|
15938
|
+
let spent = replace ? 0 : BUILTIN_QUESTION_CHARS;
|
|
15939
|
+
for (const pack of ordered) {
|
|
15431
15940
|
for (const entry of packSemantic(pack)) {
|
|
15941
|
+
if (isReservedClaim(pack, entry.name)) {
|
|
15942
|
+
errors.push(`pack ${pack.id} declares semantic policy ${entry.name}, a name reserved for FailproofAI's own Jev checks, ` + `so that pack's version of it is never asked`);
|
|
15943
|
+
continue;
|
|
15944
|
+
}
|
|
15945
|
+
const claimants = contested.get(entry.name);
|
|
15946
|
+
if (claimants) {
|
|
15947
|
+
const message = `packs ${claimants.join(" and ")} declare different semantic policies named ${entry.name}, so it is asked ` + `for neither of them and no policy can be cleared by that name`;
|
|
15948
|
+
if (!errors.includes(message))
|
|
15949
|
+
errors.push(message);
|
|
15950
|
+
continue;
|
|
15951
|
+
}
|
|
15432
15952
|
if (seen.has(entry.name)) {
|
|
15433
15953
|
errors.push(`pack ${pack.id} declares semantic policy ${entry.name}, which another pack already declared`);
|
|
15434
15954
|
continue;
|
|
@@ -15439,7 +15959,7 @@ function semanticPoliciesFromPacks(packs) {
|
|
|
15439
15959
|
continue;
|
|
15440
15960
|
}
|
|
15441
15961
|
try {
|
|
15442
|
-
policies.push(toSemanticPolicy(entry));
|
|
15962
|
+
policies.push(toSemanticPolicy(entry, pack));
|
|
15443
15963
|
} catch (err) {
|
|
15444
15964
|
errors.push(`pack ${pack.id} semantic policy ${entry.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
15445
15965
|
continue;
|
|
@@ -15448,11 +15968,11 @@ function semanticPoliciesFromPacks(packs) {
|
|
|
15448
15968
|
spent += cost;
|
|
15449
15969
|
}
|
|
15450
15970
|
}
|
|
15451
|
-
if (policies.length === 0)
|
|
15971
|
+
if (policies.length === (replace ? 0 : SEMANTIC_POLICIES.length))
|
|
15452
15972
|
return { policies: SEMANTIC_POLICIES, fromPack: false, errors };
|
|
15453
15973
|
return { policies, fromPack: true, errors };
|
|
15454
15974
|
}
|
|
15455
|
-
function resolveSemanticPolicies() {
|
|
15975
|
+
function resolveSemanticPolicies(cli) {
|
|
15456
15976
|
let packs = [];
|
|
15457
15977
|
let manifestErrors = [];
|
|
15458
15978
|
try {
|
|
@@ -15462,7 +15982,7 @@ function resolveSemanticPolicies() {
|
|
|
15462
15982
|
} catch {
|
|
15463
15983
|
return SEMANTIC_POLICIES;
|
|
15464
15984
|
}
|
|
15465
|
-
const resolved = semanticPoliciesFromPacks(packs);
|
|
15985
|
+
const resolved = semanticPoliciesFromPacks(jevPacks(packs, cli));
|
|
15466
15986
|
for (const message of [...manifestErrors, ...resolved.errors]) {
|
|
15467
15987
|
if (warned.has(message))
|
|
15468
15988
|
continue;
|
|
@@ -15471,8 +15991,9 @@ function resolveSemanticPolicies() {
|
|
|
15471
15991
|
}
|
|
15472
15992
|
return resolved.policies;
|
|
15473
15993
|
}
|
|
15474
|
-
var GLOBAL_QUESTION_CHARS, MAX_PACK_QUESTION_CHARS, warned;
|
|
15994
|
+
var GLOBAL_QUESTION_CHARS, MAX_PACK_QUESTION_CHARS, BUILTIN_QUESTION_CHARS, warned;
|
|
15475
15995
|
var init_pack_policies = __esm(() => {
|
|
15996
|
+
init_effective_reviewers();
|
|
15476
15997
|
init_hook_logger();
|
|
15477
15998
|
init_pack_manifest();
|
|
15478
15999
|
init_compile();
|
|
@@ -15482,6 +16003,7 @@ var init_pack_policies = __esm(() => {
|
|
|
15482
16003
|
init_precondition_names();
|
|
15483
16004
|
GLOBAL_QUESTION_CHARS = Math.max(JSON.stringify({ [INJECTION_PROBE.id]: noulOf(INJECTION_PROBE), [SCOPE_PROBE.id]: noulOf(SCOPE_PROBE) }).length, JSON.stringify(Object.fromEntries([INJECTION_PROBE, ...TASK_PROBES].map((p) => [p.id, noulOf(p)]))).length);
|
|
15484
16005
|
MAX_PACK_QUESTION_CHARS = MAX_REQUEST_CHARS - MAX_STATE_CHARS - GLOBAL_QUESTION_CHARS;
|
|
16006
|
+
BUILTIN_QUESTION_CHARS = SEMANTIC_POLICIES.reduce((n, p) => n + questionChars(p), 0);
|
|
15485
16007
|
warned = new Set;
|
|
15486
16008
|
});
|
|
15487
16009
|
|
|
@@ -15506,8 +16028,8 @@ function envNumber(name, fallback) {
|
|
|
15506
16028
|
function prepareSemantic(input, opts = {}) {
|
|
15507
16029
|
const command = input.toolInput.command;
|
|
15508
16030
|
const scanned = typeof command === "string" ? scanCommand(command) : null;
|
|
15509
|
-
const facts = computeFacts(input.toolName, input.toolInput, input.cwd ?? null, input.permissionMode ?? null, scanned);
|
|
15510
|
-
const selected = selectPolicies(opts.policies ?? resolveSemanticPolicies(), facts);
|
|
16031
|
+
const facts = computeFacts(input.toolName, input.toolInput, input.cwd ?? null, input.permissionMode ?? null, scanned, input.projectRoot ?? null);
|
|
16032
|
+
const selected = selectPolicies(opts.policies ?? resolveSemanticPolicies(opts.cli), facts);
|
|
15511
16033
|
const intent = opts.intent ?? "v0";
|
|
15512
16034
|
const cleaned = intent === "v1" && opts.cleanHarnessText !== false ? cleanUserSaid(input.userSaid) : input.userSaid;
|
|
15513
16035
|
const agentLastMessage = intent === "v1" && opts.includeAgentLastMessage !== false && typeof input.agentLastMessage === "string" ? input.agentLastMessage : null;
|
|
@@ -15891,6 +16413,81 @@ var init_jev_throttle = __esm(() => {
|
|
|
15891
16413
|
PROVIDER_WINDOW_FULL = new Set(["http-429", "rate-limited"]);
|
|
15892
16414
|
});
|
|
15893
16415
|
|
|
16416
|
+
// src/hooks/semantic/session-root.ts
|
|
16417
|
+
import { mkdirSync as mkdirSync10, readdirSync as readdirSync14, readFileSync as readFileSync17, statSync as statSync19, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
16418
|
+
import { isAbsolute as isAbsolute4, resolve as resolve17 } from "node:path";
|
|
16419
|
+
function validRoot(v) {
|
|
16420
|
+
return typeof v === "string" && isAbsolute4(v) && resolve17(v) === v && v !== "/";
|
|
16421
|
+
}
|
|
16422
|
+
function storeIsOurs() {
|
|
16423
|
+
if (process.platform === "win32")
|
|
16424
|
+
return true;
|
|
16425
|
+
if (looseIntentStoreDir() !== null)
|
|
16426
|
+
return false;
|
|
16427
|
+
try {
|
|
16428
|
+
const mode = statSync19(rootsDir()).mode & 511;
|
|
16429
|
+
return (mode & DIR_WRITABLE_BY_OTHERS4) === 0;
|
|
16430
|
+
} catch {
|
|
16431
|
+
return true;
|
|
16432
|
+
}
|
|
16433
|
+
}
|
|
16434
|
+
function readPinned(sessionId) {
|
|
16435
|
+
try {
|
|
16436
|
+
const parsed = JSON.parse(readFileSync17(rootFile(sessionId), "utf8"));
|
|
16437
|
+
return validRoot(parsed?.root) ? parsed.root : null;
|
|
16438
|
+
} catch {
|
|
16439
|
+
return null;
|
|
16440
|
+
}
|
|
16441
|
+
}
|
|
16442
|
+
function pruneSessionRoots(now = Date.now()) {
|
|
16443
|
+
let removed = 0;
|
|
16444
|
+
try {
|
|
16445
|
+
const dir = rootsDir();
|
|
16446
|
+
for (const name of readdirSync14(dir)) {
|
|
16447
|
+
if (!ROOT_FILE_RE.test(name))
|
|
16448
|
+
continue;
|
|
16449
|
+
const path = resolve17(dir, name);
|
|
16450
|
+
try {
|
|
16451
|
+
const st = statSync19(path);
|
|
16452
|
+
if (st.isFile() && now - st.mtimeMs > SESSION_ROOT_MAX_AGE_MS) {
|
|
16453
|
+
unlinkSync3(path);
|
|
16454
|
+
removed++;
|
|
16455
|
+
}
|
|
16456
|
+
} catch {}
|
|
16457
|
+
}
|
|
16458
|
+
} catch {}
|
|
16459
|
+
return removed;
|
|
16460
|
+
}
|
|
16461
|
+
function sessionProjectRoot(sessionId, cwd, now = Date.now()) {
|
|
16462
|
+
if (!cwd)
|
|
16463
|
+
return null;
|
|
16464
|
+
const live = findProjectRoot(cwd);
|
|
16465
|
+
if (!sessionId || !SESSION_ID_RE3.test(sessionId))
|
|
16466
|
+
return live;
|
|
16467
|
+
try {
|
|
16468
|
+
if (!storeIsOurs())
|
|
16469
|
+
return live;
|
|
16470
|
+
const pinned = readPinned(sessionId);
|
|
16471
|
+
if (pinned !== null)
|
|
16472
|
+
return pinned;
|
|
16473
|
+
if (!validRoot(live))
|
|
16474
|
+
return live;
|
|
16475
|
+
mkdirSync10(rootsDir(), { recursive: true, mode: 448 });
|
|
16476
|
+
writeFileSync7(rootFile(sessionId), JSON.stringify({ root: live, at: now }), { mode: 384, flag: "wx" });
|
|
16477
|
+
pruneSessionRoots(now);
|
|
16478
|
+
} catch {}
|
|
16479
|
+
return live;
|
|
16480
|
+
}
|
|
16481
|
+
var SESSION_ID_RE3, ROOT_FILE_RE, SESSION_ROOT_MAX_AGE_MS, DIR_WRITABLE_BY_OTHERS4 = 18, rootsDir = () => resolve17(semanticDir(), "roots"), rootFile = (sessionId) => resolve17(rootsDir(), `${sessionId}.json`);
|
|
16482
|
+
var init_session_root = __esm(() => {
|
|
16483
|
+
init_fp_home();
|
|
16484
|
+
init_facts();
|
|
16485
|
+
init_intent();
|
|
16486
|
+
SESSION_ID_RE3 = /^[A-Za-z0-9._-]{1,128}$/;
|
|
16487
|
+
ROOT_FILE_RE = /^[A-Za-z0-9._-]{1,128}\.json$/;
|
|
16488
|
+
SESSION_ROOT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
16489
|
+
});
|
|
16490
|
+
|
|
15894
16491
|
// src/hooks/semantic/jev-review.ts
|
|
15895
16492
|
function servedFromCache2(response) {
|
|
15896
16493
|
try {
|
|
@@ -15919,13 +16516,15 @@ function resolveTimeout(cfg) {
|
|
|
15919
16516
|
return DEFAULT_JEV_TIMEOUT_MS;
|
|
15920
16517
|
return Math.min(MAX_JEV_TIMEOUT_MS2, Math.max(MIN_JEV_TIMEOUT_MS2, t));
|
|
15921
16518
|
}
|
|
16519
|
+
function jevDecider(outcome) {
|
|
16520
|
+
const wanted = outcome.verdict.decision === "deny" ? "deny" : "instruct";
|
|
16521
|
+
return outcome.verdict.outcomes.find((o) => o.verdict === wanted);
|
|
16522
|
+
}
|
|
15922
16523
|
function jevPolicyName(outcome) {
|
|
15923
|
-
const
|
|
15924
|
-
const wanted = verdict.decision === "deny" ? "deny" : "instruct";
|
|
15925
|
-
const first = verdict.outcomes.find((o) => o.verdict === wanted);
|
|
16524
|
+
const first = jevDecider(outcome);
|
|
15926
16525
|
if (first)
|
|
15927
16526
|
return `semantic/${first.policy}`;
|
|
15928
|
-
return verdict.beyondTask ? "semantic/beyond-task" : "semantic/jev";
|
|
16527
|
+
return outcome.verdict.beyondTask ? "semantic/beyond-task" : "semantic/jev";
|
|
15929
16528
|
}
|
|
15930
16529
|
function fallbackCode(reason) {
|
|
15931
16530
|
const code = reason.split(":")[0].trim();
|
|
@@ -15940,15 +16539,18 @@ function toReview(outcome, cached = false) {
|
|
|
15940
16539
|
const model = sent ? outcome.model : null;
|
|
15941
16540
|
const outcomes = outcome.verdict.outcomes;
|
|
15942
16541
|
const injection = sent ? outcome.verdict.injectionSuspected : null;
|
|
16542
|
+
const warned = (o) => o.verdict === "instruct" && o.intent !== "downgraded-task-step";
|
|
15943
16543
|
return {
|
|
15944
16544
|
kind: "answered",
|
|
15945
16545
|
decision: outcome.verdict.decision,
|
|
15946
16546
|
reason: outcome.verdict.reason,
|
|
15947
16547
|
policyName: jevPolicyName(outcome),
|
|
16548
|
+
...jevDecider(outcome)?.origin ? { origin: jevDecider(outcome)?.origin } : {},
|
|
15948
16549
|
asked: sent ? outcomes.map((o) => o.policy) : [],
|
|
15949
|
-
notDenied: sent ? outcomes.filter((o) => o.verdict !== "deny").map((o) => o.policy) : [],
|
|
16550
|
+
notDenied: sent ? outcomes.filter((o) => o.verdict !== "deny" && !warned(o)).map((o) => o.policy) : [],
|
|
15950
16551
|
injectionAsked: injection !== null,
|
|
15951
16552
|
injected: injection !== null && injection >= DEFAULT_THRESHOLDS_V1.injection,
|
|
16553
|
+
unclearableWarned: outcomes.some((o) => o.mode === "deny" && warned(o)),
|
|
15952
16554
|
truncated: outcome.truncated && sent,
|
|
15953
16555
|
requestCut: outcome.requestCut && sent,
|
|
15954
16556
|
latencyMs,
|
|
@@ -15988,11 +16590,13 @@ function startJevReview(cfg, call) {
|
|
|
15988
16590
|
} catch {
|
|
15989
16591
|
intent = { userSaid: [], agentLastMessage: null };
|
|
15990
16592
|
}
|
|
16593
|
+
const projectRoot = sessionProjectRoot(call.sessionId, call.cwd);
|
|
15991
16594
|
const input = {
|
|
15992
16595
|
eventType: call.eventType,
|
|
15993
16596
|
toolName: call.toolName,
|
|
15994
16597
|
toolInput: toolInputRecord(call.toolInput),
|
|
15995
16598
|
...call.cwd ? { cwd: call.cwd } : {},
|
|
16599
|
+
...projectRoot ? { projectRoot } : {},
|
|
15996
16600
|
...call.permissionMode ? { permissionMode: call.permissionMode } : {},
|
|
15997
16601
|
userSaid: intent.userSaid,
|
|
15998
16602
|
agentLastMessage: intent.agentLastMessage
|
|
@@ -16007,6 +16611,7 @@ function startJevReview(cfg, call) {
|
|
|
16007
16611
|
let abandoned = false;
|
|
16008
16612
|
const answered = evaluateSemantic(input, {
|
|
16009
16613
|
transport,
|
|
16614
|
+
cli: call.cli,
|
|
16010
16615
|
via: route.via,
|
|
16011
16616
|
model: route.model,
|
|
16012
16617
|
timeoutMs,
|
|
@@ -16050,6 +16655,7 @@ var init_jev_review = __esm(() => {
|
|
|
16050
16655
|
init_jev_client();
|
|
16051
16656
|
init_jev_config();
|
|
16052
16657
|
init_jev_throttle();
|
|
16658
|
+
init_session_root();
|
|
16053
16659
|
throttle = exports_jev_throttle;
|
|
16054
16660
|
intentReader = exports_intent;
|
|
16055
16661
|
ALWAYS_ON = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => normalizePolicyName(p.name)));
|
|
@@ -16256,7 +16862,7 @@ async function readJevConfig() {
|
|
|
16256
16862
|
return null;
|
|
16257
16863
|
await Promise.resolve().then(() => init_jev_config());
|
|
16258
16864
|
const config = loadJevConfig();
|
|
16259
|
-
return config ? { config, defaultMode: DEFAULT_JEV_MODE } : null;
|
|
16865
|
+
return config && config.mode !== "off" ? { config, defaultMode: DEFAULT_JEV_MODE } : null;
|
|
16260
16866
|
} catch (err) {
|
|
16261
16867
|
hookLogInfo(`Jev config could not be read (${err instanceof Error ? err.message : String(err)}); the regex engine decides alone`);
|
|
16262
16868
|
return null;
|
|
@@ -16391,7 +16997,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16391
16997
|
registerPolicy("failproofai/daemon-unreachable", "Fail-closed: the failproofaid daemon could not be reached for this daemon-configured scope.", async () => ({ decision: decision.decision, reason: decision.reason }), {});
|
|
16392
16998
|
} else {
|
|
16393
16999
|
config = readMergedHooksConfig(session.cwd);
|
|
16394
|
-
clearPolicies();
|
|
17000
|
+
clearPolicies(cli);
|
|
16395
17001
|
activePause = readActivePause(session.sessionId);
|
|
16396
17002
|
const packsInstalledHere = hasInstalledPacks();
|
|
16397
17003
|
const legacyNames = activePause || packsInstalledHere ? [] : config.enabledPolicies;
|
|
@@ -16507,9 +17113,9 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16507
17113
|
...pack ? { packId: pack.id, packVersion: pack.version } : {}
|
|
16508
17114
|
});
|
|
16509
17115
|
const authority = authorityDeclarationFor(hook, { cloudManaged, pack });
|
|
16510
|
-
const refused = resolvePolicyAuthority(authority).downgraded;
|
|
17116
|
+
const refused = resolvePolicyAuthority(authority, effectiveReviewerNames(), contestedReviewerNames()).downgraded;
|
|
16511
17117
|
if (refused)
|
|
16512
|
-
warnAuthority(
|
|
17118
|
+
warnAuthority(refusedAuthorityWarning(registeredName, refused));
|
|
16513
17119
|
registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1, pack?.policies.find((p) => p.name === hook.name)?.params, authority);
|
|
16514
17120
|
}
|
|
16515
17121
|
if (!activePause) {
|
|
@@ -16564,10 +17170,20 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16564
17170
|
...twoTier,
|
|
16565
17171
|
releaseRegistry: opts?.releaseRegistry
|
|
16566
17172
|
}) : await evaluatePolicies(canonicalEventType, parsed, session, config);
|
|
17173
|
+
const shadowVerdict = result.twoTier?.shadowVerdict;
|
|
17174
|
+
if (shadowVerdict) {
|
|
17175
|
+
observedResults.push({
|
|
17176
|
+
policyId: shadowVerdict.policyName,
|
|
17177
|
+
version: shadowVerdict.version,
|
|
17178
|
+
decision: shadowVerdict.decision,
|
|
17179
|
+
reason: shadowVerdict.reason
|
|
17180
|
+
});
|
|
17181
|
+
}
|
|
16567
17182
|
const durationMs = Math.round(performance.now() - startTime);
|
|
16568
17183
|
hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
|
|
16569
17184
|
await captureJevIntent(canonicalEventType, result.decision, parsed, session, cli, opts);
|
|
16570
17185
|
const matchedPolicies = matchedBeforeRelease ?? getPoliciesForEvent(canonicalEventType, parsed.tool_name).map((p) => p.name);
|
|
17186
|
+
const jevOrigin = result.policyName && result.twoTier?.decidedByJev ? result.twoTier.jevOrigin : undefined;
|
|
16571
17187
|
const activityEntry = {
|
|
16572
17188
|
timestamp: Date.now(),
|
|
16573
17189
|
eventType: canonicalEventType,
|
|
@@ -16585,6 +17201,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16585
17201
|
cwd: session.cwd,
|
|
16586
17202
|
permissionMode: session.permissionMode,
|
|
16587
17203
|
hookEventName: session.hookEventName,
|
|
17204
|
+
...result.policyName && result.twoTier?.decidedByJev ? { policySource: "jev", ...jevOrigin } : {},
|
|
16588
17205
|
...result.policyName && !result.twoTier?.decidedByJev ? (() => {
|
|
16589
17206
|
const attribution = policyAttribution.get(result.policyName);
|
|
16590
17207
|
return {
|
|
@@ -16659,6 +17276,7 @@ var init_handler = __esm(() => {
|
|
|
16659
17276
|
init_policy_evaluator();
|
|
16660
17277
|
init_policy_registry();
|
|
16661
17278
|
init_custom_hooks_loader();
|
|
17279
|
+
init_effective_reviewers();
|
|
16662
17280
|
init_policy_authority();
|
|
16663
17281
|
init_hook_activity_store();
|
|
16664
17282
|
init_delivery_health();
|
|
@@ -16679,7 +17297,7 @@ var init_handler = __esm(() => {
|
|
|
16679
17297
|
|
|
16680
17298
|
// src/hooks/worker-server.ts
|
|
16681
17299
|
import { createConnection, createServer } from "node:net";
|
|
16682
|
-
import { existsSync as existsSync20, unlinkSync as
|
|
17300
|
+
import { existsSync as existsSync20, unlinkSync as unlinkSync4 } from "node:fs";
|
|
16683
17301
|
function isWorkerHookRequest(msg) {
|
|
16684
17302
|
if (!msg || typeof msg !== "object")
|
|
16685
17303
|
return false;
|
|
@@ -16847,7 +17465,7 @@ function startWorkerServer(socketPath, shutdown) {
|
|
|
16847
17465
|
const listen = () => {
|
|
16848
17466
|
if (existsSync20(socketPath)) {
|
|
16849
17467
|
try {
|
|
16850
|
-
|
|
17468
|
+
unlinkSync4(socketPath);
|
|
16851
17469
|
} catch {}
|
|
16852
17470
|
}
|
|
16853
17471
|
server.listen(socketPath);
|
|
@@ -16868,13 +17486,13 @@ var init_worker_server = __esm(() => {
|
|
|
16868
17486
|
|
|
16869
17487
|
// bin/failproofai-worker.mjs
|
|
16870
17488
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
16871
|
-
import { dirname as
|
|
17489
|
+
import { dirname as dirname9, resolve as resolve18 } from "node:path";
|
|
16872
17490
|
import { fileURLToPath } from "node:url";
|
|
16873
17491
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
16874
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
17492
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve18(dirname9(realpathSync2(fileURLToPath(import.meta.url))), "..");
|
|
16875
17493
|
}
|
|
16876
17494
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
16877
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
17495
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve18(dirname9(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
|
|
16878
17496
|
}
|
|
16879
17497
|
var socketPath = process.env.FAILPROOFAI_WORKER_SOCKET;
|
|
16880
17498
|
if (!socketPath) {
|