failproofai 1.0.12-beta.0 → 1.0.12-beta.2
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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- 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 +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- 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 +22 -22
- 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 +1 -1
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +4 -4
- 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.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 +8 -8
- package/.next/standalone/.next/server/app/settings/page.js +7 -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]__0-ha5ydqq0mws._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20xe05hm5u8_1._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
- package/.next/standalone/.next/server/chunks/{_0kc-piux3hady._.js → _1y590mjzpkbos._.js} +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ts0fg5yih1c1._.js → [root-of-the-server]__00l2u6j86cb8y._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pwj2h2efe5f-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bseae9u7g98r._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0mouhaflye1ro._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _0ouu886szh6yj._.js} +10 -9
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1ve8y151ua7qx._.js → _1fw0_nzkae5ao._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_20hn4gn87nsjc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr0vt64h._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_00jxj8foxqz5t.js → node_modules_html-to-image_es_index_1fv03uigni8s5.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0janj0i6dqko7._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{0pjxnp927y7_j.js → 0ay__-d2h1xb5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2znt1784j9gd9.js → 0tbm3vjm62h-2.js} +1 -1
- package/.next/standalone/.next/static/chunks/1c_jf6g5wnslk.js +1 -0
- package/.next/standalone/.next/static/chunks/{3tn1bzk094e8e.js → 1my3-5p47t7r-.js} +1 -1
- package/.next/standalone/.next/static/chunks/20py8k1gd_i1m.js +1 -0
- package/.next/standalone/.next/static/chunks/21hckpmsc4g70.js +6 -0
- package/.next/standalone/.next/static/chunks/2hmex93ppj1ke.js +1 -0
- package/.next/standalone/.next/static/chunks/2o6d37j425uci.js +1 -0
- package/.next/standalone/.next/static/chunks/40oworn_-x12p.js +1 -0
- package/.next/standalone/.next/static/chunks/{2qz5piiir-qqm.js → 42xptz88mtq3x.js} +1 -1
- package/.next/standalone/app/actions/update-jev-config.ts +5 -3
- package/.next/standalone/app/settings/jev-panel.tsx +1 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +32 -0
- package/.next/standalone/fp-cloud-cli/README.md +40 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +91 -12
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +78 -16
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +124 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -20
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +14 -7
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +527 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
- package/.next/standalone/hermes-plugin/README.md +6 -4
- package/.next/standalone/hermes-plugin/__init__.py +27 -0
- package/.next/standalone/hermes-plugin/client.py +5 -1
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/package.json +11 -10
- package/.next/standalone/sdk/python/uv.lock +31 -8
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +17 -1
- package/dist/cli.mjs +3507 -1250
- package/dist/worker.mjs +1779 -169
- package/hermes-plugin/README.md +6 -4
- package/hermes-plugin/__init__.py +27 -0
- package/hermes-plugin/client.py +5 -1
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +11 -10
- package/src/audit/open-browser.ts +6 -7
- package/src/hooks/agent-roster.ts +180 -0
- package/src/hooks/agent-skill.ts +203 -0
- package/src/hooks/agent-targets.ts +60 -0
- package/src/hooks/cloud-connection.ts +91 -45
- package/src/hooks/cloud-enrollment-cli.ts +7 -4
- package/src/hooks/cloud-login.ts +6 -3
- package/src/hooks/cloud-managed-policies.ts +430 -20
- package/src/hooks/cloud-policy-errors.ts +481 -0
- package/src/hooks/codex-system-hooks.ts +10 -4
- package/src/hooks/config-status.ts +10 -4
- package/src/hooks/configure-wizard.ts +54 -26
- package/src/hooks/custom-hooks-loader.ts +26 -3
- package/src/hooks/daemon-client.ts +9 -4
- package/src/hooks/daemon-service.ts +2 -1
- package/src/hooks/delivery-health.ts +47 -2
- package/src/hooks/effective-reviewers.ts +36 -1
- package/src/hooks/fp-config.ts +19 -0
- package/src/hooks/fp-home.ts +6 -0
- package/src/hooks/handler.ts +255 -19
- package/src/hooks/health-probe.ts +15 -0
- package/src/hooks/hook-activity-store.ts +7 -1
- package/src/hooks/integrations.ts +36 -14
- package/src/hooks/jev-activity.ts +117 -1
- package/src/hooks/jev-cli.ts +196 -1
- package/src/hooks/jev-cloud-connection.ts +92 -0
- package/src/hooks/manager.ts +174 -34
- package/src/hooks/pack-failclosed.ts +2 -0
- package/src/hooks/pack-manifest.ts +8 -1
- package/src/hooks/policy-evaluator.ts +22 -1
- package/src/hooks/policy-registry.ts +3 -2
- package/src/hooks/policy-reviewability.ts +23 -5
- package/src/hooks/semantic/cloud-jev-health.ts +227 -0
- package/src/hooks/semantic/cloud-jev.ts +868 -0
- package/src/hooks/semantic/combine.ts +26 -1
- package/src/hooks/semantic/jev-client.ts +19 -2
- package/src/hooks/semantic/jev-config.ts +179 -30
- package/src/hooks/semantic/jev-review.ts +217 -10
- package/src/hooks/semantic/pack-policies.ts +2 -1
- package/src/hooks/semantic/types.ts +8 -0
- package/src/hooks/session-pause-cli.ts +1 -1
- package/src/hooks/tui.ts +13 -4
- package/src/hooks/uninstall-cli.ts +12 -1
- package/src/hooks/worker-server.ts +5 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
- package/.next/standalone/.next/static/chunks/0a-ccsp3xmypz.js +0 -1
- package/.next/standalone/.next/static/chunks/13jv-xvabeb62.js +0 -6
- package/.next/standalone/.next/static/chunks/2ank2u3apy8m6.js +0 -1
- package/.next/standalone/.next/static/chunks/2meqb9kygrncb.js +0 -1
- package/.next/standalone/.next/static/chunks/2ohktp_-lkv3x.js +0 -1
- package/.next/standalone/.next/static/chunks/2py0eldhwz8ay.js +0 -1
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_ssgManifest.js +0 -0
package/dist/worker.mjs
CHANGED
|
@@ -26,8 +26,9 @@ var __esm = (fn, res, err) => () => {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// src/hooks/types.ts
|
|
29
|
-
var CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_EVENT_MAP, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_TOOL_MAP, DEVIN_TOOL_MAP, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES;
|
|
29
|
+
var INTEGRATION_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_EVENT_MAP, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_TOOL_MAP, DEVIN_TOOL_MAP, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES;
|
|
30
30
|
var init_types = __esm(() => {
|
|
31
|
+
INTEGRATION_TYPES = ["claude", "codex", "copilot", "cursor", "opencode", "pi", "hermes", "openclaw", "factory", "devin", "antigravity", "goose"];
|
|
31
32
|
CODEX_EVENT_MAP = {
|
|
32
33
|
session_start: "SessionStart",
|
|
33
34
|
pre_tool_use: "PreToolUse",
|
|
@@ -381,7 +382,7 @@ function failproofaiHome(home) {
|
|
|
381
382
|
return resolve(home, ".failproofai");
|
|
382
383
|
return process.env.FAILPROOFAI_HOME || resolve(homedir(), ".failproofai");
|
|
383
384
|
}
|
|
384
|
-
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;
|
|
385
|
+
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"), agentRosterFile = (home) => atHome(home, "agents", "roster.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;
|
|
385
386
|
var init_fp_home = __esm(() => {
|
|
386
387
|
customPoliciesDir = policiesDir;
|
|
387
388
|
legacy = {
|
|
@@ -1165,9 +1166,55 @@ function instruct(reason) {
|
|
|
1165
1166
|
}
|
|
1166
1167
|
|
|
1167
1168
|
// package.json
|
|
1168
|
-
var version = "1.0.12-beta.
|
|
1169
|
+
var version = "1.0.12-beta.2";
|
|
1169
1170
|
var init_package = () => {};
|
|
1170
1171
|
|
|
1172
|
+
// src/hooks/agent-targets.ts
|
|
1173
|
+
function validAgentIdentity(value) {
|
|
1174
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1175
|
+
return false;
|
|
1176
|
+
const agent = value;
|
|
1177
|
+
return Object.keys(agent).every((key) => key === "integration" || key === "instanceId") && typeof agent.integration === "string" && integrations.has(agent.integration) && typeof agent.instanceId === "string" && INSTANCE_ID_RE.test(agent.instanceId);
|
|
1178
|
+
}
|
|
1179
|
+
function parseAgentTargets(value, schemaVersion) {
|
|
1180
|
+
if (value === undefined || value === null)
|
|
1181
|
+
return;
|
|
1182
|
+
if (schemaVersion < 3 || !Array.isArray(value) || value.length < 1 || value.length > 32) {
|
|
1183
|
+
throw new Error("invalid agentTargets in cloud-managed active manifest");
|
|
1184
|
+
}
|
|
1185
|
+
const seen = new Set;
|
|
1186
|
+
return value.map((raw) => {
|
|
1187
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1188
|
+
throw new Error("invalid agentTargets in cloud-managed active manifest");
|
|
1189
|
+
}
|
|
1190
|
+
const entry = raw;
|
|
1191
|
+
if (Object.keys(entry).some((key) => key !== "integration" && key !== "instanceId") || typeof entry.integration !== "string" || !integrations.has(entry.integration) || entry.instanceId !== undefined && (typeof entry.instanceId !== "string" || !INSTANCE_ID_RE.test(entry.instanceId))) {
|
|
1192
|
+
throw new Error("invalid agentTargets in cloud-managed active manifest");
|
|
1193
|
+
}
|
|
1194
|
+
const key = `${entry.integration}\x00${entry.instanceId ?? ""}`;
|
|
1195
|
+
if (seen.has(key))
|
|
1196
|
+
throw new Error("duplicate agentTargets in cloud-managed active manifest");
|
|
1197
|
+
seen.add(key);
|
|
1198
|
+
return {
|
|
1199
|
+
integration: entry.integration,
|
|
1200
|
+
...entry.instanceId !== undefined ? { instanceId: entry.instanceId } : {}
|
|
1201
|
+
};
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
function agentTargetsMatch(targets, agent) {
|
|
1205
|
+
if (targets === undefined)
|
|
1206
|
+
return true;
|
|
1207
|
+
if (!validAgentIdentity(agent))
|
|
1208
|
+
return false;
|
|
1209
|
+
return targets.some((target) => target.integration === agent.integration && (target.instanceId === undefined || target.instanceId === agent.instanceId));
|
|
1210
|
+
}
|
|
1211
|
+
var INSTANCE_ID_RE, integrations;
|
|
1212
|
+
var init_agent_targets = __esm(() => {
|
|
1213
|
+
init_types();
|
|
1214
|
+
INSTANCE_ID_RE = /^agt_[0-9a-f]{16,32}$/;
|
|
1215
|
+
integrations = new Set(INTEGRATION_TYPES);
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1171
1218
|
// src/hooks/policy-types.ts
|
|
1172
1219
|
function effectiveAuthority(p) {
|
|
1173
1220
|
if (p.alwaysOn === true)
|
|
@@ -1298,11 +1345,22 @@ var init_policy_authority = __esm(() => {
|
|
|
1298
1345
|
|
|
1299
1346
|
// src/hooks/cloud-managed-policies.ts
|
|
1300
1347
|
import { createHash } from "node:crypto";
|
|
1301
|
-
import { existsSync as existsSync4, readFileSync as readFileSync2, realpathSync, rmSync } from "node:fs";
|
|
1348
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, realpathSync, rmSync, statSync as statSync3 } from "node:fs";
|
|
1302
1349
|
import { isAbsolute, relative, resolve as resolve3 } from "node:path";
|
|
1303
1350
|
function cloudManagedPolicyRoot() {
|
|
1304
1351
|
return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
|
|
1305
1352
|
}
|
|
1353
|
+
function disconnectedByConfig() {
|
|
1354
|
+
try {
|
|
1355
|
+
const raw = JSON.parse(readFileSync2(configFile(), "utf8"));
|
|
1356
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1357
|
+
return false;
|
|
1358
|
+
const mode = raw.mode;
|
|
1359
|
+
return !!mode && typeof mode === "object" && !Array.isArray(mode) && mode.kind === "oss";
|
|
1360
|
+
} catch {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1306
1364
|
function parseManifest(value) {
|
|
1307
1365
|
if (!value || typeof value !== "object")
|
|
1308
1366
|
throw new Error("active manifest is not an object");
|
|
@@ -1338,20 +1396,184 @@ function resolveManagedPath(root, candidate) {
|
|
|
1338
1396
|
}
|
|
1339
1397
|
return realFile;
|
|
1340
1398
|
}
|
|
1341
|
-
function
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1399
|
+
function parseCloudJevMode(raw) {
|
|
1400
|
+
return raw === "off" || raw === "observe" || raw === "enforce" ? raw : null;
|
|
1401
|
+
}
|
|
1402
|
+
function cloudJevAsks(mode) {
|
|
1403
|
+
return mode === "observe" || mode === "enforce";
|
|
1404
|
+
}
|
|
1405
|
+
function fileVersion(path) {
|
|
1406
|
+
try {
|
|
1407
|
+
const s = statSync3(path, { bigint: true });
|
|
1408
|
+
return `${s.dev}:${s.ino}:${s.mode}:${s.size}:${s.mtimeNs}:${s.ctimeNs}`;
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
const code = err.code;
|
|
1411
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
1412
|
+
return null;
|
|
1413
|
+
throw err;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
function readActiveJson() {
|
|
1417
|
+
if (disconnectedByConfig()) {
|
|
1418
|
+
activeJsonCache = null;
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
const path = resolve3(cloudManagedPolicyRoot(), "active.json");
|
|
1422
|
+
const version = fileVersion(path);
|
|
1423
|
+
if (version === null) {
|
|
1424
|
+
activeJsonCache = null;
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
if (activeJsonCache && activeJsonCache.path === path && activeJsonCache.version === version)
|
|
1428
|
+
return activeJsonCache.value;
|
|
1429
|
+
const value = JSON.parse(readFileSync2(path, "utf8"));
|
|
1430
|
+
activeJsonCache = { path, version, value };
|
|
1431
|
+
return value;
|
|
1432
|
+
}
|
|
1433
|
+
function readActiveRaw() {
|
|
1434
|
+
const raw = readActiveJson();
|
|
1435
|
+
if (raw === undefined)
|
|
1436
|
+
return null;
|
|
1437
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1438
|
+
throw new Error("active manifest is not an object");
|
|
1439
|
+
const record = raw;
|
|
1440
|
+
if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(record.schemaVersion)) {
|
|
1441
|
+
throw new Error(`unsupported active manifest schema ${String(record.schemaVersion)}`);
|
|
1442
|
+
}
|
|
1443
|
+
return record;
|
|
1444
|
+
}
|
|
1445
|
+
function readCloudAgentScopeRequired() {
|
|
1446
|
+
try {
|
|
1447
|
+
return readActiveRaw()?.schemaVersion === 3;
|
|
1448
|
+
} catch {
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function readCloudJevMode() {
|
|
1453
|
+
try {
|
|
1454
|
+
return parseCloudJevMode(readActiveRaw()?.jevMode);
|
|
1455
|
+
} catch {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
function readCloudJevState() {
|
|
1460
|
+
let active;
|
|
1461
|
+
try {
|
|
1462
|
+
active = readActiveRaw();
|
|
1463
|
+
} catch {
|
|
1464
|
+
return noCloudJev();
|
|
1465
|
+
}
|
|
1466
|
+
if (!active)
|
|
1467
|
+
return noCloudJev();
|
|
1468
|
+
const rawMode = active.jevMode;
|
|
1469
|
+
const jevMode = parseCloudJevMode(rawMode);
|
|
1470
|
+
const errors = [];
|
|
1471
|
+
if (rawMode !== undefined && rawMode !== null && jevMode === null) {
|
|
1472
|
+
errors.push({ id: "jevMode", version: null, kind: "daemon", message: `unknown Jev mode ${JSON.stringify(rawMode)} ignored` });
|
|
1473
|
+
}
|
|
1474
|
+
const deployment = Number.isSafeInteger(active.deployment) ? active.deployment : null;
|
|
1475
|
+
return { jevMode, deployment, errors };
|
|
1476
|
+
}
|
|
1477
|
+
function readCloudJevAssignments() {
|
|
1478
|
+
try {
|
|
1479
|
+
const active = readActiveRaw();
|
|
1480
|
+
if (!active || !Array.isArray(active.jevPolicies))
|
|
1481
|
+
return null;
|
|
1482
|
+
const schema = active.schemaVersion;
|
|
1483
|
+
const out = [];
|
|
1484
|
+
for (const raw of active.jevPolicies) {
|
|
1485
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1486
|
+
return null;
|
|
1487
|
+
const entry = raw;
|
|
1488
|
+
if (typeof entry.id !== "string" || !POLICY_ID_RE.test(entry.id))
|
|
1489
|
+
return null;
|
|
1490
|
+
if (!Number.isSafeInteger(entry.version) || entry.version < 0)
|
|
1491
|
+
return null;
|
|
1492
|
+
if (entry.effect !== undefined && entry.effect !== "enforce" && entry.effect !== "observe")
|
|
1493
|
+
return null;
|
|
1494
|
+
const targets = parseAgentTargets(entry.agentTargets, schema);
|
|
1495
|
+
out.push({
|
|
1496
|
+
id: entry.id,
|
|
1497
|
+
version: entry.version,
|
|
1498
|
+
effect: entry.effect === "observe" ? "observe" : "enforce",
|
|
1499
|
+
...targets ? { agentTargets: targets } : {}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
return out;
|
|
1503
|
+
} catch {
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
function cloudJevAssignmentsFor(agent) {
|
|
1508
|
+
const all = readCloudJevAssignments();
|
|
1509
|
+
return all === null ? null : all.filter((a) => agentTargetsMatch(a.agentTargets, agent));
|
|
1510
|
+
}
|
|
1511
|
+
function cloudReviewerName(policyId, name) {
|
|
1512
|
+
return `cloud:${policyId}/${name}`;
|
|
1513
|
+
}
|
|
1514
|
+
function cloudAuthorityDeclaration(policy, mode) {
|
|
1515
|
+
if (policy.authority !== "reviewable")
|
|
1516
|
+
return policy.authority ? { authority: policy.authority } : {};
|
|
1517
|
+
if (policy.effect === "observe" || !cloudJevAsks(mode))
|
|
1518
|
+
return { authority: "hard" };
|
|
1519
|
+
return {
|
|
1520
|
+
authority: "reviewable",
|
|
1521
|
+
reviewedBy: (policy.reviewedBy ?? []).map((n) => CLOUD_REVIEWER_RE.test(n) ? n : cloudReviewerName(policy.id, n))
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
function withCloudAuthority(policy, mode) {
|
|
1525
|
+
const { authority: _authority, reviewedBy: _reviewedBy, ...rest } = policy;
|
|
1526
|
+
return { ...rest, ...cloudAuthorityDeclaration(policy, mode) };
|
|
1527
|
+
}
|
|
1528
|
+
function cloudReviewerNames(policies, mode) {
|
|
1529
|
+
const out = [];
|
|
1530
|
+
for (const policy of policies) {
|
|
1531
|
+
const declared = cloudAuthorityDeclaration(policy, mode);
|
|
1532
|
+
if (declared.authority === "reviewable")
|
|
1533
|
+
out.push(...declared.reviewedBy ?? []);
|
|
1534
|
+
}
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
function readCloudAuthorityInputs(agent = null) {
|
|
1538
|
+
try {
|
|
1539
|
+
const raw = readActiveJson();
|
|
1540
|
+
if (raw === undefined)
|
|
1541
|
+
return [];
|
|
1542
|
+
const manifest = parseManifest(raw);
|
|
1543
|
+
return manifest.policies.flatMap((policy) => {
|
|
1544
|
+
if (typeof policy.id !== "string")
|
|
1545
|
+
return [];
|
|
1546
|
+
const targets = parseAgentTargets(policy.agentTargets, manifest.schemaVersion);
|
|
1547
|
+
if (!agentTargetsMatch(targets, agent))
|
|
1548
|
+
return [];
|
|
1549
|
+
return [{
|
|
1550
|
+
id: policy.id,
|
|
1551
|
+
effect: policy.effect === "observe" ? "observe" : "enforce",
|
|
1552
|
+
...targets ? { agentTargets: targets } : {},
|
|
1553
|
+
...authorityFieldsOf(policy)
|
|
1554
|
+
}];
|
|
1555
|
+
});
|
|
1556
|
+
} catch {
|
|
1345
1557
|
return [];
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
function readActiveCloudManagedPolicies(agent = null) {
|
|
1561
|
+
return readActiveCloudManagedPoliciesFor((targets) => agentTargetsMatch(targets, agent));
|
|
1562
|
+
}
|
|
1563
|
+
function readActiveCloudManagedPoliciesFor(applies) {
|
|
1564
|
+
const root = cloudManagedPolicyRoot();
|
|
1346
1565
|
let raw;
|
|
1347
1566
|
try {
|
|
1348
|
-
raw =
|
|
1567
|
+
raw = readActiveJson();
|
|
1349
1568
|
} catch (err) {
|
|
1350
1569
|
throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
1351
1570
|
}
|
|
1571
|
+
if (raw === undefined)
|
|
1572
|
+
return [];
|
|
1352
1573
|
const manifest = parseManifest(raw);
|
|
1574
|
+
const applicable = manifest.policies.filter((policy) => applies(parseAgentTargets(policy.agentTargets, manifest.schemaVersion)));
|
|
1353
1575
|
const seen = new Set;
|
|
1354
|
-
return
|
|
1576
|
+
return applicable.map((policy) => {
|
|
1355
1577
|
if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
|
|
1356
1578
|
throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
|
|
1357
1579
|
}
|
|
@@ -1380,17 +1602,20 @@ function readActiveCloudManagedPolicies() {
|
|
|
1380
1602
|
sha256: policy.sha256,
|
|
1381
1603
|
path,
|
|
1382
1604
|
deployment: manifest.deployment,
|
|
1605
|
+
...parseAgentTargets(policy.agentTargets, manifest.schemaVersion) ? { agentTargets: parseAgentTargets(policy.agentTargets, manifest.schemaVersion) } : {},
|
|
1383
1606
|
...authorityFieldsOf(policy)
|
|
1384
1607
|
};
|
|
1385
1608
|
});
|
|
1386
1609
|
}
|
|
1387
|
-
var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
|
|
1610
|
+
var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE, CLOUD_POLICY_ERRORS_FILE = "errors.json", JEV_BUDGET_FILE = "jev-budget.json", noCloudJev = () => ({ jevMode: null, deployment: null, errors: [] }), activeJsonCache = null, CLOUD_REVIEWER_RE;
|
|
1388
1611
|
var init_cloud_managed_policies = __esm(() => {
|
|
1389
1612
|
init_fp_home();
|
|
1613
|
+
init_agent_targets();
|
|
1390
1614
|
init_policy_authority();
|
|
1391
|
-
ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
|
|
1615
|
+
ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2, 3];
|
|
1392
1616
|
SHA256_RE = /^[a-f0-9]{64}$/;
|
|
1393
1617
|
POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
1618
|
+
CLOUD_REVIEWER_RE = /^cloud:[^/]+\/./;
|
|
1394
1619
|
});
|
|
1395
1620
|
|
|
1396
1621
|
// src/hooks/semver-precedence.ts
|
|
@@ -1913,11 +2138,18 @@ function effectiveReviewerNames() {
|
|
|
1913
2138
|
return cached;
|
|
1914
2139
|
let names = NO_REVIEWERS;
|
|
1915
2140
|
cachedContested = new Map;
|
|
2141
|
+
cachedPackChecks = false;
|
|
1916
2142
|
try {
|
|
1917
2143
|
const packs = jevPacks(readInstalledPacks().packs, reviewerCli);
|
|
1918
2144
|
names = reviewerNamesFor(packs);
|
|
2145
|
+
cachedPackChecks = names.size > 0;
|
|
1919
2146
|
cachedContested = contestedSemanticNames(packs);
|
|
1920
2147
|
} catch {}
|
|
2148
|
+
try {
|
|
2149
|
+
const cloud = cloudReviewerNames(readCloudAuthorityInputs(reviewerAgent), readCloudJevMode());
|
|
2150
|
+
if (cloud.length > 0)
|
|
2151
|
+
names = new Set([...names, ...cloud]);
|
|
2152
|
+
} catch {}
|
|
1921
2153
|
cached = names;
|
|
1922
2154
|
return names;
|
|
1923
2155
|
}
|
|
@@ -1928,6 +2160,10 @@ function contestedReviewerNames() {
|
|
|
1928
2160
|
function jevChecksInstalled() {
|
|
1929
2161
|
return effectiveReviewerNames().size > 0;
|
|
1930
2162
|
}
|
|
2163
|
+
function packJevChecksInstalled() {
|
|
2164
|
+
effectiveReviewerNames();
|
|
2165
|
+
return cachedPackChecks;
|
|
2166
|
+
}
|
|
1931
2167
|
function reviewerNamesFor(packs) {
|
|
1932
2168
|
const contested = contestedSemanticNames(packs);
|
|
1933
2169
|
const declared = packs.flatMap((p) => (p.semantic ?? []).filter((s) => !isReservedClaim(p, s.name)).map((s) => s.name)).filter((name) => !contested.has(name));
|
|
@@ -1935,14 +2171,16 @@ function reviewerNamesFor(packs) {
|
|
|
1935
2171
|
return NO_REVIEWERS;
|
|
1936
2172
|
return new Set(declared);
|
|
1937
2173
|
}
|
|
1938
|
-
function forgetEffectiveReviewerNames(cli) {
|
|
2174
|
+
function forgetEffectiveReviewerNames(cli, agent = null) {
|
|
1939
2175
|
cached = null;
|
|
1940
2176
|
reviewerCli = cli;
|
|
2177
|
+
reviewerAgent = agent;
|
|
1941
2178
|
}
|
|
1942
|
-
var cached = null, cachedContested, reviewerCli;
|
|
2179
|
+
var cached = null, cachedPackChecks = false, cachedContested, reviewerCli, reviewerAgent = null;
|
|
1943
2180
|
var init_effective_reviewers = __esm(() => {
|
|
1944
2181
|
init_pack_manifest();
|
|
1945
2182
|
init_policy_authority();
|
|
2183
|
+
init_cloud_managed_policies();
|
|
1946
2184
|
cachedContested = new Map;
|
|
1947
2185
|
});
|
|
1948
2186
|
|
|
@@ -2009,11 +2247,11 @@ function getPoliciesForEvent(eventType, toolName) {
|
|
|
2009
2247
|
cache.set(key, result);
|
|
2010
2248
|
return result;
|
|
2011
2249
|
}
|
|
2012
|
-
function clearPolicies(cli) {
|
|
2250
|
+
function clearPolicies(cli, agent = null) {
|
|
2013
2251
|
const g = globalThis;
|
|
2014
2252
|
g[REGISTRY_KEY] = [];
|
|
2015
2253
|
setIndexCache(null);
|
|
2016
|
-
forgetEffectiveReviewerNames(cli);
|
|
2254
|
+
forgetEffectiveReviewerNames(cli, agent);
|
|
2017
2255
|
}
|
|
2018
2256
|
var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
|
|
2019
2257
|
var init_policy_registry = __esm(() => {
|
|
@@ -2023,7 +2261,7 @@ var init_policy_registry = __esm(() => {
|
|
|
2023
2261
|
|
|
2024
2262
|
// src/hooks/builtin-policies.ts
|
|
2025
2263
|
import { resolve as resolve5, join as join2 } from "node:path";
|
|
2026
|
-
import { statSync as
|
|
2264
|
+
import { statSync as statSync4 } from "node:fs";
|
|
2027
2265
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2028
2266
|
import { execSync, execFileSync } from "node:child_process";
|
|
2029
2267
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -2568,7 +2806,7 @@ function destroysFailproofaiState(command, depth = 0) {
|
|
|
2568
2806
|
}
|
|
2569
2807
|
function statGitHeadMtimeMs(cwd) {
|
|
2570
2808
|
try {
|
|
2571
|
-
return
|
|
2809
|
+
return statSync4(join2(cwd, ".git", "HEAD")).mtimeMs;
|
|
2572
2810
|
} catch {
|
|
2573
2811
|
return null;
|
|
2574
2812
|
}
|
|
@@ -4164,6 +4402,57 @@ function normalizeJevFallbackReason(raw) {
|
|
|
4164
4402
|
const code = JEV_FREE_TEXT_PREFIXES.get(head) ?? head;
|
|
4165
4403
|
return JEV_REASON_CODES.has(code) ? code : JEV_REASON_OTHER;
|
|
4166
4404
|
}
|
|
4405
|
+
function sanitizeCheck(raw) {
|
|
4406
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
4407
|
+
return null;
|
|
4408
|
+
const r = raw;
|
|
4409
|
+
if (typeof r.check !== "string" || !JEV_CHECK_NAME_RE.test(r.check))
|
|
4410
|
+
return null;
|
|
4411
|
+
if (typeof r.verdict !== "string" || !CHECK_VERDICT_SET.has(r.verdict))
|
|
4412
|
+
return null;
|
|
4413
|
+
if (typeof r.why !== "string" || !CHECK_WHY_SET.has(r.why))
|
|
4414
|
+
return null;
|
|
4415
|
+
if (!isP(r.p))
|
|
4416
|
+
return null;
|
|
4417
|
+
const out = {
|
|
4418
|
+
check: r.check,
|
|
4419
|
+
verdict: r.verdict,
|
|
4420
|
+
why: r.why,
|
|
4421
|
+
p: round2(r.p)
|
|
4422
|
+
};
|
|
4423
|
+
if (typeof r.policy === "string" && isJevPolicyName(r.policy) && !/\s/.test(r.policy) && /^(?:cloud|pack)\//.test(r.policy)) {
|
|
4424
|
+
out.policy = r.policy;
|
|
4425
|
+
}
|
|
4426
|
+
if (isP(r.exempt))
|
|
4427
|
+
out.exempt = round2(r.exempt);
|
|
4428
|
+
if (r.watched === true)
|
|
4429
|
+
out.watched = true;
|
|
4430
|
+
if (r.probes && typeof r.probes === "object" && !Array.isArray(r.probes)) {
|
|
4431
|
+
const probes = {};
|
|
4432
|
+
for (const [id, v] of Object.entries(r.probes)) {
|
|
4433
|
+
if (Object.keys(probes).length >= JEV_CHECK_PROBES_MAX)
|
|
4434
|
+
break;
|
|
4435
|
+
if (JEV_PROBE_ID_RE.test(id) && isP(v))
|
|
4436
|
+
probes[id] = round2(v);
|
|
4437
|
+
}
|
|
4438
|
+
if (Object.keys(probes).length > 0)
|
|
4439
|
+
out.probes = probes;
|
|
4440
|
+
}
|
|
4441
|
+
return out;
|
|
4442
|
+
}
|
|
4443
|
+
function sanitizeJevChecks(raw) {
|
|
4444
|
+
if (!Array.isArray(raw))
|
|
4445
|
+
return;
|
|
4446
|
+
const out = [];
|
|
4447
|
+
for (const item of raw) {
|
|
4448
|
+
if (out.length >= JEV_CHECKS_MAX)
|
|
4449
|
+
break;
|
|
4450
|
+
const rec = sanitizeCheck(item);
|
|
4451
|
+
if (rec)
|
|
4452
|
+
out.push(rec);
|
|
4453
|
+
}
|
|
4454
|
+
return raw.length > 0 && out.length === 0 ? undefined : out;
|
|
4455
|
+
}
|
|
4167
4456
|
function sanitizeJevActivity(entry) {
|
|
4168
4457
|
if (!JEV_ACTIVITY_KEYS.some((k) => entry[k] !== undefined))
|
|
4169
4458
|
return entry;
|
|
@@ -4202,6 +4491,11 @@ function sanitizeJevActivity(entry) {
|
|
|
4202
4491
|
out.jevModel = out.jevModel.trim();
|
|
4203
4492
|
else
|
|
4204
4493
|
drop("jevModel");
|
|
4494
|
+
const checks = out.evaluator === "jev" ? sanitizeJevChecks(out.jevChecks) : undefined;
|
|
4495
|
+
if (checks === undefined)
|
|
4496
|
+
drop("jevChecks");
|
|
4497
|
+
else
|
|
4498
|
+
out.jevChecks = checks;
|
|
4205
4499
|
return out;
|
|
4206
4500
|
}
|
|
4207
4501
|
function jevOutcome(raw) {
|
|
@@ -4212,6 +4506,8 @@ function outcomeOf(e) {
|
|
|
4212
4506
|
return "fallback";
|
|
4213
4507
|
if (e.evaluator !== "jev")
|
|
4214
4508
|
return null;
|
|
4509
|
+
if (Array.isArray(e.jevChecks) && e.jevChecks.length === 0)
|
|
4510
|
+
return "no-request";
|
|
4215
4511
|
if (e.jevCleared !== undefined || e.jevLatencyMs !== undefined || e.jevModel !== undefined)
|
|
4216
4512
|
return "answered";
|
|
4217
4513
|
if (e.jevDecision === "deny" || e.jevDecision === "instruct")
|
|
@@ -4220,7 +4516,7 @@ function outcomeOf(e) {
|
|
|
4220
4516
|
return "no-request";
|
|
4221
4517
|
return "not-consulted";
|
|
4222
4518
|
}
|
|
4223
|
-
var JEV_ACTIVITY_KEYS, EVALUATORS, DECISIONS, MODES, REASON_HEAD_RE, HTTP_CODE_RE, JEV_REASON_MAX_CHARS = 40, JEV_REASON_OTHER = "other", JEV_REASON_CODE_LIST, JEV_REASON_CODES, JEV_REASON_REQUEST_CUT = "request-cut", JEV_REASON_PROVIDER_REFUSED = "provider-refused", JEV_FREE_TEXT_PREFIXES, JEV_MODEL_RE, JEV_POLICY_NAME_MAX_CHARS = 200, NAME_FORBIDDEN_RE, REGISTERED_NAMESPACE_RE, JEV_CLEARED_MAX = 64, ANSWERED_BUT_NOT_APPLIED_REASONS;
|
|
4519
|
+
var JEV_ACTIVITY_KEYS, EVALUATORS, DECISIONS, MODES, REASON_HEAD_RE, HTTP_CODE_RE, JEV_REASON_MAX_CHARS = 40, JEV_REASON_OTHER = "other", JEV_REASON_CODE_LIST, JEV_REASON_CODES, JEV_REASON_REQUEST_CUT = "request-cut", JEV_REASON_PROVIDER_REFUSED = "provider-refused", JEV_FREE_TEXT_PREFIXES, JEV_MODEL_RE, JEV_POLICY_NAME_MAX_CHARS = 200, NAME_FORBIDDEN_RE, REGISTERED_NAMESPACE_RE, JEV_CLEARED_MAX = 64, JEV_CHECK_VERDICTS, JEV_CHECK_WHY, JEV_CHECKS_MAX = 32, JEV_CHECK_PROBES_MAX = 8, JEV_CHECK_NAME_RE, JEV_PROBE_ID_RE, CHECK_VERDICT_SET, CHECK_WHY_SET, isP = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 1, round2 = (v) => Math.round(v * 100) / 100, ANSWERED_BUT_NOT_APPLIED_REASONS;
|
|
4224
4520
|
var init_jev_activity = __esm(() => {
|
|
4225
4521
|
JEV_ACTIVITY_KEYS = [
|
|
4226
4522
|
"evaluator",
|
|
@@ -4229,7 +4525,8 @@ var init_jev_activity = __esm(() => {
|
|
|
4229
4525
|
"jevFallbackReason",
|
|
4230
4526
|
"jevLatencyMs",
|
|
4231
4527
|
"jevModel",
|
|
4232
|
-
"jevMode"
|
|
4528
|
+
"jevMode",
|
|
4529
|
+
"jevChecks"
|
|
4233
4530
|
];
|
|
4234
4531
|
EVALUATORS = new Set(["jev", "jev-fallback"]);
|
|
4235
4532
|
DECISIONS = new Set(["allow", "instruct", "deny"]);
|
|
@@ -4276,6 +4573,22 @@ var init_jev_activity = __esm(() => {
|
|
|
4276
4573
|
JEV_MODEL_RE = /^[A-Za-z0-9._:/@~+-]{1,200}$/;
|
|
4277
4574
|
NAME_FORBIDDEN_RE = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
|
|
4278
4575
|
REGISTERED_NAMESPACE_RE = /^(?:custom|pack|cloud|failproofai|\.failproofai-[A-Za-z0-9_-]{1,32})\//;
|
|
4576
|
+
JEV_CHECK_VERDICTS = ["none", "deny", "instruct", "overridden"];
|
|
4577
|
+
JEV_CHECK_WHY = [
|
|
4578
|
+
"not-detected",
|
|
4579
|
+
"exempt",
|
|
4580
|
+
"fired",
|
|
4581
|
+
"injection",
|
|
4582
|
+
"user-asked",
|
|
4583
|
+
"op-requested",
|
|
4584
|
+
"task-step",
|
|
4585
|
+
"downgraded",
|
|
4586
|
+
"scan-incomplete"
|
|
4587
|
+
];
|
|
4588
|
+
JEV_CHECK_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
4589
|
+
JEV_PROBE_ID_RE = /^[a-z][a-z0-9_]{0,31}$/;
|
|
4590
|
+
CHECK_VERDICT_SET = new Set(JEV_CHECK_VERDICTS);
|
|
4591
|
+
CHECK_WHY_SET = new Set(JEV_CHECK_WHY);
|
|
4279
4592
|
ANSWERED_BUT_NOT_APPLIED_REASONS = new Set([
|
|
4280
4593
|
JEV_REASON_REQUEST_CUT,
|
|
4281
4594
|
"truncated",
|
|
@@ -4336,7 +4649,8 @@ function combineTwoTier(verdicts, review, mode) {
|
|
|
4336
4649
|
...cleared.length > 0 && RANK[enforced.final.decision] < RANK[legacy.decision] ? { jevCleared: cleared } : {},
|
|
4337
4650
|
...review.latencyMs !== null ? { jevLatencyMs: review.latencyMs } : {},
|
|
4338
4651
|
...review.model ? { jevModel: review.model } : {},
|
|
4339
|
-
jevMode: mode
|
|
4652
|
+
jevMode: mode,
|
|
4653
|
+
...review.checks ? { jevChecks: [...review.checks] } : {}
|
|
4340
4654
|
};
|
|
4341
4655
|
if (mode === "observe") {
|
|
4342
4656
|
const observeVerdict = review.decision === "deny" || review.decision === "instruct" ? {
|
|
@@ -4386,7 +4700,7 @@ import {
|
|
|
4386
4700
|
mkdirSync as mkdirSync3,
|
|
4387
4701
|
renameSync as renameSync2,
|
|
4388
4702
|
rmSync as rmSync2,
|
|
4389
|
-
statSync as
|
|
4703
|
+
statSync as statSync5,
|
|
4390
4704
|
writeFileSync as writeFileSync2
|
|
4391
4705
|
} from "node:fs";
|
|
4392
4706
|
import { dirname as dirname2 } from "node:path";
|
|
@@ -4401,12 +4715,12 @@ function writeJsonAtomically(filePath, value, opts = {}) {
|
|
|
4401
4715
|
try {
|
|
4402
4716
|
writeFileSync2(tmp, JSON.stringify(value, null, 2), { mode });
|
|
4403
4717
|
try {
|
|
4404
|
-
if ((
|
|
4718
|
+
if ((statSync5(tmp).mode & 63) !== 0)
|
|
4405
4719
|
chmodSync(tmp, mode);
|
|
4406
4720
|
} catch {}
|
|
4407
4721
|
renameSync2(tmp, filePath);
|
|
4408
4722
|
try {
|
|
4409
|
-
if ((
|
|
4723
|
+
if ((statSync5(filePath).mode & 63) !== 0)
|
|
4410
4724
|
chmodSync(filePath, mode);
|
|
4411
4725
|
} catch {}
|
|
4412
4726
|
} catch (err) {
|
|
@@ -4429,7 +4743,7 @@ import {
|
|
|
4429
4743
|
openSync,
|
|
4430
4744
|
readFileSync as readFileSync4,
|
|
4431
4745
|
readSync,
|
|
4432
|
-
statSync as
|
|
4746
|
+
statSync as statSync6,
|
|
4433
4747
|
writeFileSync as writeFileSync3
|
|
4434
4748
|
} from "node:fs";
|
|
4435
4749
|
import { dirname as dirname3 } from "node:path";
|
|
@@ -4586,6 +4900,13 @@ function projectConfig(parsed) {
|
|
|
4586
4900
|
function isAgentTraced(id, config = readConfig()) {
|
|
4587
4901
|
return !config.agents || config.agents.selected.includes(id);
|
|
4588
4902
|
}
|
|
4903
|
+
function readCredentials() {
|
|
4904
|
+
try {
|
|
4905
|
+
return projectCredentials(JSON.parse(readFileSync4(credentialsFile(), "utf8")));
|
|
4906
|
+
} catch {
|
|
4907
|
+
return {};
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4589
4910
|
function projectCredentials(parsed) {
|
|
4590
4911
|
{
|
|
4591
4912
|
const cloud = parsed.cloud;
|
|
@@ -4661,7 +4982,7 @@ function readJevCloudCredential() {
|
|
|
4661
4982
|
if (meaningful) {
|
|
4662
4983
|
let dirMode = null;
|
|
4663
4984
|
try {
|
|
4664
|
-
const m =
|
|
4985
|
+
const m = statSync6(dirname3(path)).mode & 511;
|
|
4665
4986
|
if ((m & DIR_WRITABLE_BY_OTHERS) !== 0)
|
|
4666
4987
|
dirMode = m;
|
|
4667
4988
|
} catch {}
|
|
@@ -4996,6 +5317,12 @@ async function evaluatePolicies(eventType, payload, session, config, twoTier) {
|
|
|
4996
5317
|
twoTier.releaseRegistry?.();
|
|
4997
5318
|
review = await twoTier.review;
|
|
4998
5319
|
}
|
|
5320
|
+
if (review.kind === "answered" && review.asked.length === 0 && review.decision === "allow" && !review.observedAsked?.length) {
|
|
5321
|
+
return formatVerdict(eventType, session, toolName, regexOnly(collected.verdicts));
|
|
5322
|
+
}
|
|
5323
|
+
if (review.kind === "not-consulted" && twoTier.cloud) {
|
|
5324
|
+
return formatVerdict(eventType, session, toolName, regexOnly(collected.verdicts));
|
|
5325
|
+
}
|
|
4999
5326
|
const combined = combineTwoTier(collected.verdicts, review, twoTier.mode);
|
|
5000
5327
|
if (combined.cleared.length > 0) {
|
|
5001
5328
|
hookLogInfo(`jev ${twoTier.mode === "observe" ? "would clear" : "cleared"}: ${combined.cleared.join(", ")}`);
|
|
@@ -5008,8 +5335,9 @@ async function evaluatePolicies(eventType, payload, session, config, twoTier) {
|
|
|
5008
5335
|
twoTier: {
|
|
5009
5336
|
activity: combined.activity,
|
|
5010
5337
|
decidedByJev: combined.decidedByJev,
|
|
5011
|
-
...combined.decidedByJev && review.kind === "answered" && review.origin ? { jevOrigin: review.origin } : {},
|
|
5012
|
-
...combined.observeVerdict ? { observeVerdict: combined.observeVerdict } : {}
|
|
5338
|
+
...(combined.decidedByJev || combined.observeVerdict) && review.kind === "answered" && review.origin ? { jevOrigin: review.origin } : {},
|
|
5339
|
+
...combined.observeVerdict ? { observeVerdict: combined.observeVerdict } : {},
|
|
5340
|
+
...review.kind === "answered" && review.observedCloud?.length ? { observedCloud: review.observedCloud } : {}
|
|
5013
5341
|
}
|
|
5014
5342
|
};
|
|
5015
5343
|
}
|
|
@@ -6069,7 +6397,7 @@ async function loadSingleFile(absPath, opts) {
|
|
|
6069
6397
|
return null;
|
|
6070
6398
|
} catch (err) {
|
|
6071
6399
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6072
|
-
const errorType = /Cannot find module|MODULE_NOT_FOUND|
|
|
6400
|
+
const errorType = /Cannot find module|MODULE_NOT_FOUND|Cannot find package/i.test(msg) ? "module_not_found" : /ENOENT/i.test(msg) ? "file_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : /timed out loading module/i.test(msg) ? "load_timeout" : "runtime_error";
|
|
6073
6401
|
trackHookEvent(getInstanceId(), "custom_hooks_load_error", {
|
|
6074
6402
|
error_type: errorType,
|
|
6075
6403
|
is_convention: !!opts?.conventionScope,
|
|
@@ -6129,8 +6457,11 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
6129
6457
|
const conventionEnabled = opts?.customPoliciesEnabled !== false;
|
|
6130
6458
|
const loadedPaths = new Set;
|
|
6131
6459
|
const cloudManagedByPath = new Map;
|
|
6460
|
+
const cloudIdsByPath = new Map;
|
|
6461
|
+
const cloudFailures = new Map;
|
|
6132
6462
|
for (const policy of opts?.cloudManagedPolicies ?? []) {
|
|
6133
6463
|
const key = resolve7(policy.path);
|
|
6464
|
+
cloudIdsByPath.set(key, [...cloudIdsByPath.get(key) ?? [], policy.id]);
|
|
6134
6465
|
const existing = cloudManagedByPath.get(key);
|
|
6135
6466
|
if (!existing) {
|
|
6136
6467
|
cloudManagedByPath.set(key, policy);
|
|
@@ -6211,6 +6542,10 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
6211
6542
|
for (const id of packIdsByPath.get(absPath) ?? [pack.id])
|
|
6212
6543
|
packFailures.set(id, failure);
|
|
6213
6544
|
}
|
|
6545
|
+
if (failure && cloudManaged) {
|
|
6546
|
+
for (const id of cloudIdsByPath.get(absPath) ?? [cloudManaged.id])
|
|
6547
|
+
cloudFailures.set(id, failure);
|
|
6548
|
+
}
|
|
6214
6549
|
for (const hook of getCustomHooks().slice(hooksBefore)) {
|
|
6215
6550
|
const tagged = hook;
|
|
6216
6551
|
if (cloudManaged) {
|
|
@@ -6227,6 +6562,11 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
6227
6562
|
}
|
|
6228
6563
|
} else {
|
|
6229
6564
|
hookLogWarn(`custom policy path not found: ${absPath}`);
|
|
6565
|
+
if (cloudManaged) {
|
|
6566
|
+
const missing = { type: "path_missing", reason: `path missing: ${absPath}` };
|
|
6567
|
+
for (const id of cloudIdsByPath.get(absPath) ?? [cloudManaged.id])
|
|
6568
|
+
cloudFailures.set(id, missing);
|
|
6569
|
+
}
|
|
6230
6570
|
if (pack) {
|
|
6231
6571
|
const missing = { type: "path_missing", reason: `path missing: ${absPath}` };
|
|
6232
6572
|
for (const id of packIdsByPath.get(absPath) ?? [pack.id])
|
|
@@ -6309,7 +6649,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
6309
6649
|
if (winner && ids.length > 1)
|
|
6310
6650
|
packAliases.set(winner.id, ids);
|
|
6311
6651
|
}
|
|
6312
|
-
return { hooks: allHooks, conventionSources, packFailures, packAliases };
|
|
6652
|
+
return { hooks: allHooks, conventionSources, packFailures, packAliases, cloudFailures };
|
|
6313
6653
|
}
|
|
6314
6654
|
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE, warnedSemanticOutsidePack;
|
|
6315
6655
|
var init_custom_hooks_loader = __esm(() => {
|
|
@@ -6337,7 +6677,7 @@ import {
|
|
|
6337
6677
|
readdirSync as readdirSync2,
|
|
6338
6678
|
mkdirSync as mkdirSync5,
|
|
6339
6679
|
existsSync as existsSync9,
|
|
6340
|
-
statSync as
|
|
6680
|
+
statSync as statSync7,
|
|
6341
6681
|
unlinkSync
|
|
6342
6682
|
} from "node:fs";
|
|
6343
6683
|
import { join as join4 } from "node:path";
|
|
@@ -6358,7 +6698,7 @@ function acquireLock() {
|
|
|
6358
6698
|
if (e.code !== "EEXIST")
|
|
6359
6699
|
return;
|
|
6360
6700
|
try {
|
|
6361
|
-
const s =
|
|
6701
|
+
const s = statSync7(lockPath);
|
|
6362
6702
|
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
6363
6703
|
writeFileSync4(lockPath, String(process.pid), "utf-8");
|
|
6364
6704
|
return;
|
|
@@ -7156,7 +7496,7 @@ var init_cursor_projects = __esm(() => {
|
|
|
7156
7496
|
});
|
|
7157
7497
|
|
|
7158
7498
|
// lib/sqlite-reader.ts
|
|
7159
|
-
import { existsSync as existsSync10, readFileSync as readFileSync6, statSync as
|
|
7499
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, statSync as statSync8 } from "node:fs";
|
|
7160
7500
|
import initSqlJs from "sql.js/dist/sql-asm.js";
|
|
7161
7501
|
async function tryNodeSqlite(dbPath) {
|
|
7162
7502
|
try {
|
|
@@ -7180,8 +7520,8 @@ function loadSqlJs() {
|
|
|
7180
7520
|
sqlPromise = initSqlJs();
|
|
7181
7521
|
return sqlPromise;
|
|
7182
7522
|
}
|
|
7183
|
-
function
|
|
7184
|
-
const stat =
|
|
7523
|
+
function fileVersion2(path) {
|
|
7524
|
+
const stat = statSync8(path);
|
|
7185
7525
|
return { size: stat.size, mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs };
|
|
7186
7526
|
}
|
|
7187
7527
|
function sameVersion(a, b) {
|
|
@@ -7268,9 +7608,9 @@ function applyCommittedWal(db, wal) {
|
|
|
7268
7608
|
function readPortableSnapshot(dbPath) {
|
|
7269
7609
|
const walPath = `${dbPath}-wal`;
|
|
7270
7610
|
for (let attempt = 0;attempt < 3; attempt += 1) {
|
|
7271
|
-
const before =
|
|
7611
|
+
const before = fileVersion2(dbPath);
|
|
7272
7612
|
const main = readFileSync6(dbPath);
|
|
7273
|
-
const afterMain =
|
|
7613
|
+
const afterMain = fileVersion2(dbPath);
|
|
7274
7614
|
if (!sameVersion(before, afterMain))
|
|
7275
7615
|
continue;
|
|
7276
7616
|
let wal = null;
|
|
@@ -7280,7 +7620,7 @@ function readPortableSnapshot(dbPath) {
|
|
|
7280
7620
|
if (error.code !== "ENOENT")
|
|
7281
7621
|
throw error;
|
|
7282
7622
|
}
|
|
7283
|
-
const afterWal =
|
|
7623
|
+
const afterWal = fileVersion2(dbPath);
|
|
7284
7624
|
if (!sameVersion(before, afterWal))
|
|
7285
7625
|
continue;
|
|
7286
7626
|
return wal ? applyCommittedWal(main, wal) : main;
|
|
@@ -8218,7 +8558,7 @@ var init_openclaw_db = __esm(() => {
|
|
|
8218
8558
|
|
|
8219
8559
|
// lib/openclaw-sessions.ts
|
|
8220
8560
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
8221
|
-
import { readdirSync as readdirSync4, statSync as
|
|
8561
|
+
import { readdirSync as readdirSync4, statSync as statSync9 } from "node:fs";
|
|
8222
8562
|
import { homedir as homedir11 } from "node:os";
|
|
8223
8563
|
import { join as join14 } from "node:path";
|
|
8224
8564
|
function openclawHome() {
|
|
@@ -8371,7 +8711,7 @@ function listOpenClawTranscripts() {
|
|
|
8371
8711
|
continue;
|
|
8372
8712
|
const transcriptPath = join14(sessionsDir, file);
|
|
8373
8713
|
try {
|
|
8374
|
-
const st =
|
|
8714
|
+
const st = statSync9(transcriptPath);
|
|
8375
8715
|
out.push({
|
|
8376
8716
|
agentId,
|
|
8377
8717
|
sessionId,
|
|
@@ -8614,7 +8954,7 @@ var init_openclaw_projects = __esm(() => {
|
|
|
8614
8954
|
|
|
8615
8955
|
// lib/factory-sessions.ts
|
|
8616
8956
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
8617
|
-
import { readdirSync as readdirSync5, statSync as
|
|
8957
|
+
import { readdirSync as readdirSync5, statSync as statSync10 } from "node:fs";
|
|
8618
8958
|
import { homedir as homedir12 } from "node:os";
|
|
8619
8959
|
import { join as join16 } from "node:path";
|
|
8620
8960
|
function factoryHome() {
|
|
@@ -8782,7 +9122,7 @@ function listFactoryTranscripts() {
|
|
|
8782
9122
|
continue;
|
|
8783
9123
|
const transcriptPath = join16(projectPath, file);
|
|
8784
9124
|
try {
|
|
8785
|
-
const st =
|
|
9125
|
+
const st = statSync10(transcriptPath);
|
|
8786
9126
|
out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
8787
9127
|
} catch {}
|
|
8788
9128
|
}
|
|
@@ -9201,7 +9541,7 @@ var init_devin_projects = __esm(() => {
|
|
|
9201
9541
|
|
|
9202
9542
|
// lib/antigravity-sessions.ts
|
|
9203
9543
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
9204
|
-
import { readdirSync as readdirSync6, statSync as
|
|
9544
|
+
import { readdirSync as readdirSync6, statSync as statSync11 } from "node:fs";
|
|
9205
9545
|
import { homedir as homedir14 } from "node:os";
|
|
9206
9546
|
import { join as join18 } from "node:path";
|
|
9207
9547
|
function antigravityHome() {
|
|
@@ -9336,7 +9676,7 @@ function listAntigravityTranscripts() {
|
|
|
9336
9676
|
continue;
|
|
9337
9677
|
const transcriptPath = join18(root, conversationId, rel);
|
|
9338
9678
|
try {
|
|
9339
|
-
const st =
|
|
9679
|
+
const st = statSync11(transcriptPath);
|
|
9340
9680
|
if (!st.isFile())
|
|
9341
9681
|
continue;
|
|
9342
9682
|
out.push({ conversationId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
@@ -9349,7 +9689,7 @@ function findAntigravityTranscript(conversationId) {
|
|
|
9349
9689
|
return null;
|
|
9350
9690
|
const transcriptPath = join18(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
|
|
9351
9691
|
try {
|
|
9352
|
-
if (
|
|
9692
|
+
if (statSync11(transcriptPath).isFile())
|
|
9353
9693
|
return transcriptPath;
|
|
9354
9694
|
} catch {}
|
|
9355
9695
|
return null;
|
|
@@ -10258,7 +10598,7 @@ var init_log_entries = __esm(() => {
|
|
|
10258
10598
|
});
|
|
10259
10599
|
|
|
10260
10600
|
// lib/codex-sessions.ts
|
|
10261
|
-
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, statSync as
|
|
10601
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, statSync as statSync12 } from "node:fs";
|
|
10262
10602
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
10263
10603
|
import { dirname as dirname6, join as join24 } from "node:path";
|
|
10264
10604
|
import { homedir as homedir16 } from "node:os";
|
|
@@ -10586,7 +10926,7 @@ var init_resolve_permission_mode = __esm(() => {
|
|
|
10586
10926
|
});
|
|
10587
10927
|
|
|
10588
10928
|
// lib/copilot-sessions.ts
|
|
10589
|
-
import { readFileSync as readFileSync10, readdirSync as readdirSync8, existsSync as existsSync14, statSync as
|
|
10929
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync8, existsSync as existsSync14, statSync as statSync13 } from "node:fs";
|
|
10590
10930
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
10591
10931
|
import { join as join25, resolve as resolve10, sep as sep2 } from "node:path";
|
|
10592
10932
|
import { homedir as homedir17 } from "node:os";
|
|
@@ -10803,7 +11143,7 @@ var init_copilot_sessions = __esm(() => {
|
|
|
10803
11143
|
});
|
|
10804
11144
|
|
|
10805
11145
|
// lib/cursor-sessions.ts
|
|
10806
|
-
import { readFileSync as readFileSync11, readdirSync as readdirSync9, existsSync as existsSync15, statSync as
|
|
11146
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync9, existsSync as existsSync15, statSync as statSync14 } from "node:fs";
|
|
10807
11147
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
10808
11148
|
import { basename as basename3, join as join26, resolve as resolve11, sep as sep3 } from "node:path";
|
|
10809
11149
|
import { homedir as homedir18 } from "node:os";
|
|
@@ -11059,7 +11399,7 @@ var init_cursor_sessions = __esm(() => {
|
|
|
11059
11399
|
});
|
|
11060
11400
|
|
|
11061
11401
|
// lib/pi-sessions.ts
|
|
11062
|
-
import { readFileSync as readFileSync12, readdirSync as readdirSync10, existsSync as existsSync16, statSync as
|
|
11402
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync10, existsSync as existsSync16, statSync as statSync15 } from "node:fs";
|
|
11063
11403
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
11064
11404
|
import { join as join27, resolve as resolve12, sep as sep4 } from "node:path";
|
|
11065
11405
|
import { homedir as homedir19 } from "node:os";
|
|
@@ -11320,6 +11660,238 @@ var init_resolve_transcript_path = __esm(() => {
|
|
|
11320
11660
|
init_factory_sessions();
|
|
11321
11661
|
init_antigravity_sessions();
|
|
11322
11662
|
});
|
|
11663
|
+
// src/hooks/cloud-policy-errors.ts
|
|
11664
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11665
|
+
import { chmodSync as chmodSync3, existsSync as existsSync17, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
11666
|
+
import { homedir as homedir20 } from "node:os";
|
|
11667
|
+
import { resolve as resolve13 } from "node:path";
|
|
11668
|
+
function isPackDropEntry(e) {
|
|
11669
|
+
return e.id.startsWith("pack:") && (e.message.startsWith(JEV_BUDGET) || e.message.startsWith(JEV_NAME_CLASH));
|
|
11670
|
+
}
|
|
11671
|
+
function redactLocalPaths(message, home = safeHomedir()) {
|
|
11672
|
+
let text = message;
|
|
11673
|
+
const base = home?.replace(/\/+$/, "") ?? "";
|
|
11674
|
+
if (base.length > 1) {
|
|
11675
|
+
let out = "";
|
|
11676
|
+
let rest = text;
|
|
11677
|
+
for (let at = rest.indexOf(base);at >= 0; at = rest.indexOf(base)) {
|
|
11678
|
+
const next = rest.charAt(at + base.length);
|
|
11679
|
+
const whole = next === "" || next === "/" || next === ":" || PATH_END.test(next);
|
|
11680
|
+
out += rest.slice(0, at) + (whole ? "~" : base);
|
|
11681
|
+
rest = rest.slice(at + base.length);
|
|
11682
|
+
}
|
|
11683
|
+
text = out + rest;
|
|
11684
|
+
}
|
|
11685
|
+
return text.replace(/(^|[\s"'`(\[<{=,]|:(?!\/\/(?!\/)))(\/[^\s"'`)\]>},;]*)/g, (_m, lead, path) => {
|
|
11686
|
+
const last = path.replace(/\/+$/, "").split("/").pop() ?? "";
|
|
11687
|
+
return lead + (last === "" ? "/" : last);
|
|
11688
|
+
});
|
|
11689
|
+
}
|
|
11690
|
+
function safeHomedir() {
|
|
11691
|
+
try {
|
|
11692
|
+
return homedir20();
|
|
11693
|
+
} catch {
|
|
11694
|
+
return null;
|
|
11695
|
+
}
|
|
11696
|
+
}
|
|
11697
|
+
function cloudPolicyErrorsPath() {
|
|
11698
|
+
return resolve13(cloudManagedPolicyRoot(), CLOUD_POLICY_ERRORS_FILE);
|
|
11699
|
+
}
|
|
11700
|
+
function readCloudPolicyErrors() {
|
|
11701
|
+
try {
|
|
11702
|
+
const raw = JSON.parse(readFileSync13(cloudPolicyErrorsPath(), "utf8"));
|
|
11703
|
+
if (!Array.isArray(raw.errors))
|
|
11704
|
+
return null;
|
|
11705
|
+
return raw.errors.filter((e) => !!e && typeof e === "object" && typeof e.id === "string" && typeof e.message === "string" && KINDS.has(e.kind) && (e.version === null || Number.isSafeInteger(e.version)));
|
|
11706
|
+
} catch {
|
|
11707
|
+
return null;
|
|
11708
|
+
}
|
|
11709
|
+
}
|
|
11710
|
+
function serialize(errors) {
|
|
11711
|
+
const list = errors.map((e) => ({ id: e.id, version: e.version, kind: e.kind, message: redactLocalPaths(e.message) }));
|
|
11712
|
+
return `${JSON.stringify({ errors: list }, null, 2)}
|
|
11713
|
+
`;
|
|
11714
|
+
}
|
|
11715
|
+
function writeCloudPolicyErrors(errors) {
|
|
11716
|
+
try {
|
|
11717
|
+
const root = cloudManagedPolicyRoot();
|
|
11718
|
+
if (!existsSync17(resolve13(root, "active.json")))
|
|
11719
|
+
return "skipped";
|
|
11720
|
+
const path = cloudPolicyErrorsPath();
|
|
11721
|
+
const text = serialize(errors);
|
|
11722
|
+
if (lastWritten && lastWritten.path === path && lastWritten.text === text && existsSync17(path))
|
|
11723
|
+
return "unchanged";
|
|
11724
|
+
let current = null;
|
|
11725
|
+
try {
|
|
11726
|
+
current = readFileSync13(path, "utf8");
|
|
11727
|
+
} catch {
|
|
11728
|
+
current = null;
|
|
11729
|
+
}
|
|
11730
|
+
if (current === text) {
|
|
11731
|
+
lastWritten = { path, text };
|
|
11732
|
+
return "unchanged";
|
|
11733
|
+
}
|
|
11734
|
+
if (current === null && errors.length === 0)
|
|
11735
|
+
return "skipped";
|
|
11736
|
+
writeAtomic(path, text);
|
|
11737
|
+
lastWritten = { path, text };
|
|
11738
|
+
return "written";
|
|
11739
|
+
} catch {
|
|
11740
|
+
return "failed";
|
|
11741
|
+
}
|
|
11742
|
+
}
|
|
11743
|
+
function cloudLoadCheckedPath() {
|
|
11744
|
+
return resolve13(cloudManagedPolicyRoot(), CLOUD_LOAD_CHECKED_FILE);
|
|
11745
|
+
}
|
|
11746
|
+
function readCloudLoadChecked() {
|
|
11747
|
+
try {
|
|
11748
|
+
const raw = JSON.parse(readFileSync13(cloudLoadCheckedPath(), "utf8"));
|
|
11749
|
+
if (!Number.isSafeInteger(raw.deployment) || !Array.isArray(raw.checked))
|
|
11750
|
+
return null;
|
|
11751
|
+
return { deployment: raw.deployment, checked: raw.checked.filter((c) => typeof c === "string") };
|
|
11752
|
+
} catch {
|
|
11753
|
+
return null;
|
|
11754
|
+
}
|
|
11755
|
+
}
|
|
11756
|
+
function recordCloudLoadChecked(deployment, policies) {
|
|
11757
|
+
try {
|
|
11758
|
+
if (!existsSync17(resolve13(cloudManagedPolicyRoot(), "active.json")))
|
|
11759
|
+
return;
|
|
11760
|
+
const path = cloudLoadCheckedPath();
|
|
11761
|
+
const prior = readCloudLoadChecked();
|
|
11762
|
+
const base = prior && prior.deployment === deployment ? prior.checked : [];
|
|
11763
|
+
const next = [...new Set([...base, ...policies.map((p) => `${p.id}@${p.version}`)])].sort();
|
|
11764
|
+
if (prior && prior.deployment === deployment && next.length === base.length)
|
|
11765
|
+
return;
|
|
11766
|
+
const text = `${JSON.stringify({ deployment, checked: next }, null, 2)}
|
|
11767
|
+
`;
|
|
11768
|
+
writeAtomic(path, text);
|
|
11769
|
+
} catch {}
|
|
11770
|
+
}
|
|
11771
|
+
function kindOf(policy) {
|
|
11772
|
+
return policy.authority === "reviewable" && (policy.reviewedBy?.length ?? 0) > 0 ? "both" : "regex";
|
|
11773
|
+
}
|
|
11774
|
+
function collectCloudPolicyErrors(input) {
|
|
11775
|
+
const out = [];
|
|
11776
|
+
if (input.manifestError) {
|
|
11777
|
+
out.push({ id: "active.json", version: null, kind: "daemon", message: `Cloud policies could not be loaded: ${input.manifestError}` });
|
|
11778
|
+
}
|
|
11779
|
+
if (input.agentScopeUnresolved) {
|
|
11780
|
+
out.push({
|
|
11781
|
+
id: "agentScope",
|
|
11782
|
+
version: null,
|
|
11783
|
+
kind: "daemon",
|
|
11784
|
+
message: "agent_scope_unresolved: this hook's agent profile is unknown; targeted Cloud policies did not match"
|
|
11785
|
+
});
|
|
11786
|
+
}
|
|
11787
|
+
for (const policy of input.jsPolicies) {
|
|
11788
|
+
const failure = input.jsFailures?.get(policy.id);
|
|
11789
|
+
if (failure) {
|
|
11790
|
+
out.push({ id: policy.id, version: policy.version, kind: kindOf(policy), message: `policy did not load (${failure.type}): ${failure.reason}` });
|
|
11791
|
+
}
|
|
11792
|
+
}
|
|
11793
|
+
out.push(...input.jev.errors);
|
|
11794
|
+
out.push(...input.budgetDrops ?? []);
|
|
11795
|
+
if (input.jevProblem) {
|
|
11796
|
+
out.push({ id: "jevMode", version: null, kind: "daemon", message: input.jevProblem });
|
|
11797
|
+
}
|
|
11798
|
+
out.push(...input.health ?? []);
|
|
11799
|
+
const seen = new Set;
|
|
11800
|
+
return out.map((e) => ({ ...e, message: redactLocalPaths(e.message) })).filter((e) => {
|
|
11801
|
+
const key = `${e.id}\x00${e.version}\x00${e.kind}\x00${e.message}`;
|
|
11802
|
+
if (seen.has(key))
|
|
11803
|
+
return false;
|
|
11804
|
+
seen.add(key);
|
|
11805
|
+
return true;
|
|
11806
|
+
});
|
|
11807
|
+
}
|
|
11808
|
+
function jevBudgetPath() {
|
|
11809
|
+
return resolve13(cloudManagedPolicyRoot(), JEV_BUDGET_FILE);
|
|
11810
|
+
}
|
|
11811
|
+
function readJevBudgetRecord() {
|
|
11812
|
+
try {
|
|
11813
|
+
const raw = JSON.parse(readFileSync13(jevBudgetPath(), "utf8"));
|
|
11814
|
+
if (typeof raw.machineId !== "string" || !(raw.deployment === null || Number.isSafeInteger(raw.deployment)) || raw.jevMode !== "observe" && raw.jevMode !== "enforce" || !Array.isArray(raw.dropped)) {
|
|
11815
|
+
return null;
|
|
11816
|
+
}
|
|
11817
|
+
const dropped = raw.dropped.filter((d) => !!d && typeof d.packId === "string" && typeof d.name === "string").map((d) => ({ packId: d.packId, name: d.name, ...d.clash === true ? { clash: true } : {} }));
|
|
11818
|
+
return { machineId: raw.machineId, deployment: raw.deployment, jevMode: raw.jevMode, dropped };
|
|
11819
|
+
} catch {
|
|
11820
|
+
return null;
|
|
11821
|
+
}
|
|
11822
|
+
}
|
|
11823
|
+
function budgetEntries(dropped) {
|
|
11824
|
+
return [...dropped].sort((a, b) => dropKey(a) < dropKey(b) ? -1 : dropKey(a) > dropKey(b) ? 1 : 0).map((d) => ({
|
|
11825
|
+
id: `pack:${d.packId}`,
|
|
11826
|
+
version: null,
|
|
11827
|
+
kind: "daemon",
|
|
11828
|
+
message: d.clash ? `${JEV_NAME_CLASH}: ${d.name} (the FailproofAI Cloud check is used)` : `${JEV_BUDGET}: dropped ${d.name}`
|
|
11829
|
+
}));
|
|
11830
|
+
}
|
|
11831
|
+
function installedPackIds() {
|
|
11832
|
+
try {
|
|
11833
|
+
const path = resolve13(packsRoot(), "installed.json");
|
|
11834
|
+
if (!existsSync17(path))
|
|
11835
|
+
return new Set;
|
|
11836
|
+
const raw = JSON.parse(readFileSync13(path, "utf8"));
|
|
11837
|
+
if (!Array.isArray(raw.packs))
|
|
11838
|
+
return null;
|
|
11839
|
+
return new Set(raw.packs.map((p) => p && typeof p === "object" ? p.id : null).filter((id) => typeof id === "string"));
|
|
11840
|
+
} catch {
|
|
11841
|
+
return null;
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
function readJevBudgetDrops(key, installed = null) {
|
|
11845
|
+
const record = readJevBudgetRecord();
|
|
11846
|
+
if (!record || !sameKey(record, key))
|
|
11847
|
+
return [];
|
|
11848
|
+
return budgetEntries(installed ? record.dropped.filter((d) => installed.has(d.packId)) : record.dropped);
|
|
11849
|
+
}
|
|
11850
|
+
function recordJevBudgetDrops(key, drops) {
|
|
11851
|
+
try {
|
|
11852
|
+
if (drops.length === 0 || !existsSync17(resolve13(cloudManagedPolicyRoot(), "active.json")))
|
|
11853
|
+
return;
|
|
11854
|
+
const current = readJevBudgetRecord();
|
|
11855
|
+
const base = current && sameKey(current, key) ? current.dropped : [];
|
|
11856
|
+
const seen = new Set(base.map(dropKey));
|
|
11857
|
+
const added = [];
|
|
11858
|
+
for (const d of drops) {
|
|
11859
|
+
const id = dropKey(d);
|
|
11860
|
+
if (seen.has(id))
|
|
11861
|
+
continue;
|
|
11862
|
+
seen.add(id);
|
|
11863
|
+
added.push({ packId: d.packId, name: d.name, ...d.clash === true ? { clash: true } : {} });
|
|
11864
|
+
}
|
|
11865
|
+
if (current && sameKey(current, key) && added.length === 0)
|
|
11866
|
+
return;
|
|
11867
|
+
const record = { ...key, dropped: [...base, ...added] };
|
|
11868
|
+
writeAtomic(jevBudgetPath(), `${JSON.stringify(record, null, 2)}
|
|
11869
|
+
`);
|
|
11870
|
+
const report = (readCloudPolicyErrors() ?? []).filter((e) => !isPackDropEntry(e));
|
|
11871
|
+
writeCloudPolicyErrors([...report, ...budgetEntries(record.dropped)]);
|
|
11872
|
+
} catch {}
|
|
11873
|
+
}
|
|
11874
|
+
function writeAtomic(path, text) {
|
|
11875
|
+
const tmp = `${path}.tmp-${process.pid}-${randomUUID2()}`;
|
|
11876
|
+
try {
|
|
11877
|
+
writeFileSync6(tmp, text, { mode: 384, flag: "wx" });
|
|
11878
|
+
chmodSync3(tmp, 384);
|
|
11879
|
+
renameSync4(tmp, path);
|
|
11880
|
+
} catch (err) {
|
|
11881
|
+
try {
|
|
11882
|
+
rmSync3(tmp, { force: true });
|
|
11883
|
+
} catch {}
|
|
11884
|
+
throw err;
|
|
11885
|
+
}
|
|
11886
|
+
}
|
|
11887
|
+
var JEV_BUDGET = "jev_budget", JEV_NAME_CLASH = "jev_name_clash", PATH_END, KINDS, lastWritten = null, CLOUD_LOAD_CHECKED_FILE = "load-checked.json", sameKey = (a, b) => a.machineId === b.machineId && a.deployment === b.deployment && a.jevMode === b.jevMode, dropKey = (d) => `${d.packId}\x00${d.name}\x00${d.clash === true}`;
|
|
11888
|
+
var init_cloud_policy_errors = __esm(() => {
|
|
11889
|
+
init_cloud_managed_policies();
|
|
11890
|
+
init_pack_manifest();
|
|
11891
|
+
PATH_END = /[\s"'`)\]>},;]/;
|
|
11892
|
+
KINDS = new Set(["regex", "jev", "both", "daemon"]);
|
|
11893
|
+
});
|
|
11894
|
+
|
|
11323
11895
|
// src/hooks/pack-failclosed.ts
|
|
11324
11896
|
function declaredList(value) {
|
|
11325
11897
|
if (value === undefined || value === null)
|
|
@@ -11448,6 +12020,7 @@ var PERMANENT_LOAD_FAILURES;
|
|
|
11448
12020
|
var init_pack_failclosed = __esm(() => {
|
|
11449
12021
|
PERMANENT_LOAD_FAILURES = new Set([
|
|
11450
12022
|
"module_not_found",
|
|
12023
|
+
"file_not_found",
|
|
11451
12024
|
"syntax_error",
|
|
11452
12025
|
"runtime_error",
|
|
11453
12026
|
"path_missing"
|
|
@@ -11455,13 +12028,13 @@ var init_pack_failclosed = __esm(() => {
|
|
|
11455
12028
|
});
|
|
11456
12029
|
|
|
11457
12030
|
// src/hooks/session-pause.ts
|
|
11458
|
-
import { existsSync as
|
|
12031
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync7, readdirSync as readdirSync11, readFileSync as readFileSync14, rmSync as rmSync4 } from "node:fs";
|
|
11459
12032
|
import { createHash as createHash5 } from "node:crypto";
|
|
11460
|
-
import { join as join28, resolve as
|
|
12033
|
+
import { join as join28, resolve as resolve14 } from "node:path";
|
|
11461
12034
|
function pauseStateDir() {
|
|
11462
12035
|
const override = process.env.FAILPROOFAI_STATE_DIR;
|
|
11463
12036
|
if (override)
|
|
11464
|
-
return
|
|
12037
|
+
return resolve14(override, "sessions");
|
|
11465
12038
|
return sessionPauseDir();
|
|
11466
12039
|
}
|
|
11467
12040
|
function pauseFilePath(sessionId) {
|
|
@@ -11495,9 +12068,9 @@ function readActivePause(sessionId, now = Date.now()) {
|
|
|
11495
12068
|
return null;
|
|
11496
12069
|
try {
|
|
11497
12070
|
const path = pauseFilePath(sessionId);
|
|
11498
|
-
if (!
|
|
12071
|
+
if (!existsSync18(path))
|
|
11499
12072
|
return null;
|
|
11500
|
-
const stored = parseStored(JSON.parse(
|
|
12073
|
+
const stored = parseStored(JSON.parse(readFileSync14(path, "utf8")));
|
|
11501
12074
|
if (!stored)
|
|
11502
12075
|
return null;
|
|
11503
12076
|
if (stored.expiresAt <= now)
|
|
@@ -11517,6 +12090,12 @@ var init_session_pause = __esm(() => {
|
|
|
11517
12090
|
PAUSE_CEILING_MS = 8 * 60 * 60000;
|
|
11518
12091
|
});
|
|
11519
12092
|
|
|
12093
|
+
// src/hooks/health-probe.ts
|
|
12094
|
+
function isHealthProbe(eventType, parsed) {
|
|
12095
|
+
return eventType === "SessionStart" && parsed.source === HEALTH_PROBE_SOURCE && parsed.session_id === undefined;
|
|
12096
|
+
}
|
|
12097
|
+
var HEALTH_PROBE_SOURCE = "failproofai-health-probe";
|
|
12098
|
+
|
|
11520
12099
|
// src/hooks/daemon-download.ts
|
|
11521
12100
|
var init_daemon_download = __esm(() => {
|
|
11522
12101
|
init_package();
|
|
@@ -11536,15 +12115,15 @@ var init_daemon_service = __esm(() => {
|
|
|
11536
12115
|
// src/hooks/fp-reset.ts
|
|
11537
12116
|
import {
|
|
11538
12117
|
copyFileSync,
|
|
11539
|
-
existsSync as
|
|
12118
|
+
existsSync as existsSync19,
|
|
11540
12119
|
mkdirSync as mkdirSync8,
|
|
11541
|
-
readFileSync as
|
|
12120
|
+
readFileSync as readFileSync15,
|
|
11542
12121
|
readdirSync as readdirSync12,
|
|
11543
|
-
renameSync as
|
|
11544
|
-
rmSync as
|
|
12122
|
+
renameSync as renameSync5,
|
|
12123
|
+
rmSync as rmSync5,
|
|
11545
12124
|
rmdirSync,
|
|
11546
|
-
statSync as
|
|
11547
|
-
writeFileSync as
|
|
12125
|
+
statSync as statSync16,
|
|
12126
|
+
writeFileSync as writeFileSync7
|
|
11548
12127
|
} from "node:fs";
|
|
11549
12128
|
function layoutWarningForHook() {
|
|
11550
12129
|
const state = detectLayout();
|
|
@@ -11553,7 +12132,7 @@ function layoutWarningForHook() {
|
|
|
11553
12132
|
if (state.kind === "future") {
|
|
11554
12133
|
return `[failproofai] this directory was written by a newer version (layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` + `Upgrade failproofai.`;
|
|
11555
12134
|
}
|
|
11556
|
-
if (
|
|
12135
|
+
if (existsSync19(globalPolicyConfigFile()))
|
|
11557
12136
|
return null;
|
|
11558
12137
|
return `[failproofai] setup predates this version — global policies are NOT being enforced. ` + `Run \`failproofai config\` to re-create it.`;
|
|
11559
12138
|
}
|
|
@@ -11565,7 +12144,7 @@ var init_fp_reset = __esm(() => {
|
|
|
11565
12144
|
});
|
|
11566
12145
|
|
|
11567
12146
|
// src/hooks/untraced-note.ts
|
|
11568
|
-
import { existsSync as
|
|
12147
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16 } from "node:fs";
|
|
11569
12148
|
import { join as join29 } from "node:path";
|
|
11570
12149
|
function untracedNotePath(home) {
|
|
11571
12150
|
return join29(stateDir(home), "untraced-notes.json");
|
|
@@ -11577,8 +12156,8 @@ function noteUntracedAgent(cli, sessionId, config) {
|
|
|
11577
12156
|
const key = `${cli}:${sessionId ?? "no-session"}`;
|
|
11578
12157
|
const path = untracedNotePath();
|
|
11579
12158
|
let seen = [];
|
|
11580
|
-
if (
|
|
11581
|
-
const parsed = JSON.parse(
|
|
12159
|
+
if (existsSync20(path)) {
|
|
12160
|
+
const parsed = JSON.parse(readFileSync16(path, "utf8"));
|
|
11582
12161
|
if (Array.isArray(parsed))
|
|
11583
12162
|
seen = parsed.filter((k) => typeof k === "string");
|
|
11584
12163
|
}
|
|
@@ -11599,14 +12178,150 @@ var init_untraced_note = __esm(() => {
|
|
|
11599
12178
|
init_hook_logger();
|
|
11600
12179
|
});
|
|
11601
12180
|
|
|
11602
|
-
// src/hooks/
|
|
11603
|
-
import {
|
|
11604
|
-
import {
|
|
11605
|
-
|
|
11606
|
-
|
|
12181
|
+
// src/hooks/agent-roster.ts
|
|
12182
|
+
import { lstatSync, readFileSync as readFileSync17, statSync as statSync17 } from "node:fs";
|
|
12183
|
+
import { homedir as homedir21 } from "node:os";
|
|
12184
|
+
import { dirname as dirname7, resolve as resolve15 } from "node:path";
|
|
12185
|
+
function installedHookAt(path, cli) {
|
|
12186
|
+
try {
|
|
12187
|
+
const stat = statSync17(path);
|
|
12188
|
+
if (!stat.isFile() || stat.size > 131072)
|
|
12189
|
+
return false;
|
|
12190
|
+
const text = readFileSync17(path, "utf8");
|
|
12191
|
+
if (text.includes("failproofai"))
|
|
12192
|
+
return true;
|
|
12193
|
+
if (cli !== "pi")
|
|
12194
|
+
return false;
|
|
12195
|
+
const config = JSON.parse(text);
|
|
12196
|
+
if (!config || typeof config !== "object" || Array.isArray(config))
|
|
12197
|
+
return false;
|
|
12198
|
+
const packages = config.packages;
|
|
12199
|
+
return Array.isArray(packages) && packages.some((entry) => typeof entry === "string" && /(?:^|\/)pi-extension\/?$/.test(entry));
|
|
12200
|
+
} catch {
|
|
12201
|
+
return false;
|
|
12202
|
+
}
|
|
11607
12203
|
}
|
|
11608
|
-
function
|
|
11609
|
-
|
|
12204
|
+
function runtimeAgentSettingsPath(cli, cwd, userHome = homedir21(), hookScope) {
|
|
12205
|
+
if (cli === "hermes") {
|
|
12206
|
+
if (hookScope && hookScope !== "user")
|
|
12207
|
+
return null;
|
|
12208
|
+
const home = process.env.HERMES_HOME;
|
|
12209
|
+
return resolve15(home?.trim() ? home : resolve15(userHome, ".hermes"), "config.yaml");
|
|
12210
|
+
}
|
|
12211
|
+
if (cli === "openclaw") {
|
|
12212
|
+
if (hookScope && hookScope !== "user")
|
|
12213
|
+
return null;
|
|
12214
|
+
const config = process.env.OPENCLAW_CONFIG_PATH;
|
|
12215
|
+
if (config?.trim())
|
|
12216
|
+
return resolve15(config);
|
|
12217
|
+
const state = process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_HOME;
|
|
12218
|
+
if (state?.trim())
|
|
12219
|
+
return resolve15(state, "openclaw.json");
|
|
12220
|
+
const profile = process.env.OPENCLAW_PROFILE?.trim();
|
|
12221
|
+
if (profile && profile !== "default" && /^[A-Za-z0-9._-]{1,80}$/.test(profile) && !profile.startsWith(".")) {
|
|
12222
|
+
return resolve15(userHome, `.openclaw-${profile}`, "openclaw.json");
|
|
12223
|
+
}
|
|
12224
|
+
return resolve15(userHome, ".openclaw", "openclaw.json");
|
|
12225
|
+
}
|
|
12226
|
+
const relative = USER_SETTINGS[cli];
|
|
12227
|
+
if (!relative)
|
|
12228
|
+
return null;
|
|
12229
|
+
const override = cli === "codex" ? process.env.CODEX_HOME : cli === "claude" ? process.env.CLAUDE_CONFIG_DIR : undefined;
|
|
12230
|
+
const user = resolve15(override?.trim() ? override : userHome, override?.trim() ? cli === "codex" ? "hooks.json" : "settings.json" : relative);
|
|
12231
|
+
if (hookScope === "user")
|
|
12232
|
+
return user;
|
|
12233
|
+
const scopeFiles = hookScope === "local" ? cli === "claude" ? [".claude/settings.local.json"] : [] : hookScope === "project" ? (PROJECT_SETTINGS[cli] ?? []).filter((name) => name !== ".claude/settings.local.json") : PROJECT_SETTINGS[cli] ?? [];
|
|
12234
|
+
const matches = new Set;
|
|
12235
|
+
if (hookScope === undefined && installedHookAt(user, cli))
|
|
12236
|
+
matches.add(user);
|
|
12237
|
+
let dir = resolve15(cwd ?? process.cwd());
|
|
12238
|
+
const userRoot = resolve15(userHome);
|
|
12239
|
+
for (let depth = 0;depth < 16 && dir !== userRoot; depth++) {
|
|
12240
|
+
for (const candidate of scopeFiles) {
|
|
12241
|
+
const path = resolve15(dir, candidate);
|
|
12242
|
+
if (installedHookAt(path, cli))
|
|
12243
|
+
matches.add(path);
|
|
12244
|
+
}
|
|
12245
|
+
const parent = dirname7(dir);
|
|
12246
|
+
if (parent === dir)
|
|
12247
|
+
break;
|
|
12248
|
+
dir = parent;
|
|
12249
|
+
}
|
|
12250
|
+
if (matches.size > 1)
|
|
12251
|
+
return null;
|
|
12252
|
+
if (hookScope === "project" || hookScope === "local")
|
|
12253
|
+
return matches.values().next().value ?? null;
|
|
12254
|
+
return matches.values().next().value ?? user;
|
|
12255
|
+
}
|
|
12256
|
+
function readRuntimeAgentIdentity(cli, settingsPath) {
|
|
12257
|
+
if (!settingsPath)
|
|
12258
|
+
return null;
|
|
12259
|
+
try {
|
|
12260
|
+
const path = agentRosterFile();
|
|
12261
|
+
const stat = lstatSync(path);
|
|
12262
|
+
if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > MAX_ROSTER_BYTES)
|
|
12263
|
+
return null;
|
|
12264
|
+
const roster = JSON.parse(readFileSync17(path, "utf8"));
|
|
12265
|
+
if (!roster || typeof roster !== "object" || Array.isArray(roster))
|
|
12266
|
+
return null;
|
|
12267
|
+
const data = roster;
|
|
12268
|
+
if (data.schemaVersion !== 1 || !Array.isArray(data.agents) || data.agents.length > MAX_ROSTER_AGENTS)
|
|
12269
|
+
return null;
|
|
12270
|
+
const absolute = resolve15(settingsPath);
|
|
12271
|
+
const agent = data.agents.find((entry) => {
|
|
12272
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
12273
|
+
return false;
|
|
12274
|
+
const record = entry;
|
|
12275
|
+
return record.integration === cli && record.settingsPath === absolute && validAgentIdentity({ integration: record.integration, instanceId: record.instanceId });
|
|
12276
|
+
});
|
|
12277
|
+
const instanceId = agent?.instanceId;
|
|
12278
|
+
return validAgentIdentity({ integration: cli, instanceId }) ? { integration: cli, instanceId } : null;
|
|
12279
|
+
} catch {
|
|
12280
|
+
return null;
|
|
12281
|
+
}
|
|
12282
|
+
}
|
|
12283
|
+
var MAX_ROSTER_AGENTS = 256, MAX_ROSTER_BYTES = 4000000, USER_SETTINGS, PROJECT_SETTINGS;
|
|
12284
|
+
var init_agent_roster = __esm(() => {
|
|
12285
|
+
init_fp_home();
|
|
12286
|
+
init_agent_targets();
|
|
12287
|
+
USER_SETTINGS = {
|
|
12288
|
+
claude: ".claude/settings.json",
|
|
12289
|
+
codex: ".codex/hooks.json",
|
|
12290
|
+
copilot: ".copilot/hooks/failproofai.json",
|
|
12291
|
+
cursor: ".cursor/hooks.json",
|
|
12292
|
+
opencode: ".config/opencode/opencode.json",
|
|
12293
|
+
pi: ".pi/agent/settings.json",
|
|
12294
|
+
factory: ".factory/hooks.json",
|
|
12295
|
+
devin: ".config/devin/config.json",
|
|
12296
|
+
antigravity: ".gemini/config/hooks.json",
|
|
12297
|
+
goose: ".agents/plugins/failproofai/hooks/hooks.json"
|
|
12298
|
+
};
|
|
12299
|
+
PROJECT_SETTINGS = {
|
|
12300
|
+
claude: [".claude/settings.local.json", ".claude/settings.json"],
|
|
12301
|
+
codex: [".codex/hooks.json"],
|
|
12302
|
+
copilot: [".github/hooks/failproofai.json"],
|
|
12303
|
+
cursor: [".cursor/hooks.json"],
|
|
12304
|
+
opencode: [".opencode/opencode.json"],
|
|
12305
|
+
pi: [".pi/settings.json"],
|
|
12306
|
+
factory: [".factory/hooks.json"],
|
|
12307
|
+
devin: [".devin/config.json"],
|
|
12308
|
+
antigravity: [".agents/hooks.json"],
|
|
12309
|
+
goose: [".agents/plugins/failproofai/hooks/hooks.json"]
|
|
12310
|
+
};
|
|
12311
|
+
});
|
|
12312
|
+
|
|
12313
|
+
// src/hooks/semantic/jev-config.ts
|
|
12314
|
+
import { closeSync as closeSync2, constants as fsConstants2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, statSync as statSync18 } from "node:fs";
|
|
12315
|
+
import { dirname as dirname8 } from "node:path";
|
|
12316
|
+
function parseJevMode(raw) {
|
|
12317
|
+
return raw === "off" || raw === "observe" || raw === "enforce" ? raw : null;
|
|
12318
|
+
}
|
|
12319
|
+
function jevCloudBaseUrl(cloudBase) {
|
|
12320
|
+
const url = new URL(cloudBase);
|
|
12321
|
+
return `${url.origin}${url.pathname.replace(/\/+$/, "")}${JEV_CLOUD_BASE_PATH}`;
|
|
12322
|
+
}
|
|
12323
|
+
function jevConfigPath() {
|
|
12324
|
+
return jevConfigFile();
|
|
11610
12325
|
}
|
|
11611
12326
|
function jevModelVersion(model) {
|
|
11612
12327
|
const m = VERSIONED_MODEL_RE.exec(model);
|
|
@@ -11660,13 +12375,6 @@ function validateBaseUrl(raw) {
|
|
|
11660
12375
|
url.pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
11661
12376
|
return { ok: true, value: url.toString() };
|
|
11662
12377
|
}
|
|
11663
|
-
function isPlainHttp(url) {
|
|
11664
|
-
try {
|
|
11665
|
-
return new URL(url).protocol === "http:";
|
|
11666
|
-
} catch {
|
|
11667
|
-
return false;
|
|
11668
|
-
}
|
|
11669
|
-
}
|
|
11670
12378
|
function originOf(raw) {
|
|
11671
12379
|
try {
|
|
11672
12380
|
const url = new URL(raw);
|
|
@@ -11746,7 +12454,12 @@ function validateJevConfig(raw, envKey, cloud) {
|
|
|
11746
12454
|
missingKey: true
|
|
11747
12455
|
};
|
|
11748
12456
|
}
|
|
11749
|
-
const cfg = {
|
|
12457
|
+
const cfg = {
|
|
12458
|
+
provider: kind,
|
|
12459
|
+
apiKey,
|
|
12460
|
+
mode: DEFAULT_JEV_MODE,
|
|
12461
|
+
timeoutMs: kind === JEV_CLOUD_PROVIDER ? CLOUD_JEV_TIMEOUT_MS : JEV_CONFIG_DEFAULT_TIMEOUT_MS
|
|
12462
|
+
};
|
|
11750
12463
|
if (o.baseUrl !== undefined) {
|
|
11751
12464
|
const r = validateBaseUrl(o.baseUrl);
|
|
11752
12465
|
if (!r.ok)
|
|
@@ -11796,12 +12509,6 @@ function validateJevConfig(raw, envKey, cloud) {
|
|
|
11796
12509
|
return { ok: false, problem: 'mode must be "off", "observe" or "enforce"' };
|
|
11797
12510
|
cfg.mode = mode;
|
|
11798
12511
|
}
|
|
11799
|
-
if (cfg.baseUrl !== undefined && isPlainHttp(cfg.baseUrl) && cfg.mode === "enforce") {
|
|
11800
|
-
return {
|
|
11801
|
-
ok: false,
|
|
11802
|
-
problem: "plain http (to localhost) is accepted only with mode observe: 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 observe" : "Use https, or mode observe")
|
|
11803
|
-
};
|
|
11804
|
-
}
|
|
11805
12512
|
return { ok: true, value: cfg };
|
|
11806
12513
|
}
|
|
11807
12514
|
function validateLoadedJevConfig(cfg) {
|
|
@@ -11832,7 +12539,7 @@ function looseConfigDirMode(path) {
|
|
|
11832
12539
|
if (!modesAreMeaningful())
|
|
11833
12540
|
return null;
|
|
11834
12541
|
try {
|
|
11835
|
-
const mode =
|
|
12542
|
+
const mode = statSync18(dirname8(path)).mode & 511;
|
|
11836
12543
|
return (mode & DIR_WRITABLE_BY_OTHERS2) !== 0 ? mode : null;
|
|
11837
12544
|
} catch {
|
|
11838
12545
|
return null;
|
|
@@ -11893,8 +12600,8 @@ function inspectJevConfig() {
|
|
|
11893
12600
|
path,
|
|
11894
12601
|
mode,
|
|
11895
12602
|
reason: "too-open",
|
|
11896
|
-
problem: `its directory ${
|
|
11897
|
-
fix: `chmod 700 ${
|
|
12603
|
+
problem: `its directory ${dirname8(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 ${dirname8(path)})`,
|
|
12604
|
+
fix: `chmod 700 ${dirname8(path)}`
|
|
11898
12605
|
};
|
|
11899
12606
|
}
|
|
11900
12607
|
if (st.size > MAX_CONFIG_BYTES)
|
|
@@ -11996,7 +12703,80 @@ function loadJevConfig() {
|
|
|
11996
12703
|
return null;
|
|
11997
12704
|
}
|
|
11998
12705
|
}
|
|
11999
|
-
|
|
12706
|
+
function loadJevConfigForCloudMode(mode) {
|
|
12707
|
+
if (mode === "off")
|
|
12708
|
+
return { config: null, machineId: null, problem: null };
|
|
12709
|
+
const key = `${mode}\x00${jevInputsVersion()}`;
|
|
12710
|
+
if (cloudModeMemo?.key === key)
|
|
12711
|
+
return cloudModeMemo.value;
|
|
12712
|
+
const value = computeCloudModeConfig(mode);
|
|
12713
|
+
cloudModeMemo = { key, value };
|
|
12714
|
+
return value;
|
|
12715
|
+
}
|
|
12716
|
+
function jevInputsVersion() {
|
|
12717
|
+
const credentials = credentialsFile();
|
|
12718
|
+
const config = configFile();
|
|
12719
|
+
return [
|
|
12720
|
+
process.platform,
|
|
12721
|
+
credentials,
|
|
12722
|
+
statVersion(credentials),
|
|
12723
|
+
statVersion(dirname8(credentials)),
|
|
12724
|
+
config,
|
|
12725
|
+
statVersion(config)
|
|
12726
|
+
].join("\x00");
|
|
12727
|
+
}
|
|
12728
|
+
function statVersion(path) {
|
|
12729
|
+
try {
|
|
12730
|
+
const st = statSync18(path, { bigint: true });
|
|
12731
|
+
return `${st.dev}:${st.ino}:${st.mode}:${st.size}:${st.mtimeNs}:${st.ctimeNs}`;
|
|
12732
|
+
} catch (err) {
|
|
12733
|
+
return `-${err.code ?? "error"}`;
|
|
12734
|
+
}
|
|
12735
|
+
}
|
|
12736
|
+
function computeCloudModeConfig(mode) {
|
|
12737
|
+
try {
|
|
12738
|
+
if (readConfig().collector.sessions !== true) {
|
|
12739
|
+
return { config: null, machineId: null, problem: TRANSCRIPTS_DISABLED_PROBLEM };
|
|
12740
|
+
}
|
|
12741
|
+
const credential = readJevCloudCredential();
|
|
12742
|
+
if (credential.status === "absent")
|
|
12743
|
+
return { config: null, machineId: null, problem: JEV_UNCONFIGURED_PROBLEM };
|
|
12744
|
+
if (credential.status === "refused") {
|
|
12745
|
+
return {
|
|
12746
|
+
config: null,
|
|
12747
|
+
machineId: null,
|
|
12748
|
+
problem: `${JEV_UNCONFIGURED_PROBLEM}: the FailproofAI Cloud Jev credential is refused — ${credential.problem}`
|
|
12749
|
+
};
|
|
12750
|
+
}
|
|
12751
|
+
const machineId = readCredentials().cloud?.machineId || null;
|
|
12752
|
+
if (!machineId) {
|
|
12753
|
+
return {
|
|
12754
|
+
config: null,
|
|
12755
|
+
machineId: null,
|
|
12756
|
+
problem: `${JEV_UNCONFIGURED_PROBLEM}: this machine has no FailproofAI Cloud machine id (no policy connection in credentials.json)`
|
|
12757
|
+
};
|
|
12758
|
+
}
|
|
12759
|
+
const raw = {
|
|
12760
|
+
provider: JEV_CLOUD_PROVIDER,
|
|
12761
|
+
baseUrl: jevCloudBaseUrl(cloudBaseFor(credential.credential)),
|
|
12762
|
+
mode,
|
|
12763
|
+
timeoutMs: CLOUD_JEV_TIMEOUT_MS
|
|
12764
|
+
};
|
|
12765
|
+
const r = validateJevConfig(raw, null, credential.credential);
|
|
12766
|
+
return r.ok ? { config: r.value, machineId, problem: null } : { config: null, machineId, problem: `${JEV_UNCONFIGURED_PROBLEM}: the FailproofAI Cloud Jev credential — ${r.problem}` };
|
|
12767
|
+
} catch (err) {
|
|
12768
|
+
return { config: null, machineId: null, problem: `${JEV_UNCONFIGURED_PROBLEM}: ${err instanceof Error ? err.message : String(err)}` };
|
|
12769
|
+
}
|
|
12770
|
+
}
|
|
12771
|
+
function cloudBaseFor(credential) {
|
|
12772
|
+
try {
|
|
12773
|
+
const cloud = readCredentials().cloud;
|
|
12774
|
+
if (cloud?.url && new URL(cloud.url).origin === new URL(credential.url).origin)
|
|
12775
|
+
return cloud.url;
|
|
12776
|
+
} catch {}
|
|
12777
|
+
return credential.url;
|
|
12778
|
+
}
|
|
12779
|
+
var DEFAULT_JEV_MODE = "enforce", JEV_PROVIDER_KINDS, JEV_CLOUD_PROVIDER = "failproofai", JEV_CLOUD_BASE_PATH = "/enforcement/v1/jev", JEV_API_KEY_ENV = "FAILPROOFAI_JEV_API_KEY", JEV_CONFIG_DEFAULT_TIMEOUT_MS = 3000, CLOUD_JEV_TIMEOUT_MS = 5000, 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)", JEV_UNCONFIGURED_PROBLEM = "jev_unconfigured", TRANSCRIPTS_DISABLED_PROBLEM = "transcripts_disabled", cloudModeMemo = null;
|
|
12000
12780
|
var init_jev_config = __esm(() => {
|
|
12001
12781
|
init_fp_config();
|
|
12002
12782
|
init_fp_home();
|
|
@@ -12015,10 +12795,127 @@ var init_jev_config = __esm(() => {
|
|
|
12015
12795
|
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_)/;
|
|
12016
12796
|
});
|
|
12017
12797
|
|
|
12798
|
+
// src/hooks/semantic/cloud-jev-health.ts
|
|
12799
|
+
function forScope(scope) {
|
|
12800
|
+
if (state.scope !== scope)
|
|
12801
|
+
state = fresh(scope);
|
|
12802
|
+
return state;
|
|
12803
|
+
}
|
|
12804
|
+
function cloudJevGate(scope, at = clock()) {
|
|
12805
|
+
const s = forScope(scope);
|
|
12806
|
+
if (s.openUntil === null)
|
|
12807
|
+
return { ask: true, probe: false };
|
|
12808
|
+
if (at < s.openUntil || s.probing)
|
|
12809
|
+
return { ask: false };
|
|
12810
|
+
s.probing = true;
|
|
12811
|
+
return { ask: true, probe: true };
|
|
12812
|
+
}
|
|
12813
|
+
function classifyCloudJevReview(review, opts = { reachedCloud: true }) {
|
|
12814
|
+
if (review.kind === "answered")
|
|
12815
|
+
return opts.reachedCloud ? "answered" : "neutral";
|
|
12816
|
+
if (review.kind !== "fallback")
|
|
12817
|
+
return "neutral";
|
|
12818
|
+
const code = review.reason;
|
|
12819
|
+
if (code === "http-429" || code === "rate-limited")
|
|
12820
|
+
return "rate-limited";
|
|
12821
|
+
if (FAILURES.has(code) || /^http-5\d\d$/.test(code))
|
|
12822
|
+
return "failed";
|
|
12823
|
+
return "neutral";
|
|
12824
|
+
}
|
|
12825
|
+
function recordCloudJevResult(scope, result, code, probe, at = clock()) {
|
|
12826
|
+
if (state.scope !== scope)
|
|
12827
|
+
return;
|
|
12828
|
+
const s = state;
|
|
12829
|
+
if (probe)
|
|
12830
|
+
s.probing = false;
|
|
12831
|
+
switch (result) {
|
|
12832
|
+
case "answered":
|
|
12833
|
+
s.failures = 0;
|
|
12834
|
+
s.lastFailure = null;
|
|
12835
|
+
s.openUntil = null;
|
|
12836
|
+
s.rateLimited = [];
|
|
12837
|
+
break;
|
|
12838
|
+
case "rate-limited":
|
|
12839
|
+
s.rateLimited.push(at);
|
|
12840
|
+
if (code === "http-429") {
|
|
12841
|
+
s.failures = 0;
|
|
12842
|
+
s.openUntil = null;
|
|
12843
|
+
}
|
|
12844
|
+
break;
|
|
12845
|
+
case "failed":
|
|
12846
|
+
s.failures += 1;
|
|
12847
|
+
s.lastFailure = code;
|
|
12848
|
+
if (probe || s.failures >= BREAKER_FAILURES)
|
|
12849
|
+
s.openUntil = at + BREAKER_OPEN_MS;
|
|
12850
|
+
break;
|
|
12851
|
+
case "neutral":
|
|
12852
|
+
break;
|
|
12853
|
+
}
|
|
12854
|
+
}
|
|
12855
|
+
function isCloudJevHealthMessage(message) {
|
|
12856
|
+
return message.startsWith(JEV_RATE_LIMITED) || message.startsWith(JEV_UNAVAILABLE);
|
|
12857
|
+
}
|
|
12858
|
+
function cloudJevHealthErrors(at = clock()) {
|
|
12859
|
+
const s = state;
|
|
12860
|
+
const out = [];
|
|
12861
|
+
s.rateLimited = s.rateLimited.filter((t) => at - t < RATE_LIMIT_WINDOW_MS);
|
|
12862
|
+
const n = s.rateLimited.length;
|
|
12863
|
+
if (n === 0) {
|
|
12864
|
+
s.reportedRate = null;
|
|
12865
|
+
} else {
|
|
12866
|
+
if (s.reportedRate === null || at - s.reportedRate.at >= REPORT_REFRESH_MS)
|
|
12867
|
+
s.reportedRate = { n, at };
|
|
12868
|
+
const minutes = Math.round(RATE_LIMIT_WINDOW_MS / 60000);
|
|
12869
|
+
const count = s.reportedRate.n;
|
|
12870
|
+
out.push({
|
|
12871
|
+
id: "jevMode",
|
|
12872
|
+
version: null,
|
|
12873
|
+
kind: "daemon",
|
|
12874
|
+
message: `${JEV_RATE_LIMITED}: ${count} call${count === 1 ? "" : "s"} fell back to regex in the last ${minutes} min`
|
|
12875
|
+
});
|
|
12876
|
+
}
|
|
12877
|
+
if (s.openUntil !== null) {
|
|
12878
|
+
out.push({
|
|
12879
|
+
id: "jevMode",
|
|
12880
|
+
version: null,
|
|
12881
|
+
kind: "daemon",
|
|
12882
|
+
message: `${JEV_UNAVAILABLE}: FailproofAI Cloud Jev failed ${BREAKER_FAILURES} or more calls in a row ` + `(last: ${s.lastFailure ?? "error"}), so tool calls skip it for ${BREAKER_OPEN_MS / 1000} s at a time ` + "and the regex decides alone"
|
|
12883
|
+
});
|
|
12884
|
+
}
|
|
12885
|
+
return out;
|
|
12886
|
+
}
|
|
12887
|
+
function reportCloudJevHealth(at = clock()) {
|
|
12888
|
+
try {
|
|
12889
|
+
const entries = cloudJevHealthErrors(at);
|
|
12890
|
+
const text = JSON.stringify(entries);
|
|
12891
|
+
if (text === state.lastReport)
|
|
12892
|
+
return;
|
|
12893
|
+
state.lastReport = text;
|
|
12894
|
+
const report = (readCloudPolicyErrors() ?? []).filter((e) => !(e.id === "jevMode" && isCloudJevHealthMessage(e.message)));
|
|
12895
|
+
writeCloudPolicyErrors([...report, ...entries]);
|
|
12896
|
+
} catch {}
|
|
12897
|
+
}
|
|
12898
|
+
var BREAKER_FAILURES = 3, BREAKER_OPEN_MS = 60000, RATE_LIMIT_WINDOW_MS, REPORT_REFRESH_MS = 30000, JEV_RATE_LIMITED = "jev_rate_limited", JEV_UNAVAILABLE = "jev_unavailable", BREAKER_SKIP_REASON = "unavailable", fresh = (scope) => ({
|
|
12899
|
+
scope,
|
|
12900
|
+
failures: 0,
|
|
12901
|
+
lastFailure: null,
|
|
12902
|
+
openUntil: null,
|
|
12903
|
+
probing: false,
|
|
12904
|
+
rateLimited: [],
|
|
12905
|
+
reportedRate: null,
|
|
12906
|
+
lastReport: "[]"
|
|
12907
|
+
}), state, clock = () => performance.now(), FAILURES;
|
|
12908
|
+
var init_cloud_jev_health = __esm(() => {
|
|
12909
|
+
init_cloud_policy_errors();
|
|
12910
|
+
RATE_LIMIT_WINDOW_MS = 10 * 60000;
|
|
12911
|
+
state = fresh("");
|
|
12912
|
+
FAILURES = new Set(["timeout", "network", "malformed", "model-mismatch", "upstream-error", "error"]);
|
|
12913
|
+
});
|
|
12914
|
+
|
|
12018
12915
|
// src/hooks/semantic/facts.ts
|
|
12019
|
-
import { existsSync as
|
|
12020
|
-
import { homedir as
|
|
12021
|
-
import { dirname as
|
|
12916
|
+
import { existsSync as existsSync21, readFileSync as readFileSync18, statSync as statSync19 } from "node:fs";
|
|
12917
|
+
import { homedir as homedir22 } from "node:os";
|
|
12918
|
+
import { dirname as dirname9, isAbsolute as isAbsolute3, resolve as resolve16 } from "node:path";
|
|
12022
12919
|
function classifyTool(toolName) {
|
|
12023
12920
|
if (SHELL_TOOLS.has(toolName))
|
|
12024
12921
|
return { toolClass: "shell", toolIsKnown: true };
|
|
@@ -12197,10 +13094,10 @@ function pathFact(token, base, projectRoot, home) {
|
|
|
12197
13094
|
const slash = t.lastIndexOf("/");
|
|
12198
13095
|
t = slash === -1 ? "." : slash === 0 ? "/" : t.slice(0, slash);
|
|
12199
13096
|
}
|
|
12200
|
-
const resolved = isAbsolute3(t) ?
|
|
13097
|
+
const resolved = isAbsolute3(t) ? resolve16(t) : resolve16(base, t);
|
|
12201
13098
|
return { asWritten: token, resolved, relation: relationOf(resolved, projectRoot, home) };
|
|
12202
13099
|
}
|
|
12203
|
-
function extractPaths(toolInput, cwd, projectRoot, scanned, home =
|
|
13100
|
+
function extractPaths(toolInput, cwd, projectRoot, scanned, home = homedir22()) {
|
|
12204
13101
|
const base0 = cwd ?? home;
|
|
12205
13102
|
const facts = [];
|
|
12206
13103
|
const seen = new Set;
|
|
@@ -12242,10 +13139,10 @@ function extractPaths(toolInput, cwd, projectRoot, scanned, home = homedir20())
|
|
|
12242
13139
|
function findGitDir(start) {
|
|
12243
13140
|
let dir = start;
|
|
12244
13141
|
for (let i = 0;i < 40; i++) {
|
|
12245
|
-
const candidate =
|
|
12246
|
-
if (
|
|
13142
|
+
const candidate = resolve16(dir, ".git");
|
|
13143
|
+
if (existsSync21(candidate))
|
|
12247
13144
|
return candidate;
|
|
12248
|
-
const parent =
|
|
13145
|
+
const parent = dirname9(dir);
|
|
12249
13146
|
if (parent === dir)
|
|
12250
13147
|
return null;
|
|
12251
13148
|
dir = parent;
|
|
@@ -12256,7 +13153,7 @@ function findProjectRoot(cwd) {
|
|
|
12256
13153
|
if (!cwd)
|
|
12257
13154
|
return null;
|
|
12258
13155
|
const gitDir = findGitDir(cwd);
|
|
12259
|
-
return gitDir ?
|
|
13156
|
+
return gitDir ? dirname9(gitDir) : cwd;
|
|
12260
13157
|
}
|
|
12261
13158
|
function readCurrentBranch(cwd) {
|
|
12262
13159
|
if (!cwd)
|
|
@@ -12265,15 +13162,15 @@ function readCurrentBranch(cwd) {
|
|
|
12265
13162
|
const gitPath = findGitDir(cwd);
|
|
12266
13163
|
if (!gitPath)
|
|
12267
13164
|
return null;
|
|
12268
|
-
let headFile =
|
|
12269
|
-
if (
|
|
12270
|
-
const pointer =
|
|
13165
|
+
let headFile = resolve16(gitPath, "HEAD");
|
|
13166
|
+
if (statSync19(gitPath).isFile()) {
|
|
13167
|
+
const pointer = readFileSync18(gitPath, "utf8").trim();
|
|
12271
13168
|
const m = /^gitdir:\s*(.+)$/.exec(pointer);
|
|
12272
13169
|
if (!m)
|
|
12273
13170
|
return null;
|
|
12274
|
-
headFile =
|
|
13171
|
+
headFile = resolve16(dirname9(gitPath), m[1], "HEAD");
|
|
12275
13172
|
}
|
|
12276
|
-
const head =
|
|
13173
|
+
const head = readFileSync18(headFile, "utf8").trim();
|
|
12277
13174
|
const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
|
|
12278
13175
|
return ref ? ref[1] : null;
|
|
12279
13176
|
} catch {
|
|
@@ -14815,31 +15712,31 @@ __export(exports_intent, {
|
|
|
14815
15712
|
readIntent: () => readIntent
|
|
14816
15713
|
});
|
|
14817
15714
|
import {
|
|
14818
|
-
chmodSync as
|
|
15715
|
+
chmodSync as chmodSync4,
|
|
14819
15716
|
closeSync as closeSync3,
|
|
14820
15717
|
mkdirSync as mkdirSync9,
|
|
14821
15718
|
openSync as openSync3,
|
|
14822
15719
|
readdirSync as readdirSync13,
|
|
14823
|
-
readFileSync as
|
|
15720
|
+
readFileSync as readFileSync19,
|
|
14824
15721
|
readSync as readSync3,
|
|
14825
|
-
renameSync as
|
|
14826
|
-
statSync as
|
|
15722
|
+
renameSync as renameSync6,
|
|
15723
|
+
statSync as statSync20,
|
|
14827
15724
|
unlinkSync as unlinkSync2,
|
|
14828
|
-
writeFileSync as
|
|
15725
|
+
writeFileSync as writeFileSync8
|
|
14829
15726
|
} from "node:fs";
|
|
14830
|
-
import { dirname as
|
|
15727
|
+
import { dirname as dirname10, resolve as resolve17 } from "node:path";
|
|
14831
15728
|
function modesAreMeaningful2() {
|
|
14832
15729
|
return process.platform !== "win32";
|
|
14833
15730
|
}
|
|
14834
15731
|
function storeDirChain() {
|
|
14835
|
-
const home =
|
|
15732
|
+
const home = resolve17(failproofaiHome());
|
|
14836
15733
|
const chain = [];
|
|
14837
15734
|
let dir = sessionsDir();
|
|
14838
15735
|
for (let i = 0;i < 8; i++) {
|
|
14839
15736
|
chain.push(dir);
|
|
14840
15737
|
if (dir === home)
|
|
14841
15738
|
break;
|
|
14842
|
-
const parent =
|
|
15739
|
+
const parent = dirname10(dir);
|
|
14843
15740
|
if (parent === dir)
|
|
14844
15741
|
break;
|
|
14845
15742
|
dir = parent;
|
|
@@ -14852,15 +15749,15 @@ function looseIntentStoreDir() {
|
|
|
14852
15749
|
for (const dir of storeDirChain()) {
|
|
14853
15750
|
let mode;
|
|
14854
15751
|
try {
|
|
14855
|
-
mode =
|
|
15752
|
+
mode = statSync20(dir).mode & 511;
|
|
14856
15753
|
} catch {
|
|
14857
15754
|
continue;
|
|
14858
15755
|
}
|
|
14859
15756
|
if ((mode & DIR_WRITABLE_BY_OTHERS3) === 0)
|
|
14860
15757
|
continue;
|
|
14861
15758
|
try {
|
|
14862
|
-
|
|
14863
|
-
if ((
|
|
15759
|
+
chmodSync4(dir, mode & ~DIR_WRITABLE_BY_OTHERS3);
|
|
15760
|
+
if ((statSync20(dir).mode & DIR_WRITABLE_BY_OTHERS3) !== 0)
|
|
14864
15761
|
return dir;
|
|
14865
15762
|
} catch {
|
|
14866
15763
|
return dir;
|
|
@@ -14870,7 +15767,7 @@ function looseIntentStoreDir() {
|
|
|
14870
15767
|
}
|
|
14871
15768
|
function readIntentFile(sessionId) {
|
|
14872
15769
|
try {
|
|
14873
|
-
const parsed = JSON.parse(
|
|
15770
|
+
const parsed = JSON.parse(readFileSync19(intentFile(sessionId), "utf8"));
|
|
14874
15771
|
return Array.isArray(parsed?.prompts) ? parsed : { prompts: [] };
|
|
14875
15772
|
} catch {
|
|
14876
15773
|
return { prompts: [] };
|
|
@@ -14887,8 +15784,8 @@ function appendPrompt(sessionId, entry) {
|
|
|
14887
15784
|
const target = intentFile(sessionId);
|
|
14888
15785
|
const isNew = !fileExists2(target);
|
|
14889
15786
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
14890
|
-
|
|
14891
|
-
|
|
15787
|
+
writeFileSync8(tmp, JSON.stringify(file), { mode: 384 });
|
|
15788
|
+
renameSync6(tmp, target);
|
|
14892
15789
|
if (isNew)
|
|
14893
15790
|
pruneExpiredSessions(entry.at);
|
|
14894
15791
|
return true;
|
|
@@ -14898,7 +15795,7 @@ function appendPrompt(sessionId, entry) {
|
|
|
14898
15795
|
}
|
|
14899
15796
|
function fileExists2(path) {
|
|
14900
15797
|
try {
|
|
14901
|
-
|
|
15798
|
+
statSync20(path);
|
|
14902
15799
|
return true;
|
|
14903
15800
|
} catch {
|
|
14904
15801
|
return false;
|
|
@@ -14911,9 +15808,9 @@ function pruneExpiredSessions(now = Date.now()) {
|
|
|
14911
15808
|
for (const name of readdirSync13(dir)) {
|
|
14912
15809
|
if (!SESSION_FILE_RE3.test(name))
|
|
14913
15810
|
continue;
|
|
14914
|
-
const path =
|
|
15811
|
+
const path = resolve17(dir, name);
|
|
14915
15812
|
try {
|
|
14916
|
-
const st =
|
|
15813
|
+
const st = statSync20(path);
|
|
14917
15814
|
if (st.isFile() && now - st.mtimeMs > INTENT_MAX_AGE_MS) {
|
|
14918
15815
|
unlinkSync2(path);
|
|
14919
15816
|
removed++;
|
|
@@ -15222,7 +16119,7 @@ function visitLinesBackwards(transcriptPath, maxBytes, visit) {
|
|
|
15222
16119
|
return;
|
|
15223
16120
|
let fd;
|
|
15224
16121
|
try {
|
|
15225
|
-
const st =
|
|
16122
|
+
const st = statSync20(path);
|
|
15226
16123
|
if (!st.isFile() || st.size === 0)
|
|
15227
16124
|
return;
|
|
15228
16125
|
fd = openSync3(path, "r");
|
|
@@ -15295,7 +16192,7 @@ function readIntent(sessionId, now = Date.now()) {
|
|
|
15295
16192
|
const agent = latest && typeof latest.agent === "string" && latest.agent.length > 0 ? latest.agent : null;
|
|
15296
16193
|
return { userSaid: prompts.map((p) => p.text), agentLastMessage: agent };
|
|
15297
16194
|
}
|
|
15298
|
-
var MAX_RECORDED_PROMPTS = 5, INTENT_MAX_AGE_MS, MAX_CLOCK_SKEW_MS, SESSION_ID_RE2, sessionsDir = () =>
|
|
16195
|
+
var MAX_RECORDED_PROMPTS = 5, INTENT_MAX_AGE_MS, MAX_CLOCK_SKEW_MS, SESSION_ID_RE2, sessionsDir = () => resolve17(semanticDir(), "sessions"), intentFile = (sessionId) => resolve17(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) => {
|
|
15299
16196
|
const meta = obj(payload.openclaw);
|
|
15300
16197
|
if (!meta)
|
|
15301
16198
|
return false;
|
|
@@ -15485,6 +16382,10 @@ function normalizeNative(body, sentModel, opts) {
|
|
|
15485
16382
|
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
15486
16383
|
throw new JevError("malformed", "Jev returned no object");
|
|
15487
16384
|
const b = body;
|
|
16385
|
+
const cloud = opts.cloudReply && b.cloud !== undefined ? { cloud: b.cloud } : {};
|
|
16386
|
+
if ((typeof b.answers !== "object" || b.answers === null) && "cloud" in cloud && b.error === undefined) {
|
|
16387
|
+
return { model: typeof b.model === "string" ? scrubSecret(b.model, opts.apiKey) : "", answers: {}, ...cloud };
|
|
16388
|
+
}
|
|
15488
16389
|
if (typeof b.answers !== "object" || b.answers === null) {
|
|
15489
16390
|
if (b.error !== undefined) {
|
|
15490
16391
|
const code = typeof b.error === "object" && b.error !== null ? b.error.code : undefined;
|
|
@@ -15512,7 +16413,8 @@ function normalizeNative(body, sentModel, opts) {
|
|
|
15512
16413
|
model,
|
|
15513
16414
|
answers: b.answers,
|
|
15514
16415
|
...usage ? { usage } : {},
|
|
15515
|
-
...unverified ? { modelUnverified: true } : {}
|
|
16416
|
+
...unverified ? { modelUnverified: true } : {},
|
|
16417
|
+
...cloud
|
|
15516
16418
|
};
|
|
15517
16419
|
}
|
|
15518
16420
|
function nativeTransport(opts) {
|
|
@@ -15710,7 +16612,8 @@ function transportForConfig(input) {
|
|
|
15710
16612
|
apiKey: cfg.apiKey,
|
|
15711
16613
|
model: route.model,
|
|
15712
16614
|
allowUnreported: false,
|
|
15713
|
-
derivedBase: true
|
|
16615
|
+
derivedBase: true,
|
|
16616
|
+
cloudReply: true
|
|
15714
16617
|
})),
|
|
15715
16618
|
via: "failproofai",
|
|
15716
16619
|
model: route.model
|
|
@@ -15880,8 +16783,8 @@ var init_pack_policies = __esm(() => {
|
|
|
15880
16783
|
|
|
15881
16784
|
// src/hooks/semantic/evaluator.ts
|
|
15882
16785
|
import { createHash as createHash6 } from "node:crypto";
|
|
15883
|
-
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync10, renameSync as
|
|
15884
|
-
import { resolve as
|
|
16786
|
+
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync10, renameSync as renameSync7, statSync as statSync21 } from "node:fs";
|
|
16787
|
+
import { resolve as resolve18 } from "node:path";
|
|
15885
16788
|
function storeCut(message) {
|
|
15886
16789
|
return typeof message === "string" && message.length >= MAX_USER_MESSAGE_CHARS - STORE_CUT_SLACK && OMISSION_MARK.test(message);
|
|
15887
16790
|
}
|
|
@@ -16054,14 +16957,14 @@ function appendVerdictLog(row) {
|
|
|
16054
16957
|
const file = verdictLogFile();
|
|
16055
16958
|
mkdirSync10(semanticDir(), { recursive: true, mode: 448 });
|
|
16056
16959
|
try {
|
|
16057
|
-
if (
|
|
16058
|
-
|
|
16960
|
+
if (statSync21(file).size > VERDICT_LOG_MAX_BYTES)
|
|
16961
|
+
renameSync7(file, `${file}.1`);
|
|
16059
16962
|
} catch {}
|
|
16060
16963
|
appendFileSync3(file, JSON.stringify(row) + `
|
|
16061
16964
|
`, { mode: 384 });
|
|
16062
16965
|
} catch {}
|
|
16063
16966
|
}
|
|
16064
|
-
var DEFAULT_JEV_TIMEOUT_MS = 3000, OMISSION_MARK, STORE_CUT_SLACK = 64, VERDICT_LOG_MAX_BYTES, verdictLogFile = () =>
|
|
16967
|
+
var DEFAULT_JEV_TIMEOUT_MS = 3000, OMISSION_MARK, STORE_CUT_SLACK = 64, VERDICT_LOG_MAX_BYTES, verdictLogFile = () => resolve18(semanticDir(), "verdicts.jsonl");
|
|
16065
16968
|
var init_evaluator = __esm(() => {
|
|
16066
16969
|
init_fp_home();
|
|
16067
16970
|
init_compile();
|
|
@@ -16075,6 +16978,500 @@ var init_evaluator = __esm(() => {
|
|
|
16075
16978
|
VERDICT_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
16076
16979
|
});
|
|
16077
16980
|
|
|
16981
|
+
// src/hooks/semantic/cloud-jev.ts
|
|
16982
|
+
function noul2(probe) {
|
|
16983
|
+
return {
|
|
16984
|
+
type: "noul",
|
|
16985
|
+
instructions: probe.instructions,
|
|
16986
|
+
...probe.criteria ? { criteria: { ...probe.criteria } } : {}
|
|
16987
|
+
};
|
|
16988
|
+
}
|
|
16989
|
+
function cloudGlobalQuestions(intent, userSaidCount) {
|
|
16990
|
+
const questions = { [INJECTION_PROBE.id]: noul2(INJECTION_PROBE) };
|
|
16991
|
+
if (userSaidCount > 0) {
|
|
16992
|
+
if (intent === "v1")
|
|
16993
|
+
for (const probe of TASK_PROBES)
|
|
16994
|
+
questions[probe.id] = noul2(probe);
|
|
16995
|
+
else
|
|
16996
|
+
questions[SCOPE_PROBE.id] = noul2(SCOPE_PROBE);
|
|
16997
|
+
}
|
|
16998
|
+
return questions;
|
|
16999
|
+
}
|
|
17000
|
+
function secretWords(toolInput) {
|
|
17001
|
+
const text = (typeof toolInput.command === "string" ? toolInput.command : Object.entries(toolInput).filter(([key, v]) => typeof v === "string" && v.length <= 300 && !/content|old_string|new_string|body|text|prompt/i.test(key)).map(([, v]) => v).join(`
|
|
17002
|
+
`)).slice(0, MAX_SECRET_SCAN_CHARS);
|
|
17003
|
+
try {
|
|
17004
|
+
return redactSecretsDetailed(text, { blunt: false }).found.map((s) => s.toLowerCase()).filter((s) => s.length > 0);
|
|
17005
|
+
} catch {
|
|
17006
|
+
return [];
|
|
17007
|
+
}
|
|
17008
|
+
}
|
|
17009
|
+
function cloudTargetScan(toolInput, scan = scanTargets(toolInput)) {
|
|
17010
|
+
const secrets = scan.groups.length > 0 ? secretWords(toolInput) : [];
|
|
17011
|
+
const hidden = (word) => secrets.some((s) => s === word || word.length >= 8 && s.includes(word) || s.length >= 8 && word.includes(s));
|
|
17012
|
+
const groups = [];
|
|
17013
|
+
let chars = 2;
|
|
17014
|
+
let complete = scan.complete;
|
|
17015
|
+
for (const group of scan.groups) {
|
|
17016
|
+
const words = [...new Set([...group].map((w) => hidden(w) ? REDACTED_TARGET_WORD : w))].sort();
|
|
17017
|
+
const cost = JSON.stringify(words).length + 1;
|
|
17018
|
+
if (chars + cost > MAX_TARGET_SCAN_CHARS) {
|
|
17019
|
+
complete = false;
|
|
17020
|
+
break;
|
|
17021
|
+
}
|
|
17022
|
+
chars += cost;
|
|
17023
|
+
groups.push(words);
|
|
17024
|
+
}
|
|
17025
|
+
return { groups, complete };
|
|
17026
|
+
}
|
|
17027
|
+
function cloudFacts(facts) {
|
|
17028
|
+
const scrub = (s) => s === null ? null : redactSecrets(s, { blunt: false }).text;
|
|
17029
|
+
return {
|
|
17030
|
+
toolName: facts.toolName,
|
|
17031
|
+
toolClass: facts.toolClass,
|
|
17032
|
+
toolIsKnown: facts.toolIsKnown,
|
|
17033
|
+
cwd: scrub(facts.cwd),
|
|
17034
|
+
projectRoot: scrub(facts.projectRoot),
|
|
17035
|
+
currentGitBranch: scrub(facts.currentGitBranch),
|
|
17036
|
+
paths: facts.paths.map((p) => ({ asWritten: scrub(p.asWritten) ?? "", resolved: scrub(p.resolved) ?? "", relation: p.relation })),
|
|
17037
|
+
permissionMode: facts.permissionMode
|
|
17038
|
+
};
|
|
17039
|
+
}
|
|
17040
|
+
function buildCloudBlock(prepared, input, machineId, agent) {
|
|
17041
|
+
const scan = scanTargets(input.toolInput);
|
|
17042
|
+
const block = {
|
|
17043
|
+
v: agent === undefined ? CLOUD_BLOCK_VERSION : 2,
|
|
17044
|
+
machineId,
|
|
17045
|
+
...agent ? { agent } : {},
|
|
17046
|
+
intentMode: prepared.intent,
|
|
17047
|
+
facts: cloudFacts(prepared.facts),
|
|
17048
|
+
targetScan: cloudTargetScan(input.toolInput, scan),
|
|
17049
|
+
userSaid: prepared.userSaid.map((turn) => redactSecrets(turn, { blunt: true }).text.replace(REDACTION_MARKER, REDACTED_SAID)),
|
|
17050
|
+
agentLastMessage: prepared.agentLastMessage,
|
|
17051
|
+
userSaidCut: prepared.userSaidCut,
|
|
17052
|
+
localPolicies: prepared.selected.map((p) => p.name)
|
|
17053
|
+
};
|
|
17054
|
+
if (blockBytes(block) > MAX_CLOUD_BLOCK_BYTES) {
|
|
17055
|
+
block.userSaid = prepared.userSaid.length > 0 ? [""] : [];
|
|
17056
|
+
block.userSaidCut = false;
|
|
17057
|
+
block.targetScan = { ...block.targetScan, complete: false };
|
|
17058
|
+
}
|
|
17059
|
+
if (blockBytes(block) > MAX_CLOUD_BLOCK_BYTES) {
|
|
17060
|
+
block.targetScan = { groups: [], complete: false };
|
|
17061
|
+
}
|
|
17062
|
+
if (block.targetScan.complete && namingReadings(block.intentMode, scan.groups, prepared.userSaid, prepared.agentLastMessage) !== namingReadings(block.intentMode, block.targetScan.groups.map((g) => new Set(g)), block.userSaid, block.agentLastMessage)) {
|
|
17063
|
+
block.targetScan = { ...block.targetScan, complete: false };
|
|
17064
|
+
}
|
|
17065
|
+
return block;
|
|
17066
|
+
}
|
|
17067
|
+
function namingReadings(intent, groups, userSaid, agentLastMessage) {
|
|
17068
|
+
const scan = { targets: new Set, groups, complete: true };
|
|
17069
|
+
const said = intent === "v1" && agentLastMessage ? [...userSaid, agentLastMessage] : userSaid;
|
|
17070
|
+
return [groups.length, userSaid.length > 0, everyTargetNamed(scan, said), intent === "v1" && partlyNamed(scan, said)].join();
|
|
17071
|
+
}
|
|
17072
|
+
function buildCloudRequest(prepared, input, machineId, agent) {
|
|
17073
|
+
const own = prepared.compiled.request;
|
|
17074
|
+
return {
|
|
17075
|
+
model: own.model,
|
|
17076
|
+
state: own.state,
|
|
17077
|
+
questions: { ...own.questions, ...cloudGlobalQuestions(prepared.intent, prepared.userSaid.length) },
|
|
17078
|
+
cloud: buildCloudBlock(prepared, input, machineId, agent)
|
|
17079
|
+
};
|
|
17080
|
+
}
|
|
17081
|
+
function malformed(what) {
|
|
17082
|
+
throw new JevError("malformed", `FailproofAI Cloud's reply: ${what}`);
|
|
17083
|
+
}
|
|
17084
|
+
function nullableProbability(v, what) {
|
|
17085
|
+
if (v === null || v === undefined)
|
|
17086
|
+
return null;
|
|
17087
|
+
if (!isProbability(v))
|
|
17088
|
+
malformed(`${what} is not a probability`);
|
|
17089
|
+
return v;
|
|
17090
|
+
}
|
|
17091
|
+
function names(v, what) {
|
|
17092
|
+
if (v === undefined || v === null)
|
|
17093
|
+
return [];
|
|
17094
|
+
if (!Array.isArray(v) || v.length > MAX_LIST || !v.every(isName2))
|
|
17095
|
+
malformed(`${what} is not a list of names`);
|
|
17096
|
+
return [...v];
|
|
17097
|
+
}
|
|
17098
|
+
function parseOutcome(raw, i) {
|
|
17099
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
17100
|
+
malformed(`outcome ${i} is not an object`);
|
|
17101
|
+
const o = raw;
|
|
17102
|
+
if (!isName2(o.policy))
|
|
17103
|
+
malformed(`outcome ${i} has no policy name`);
|
|
17104
|
+
if (o.mode !== "deny" && o.mode !== "instruct")
|
|
17105
|
+
malformed(`outcome ${i} has no mode`);
|
|
17106
|
+
if (!isProbability(o.evidence))
|
|
17107
|
+
malformed(`outcome ${i} has no evidence`);
|
|
17108
|
+
if (typeof o.targetNamedByUser !== "boolean" || typeof o.escalatedByInjection !== "boolean") {
|
|
17109
|
+
malformed(`outcome ${i} is missing a flag`);
|
|
17110
|
+
}
|
|
17111
|
+
if (typeof o.verdict !== "string" || !VERDICTS.has(o.verdict))
|
|
17112
|
+
malformed(`outcome ${i} has no verdict`);
|
|
17113
|
+
if (o.intent !== undefined && (typeof o.intent !== "string" || !INTENTS.has(o.intent)))
|
|
17114
|
+
malformed(`outcome ${i} has an unknown intent`);
|
|
17115
|
+
if (o.targetScanIncomplete !== undefined && o.targetScanIncomplete !== true)
|
|
17116
|
+
malformed(`outcome ${i} has a bad targetScanIncomplete`);
|
|
17117
|
+
if (o.userCanOverride !== undefined && typeof o.userCanOverride !== "boolean")
|
|
17118
|
+
malformed(`outcome ${i} has a bad userCanOverride`);
|
|
17119
|
+
const origin = o.origin;
|
|
17120
|
+
if (!origin || typeof origin !== "object" || typeof origin.cloudPolicyId !== "string" || !POLICY_ID_RE2.test(origin.cloudPolicyId) || !Number.isSafeInteger(origin.cloudVersion) || origin.cloudVersion < 0) {
|
|
17121
|
+
malformed(`outcome ${i} has no Cloud origin`);
|
|
17122
|
+
}
|
|
17123
|
+
if (o.effect !== undefined && o.effect !== "enforce" && o.effect !== "observe")
|
|
17124
|
+
malformed(`outcome ${i} has an unknown effect`);
|
|
17125
|
+
if (o.reason !== undefined && o.reason !== null && typeof o.reason !== "string")
|
|
17126
|
+
malformed(`outcome ${i} reason is not text`);
|
|
17127
|
+
const outcome = {
|
|
17128
|
+
policy: o.policy,
|
|
17129
|
+
mode: o.mode,
|
|
17130
|
+
...typeof o.userCanOverride === "boolean" ? { userCanOverride: o.userCanOverride } : {},
|
|
17131
|
+
origin: { packId: `cloud:${origin.cloudPolicyId}`, packVersion: String(origin.cloudVersion) },
|
|
17132
|
+
evidence: o.evidence,
|
|
17133
|
+
exempt: nullableProbability(o.exempt, `outcome ${i} exempt`),
|
|
17134
|
+
userAsked: nullableProbability(o.userAsked, `outcome ${i} userAsked`),
|
|
17135
|
+
targetNamedByUser: o.targetNamedByUser,
|
|
17136
|
+
escalatedByInjection: o.escalatedByInjection,
|
|
17137
|
+
verdict: o.verdict,
|
|
17138
|
+
...o.intent !== undefined ? { intent: o.intent } : {},
|
|
17139
|
+
...o.targetScanIncomplete === true ? { targetScanIncomplete: true } : {}
|
|
17140
|
+
};
|
|
17141
|
+
return {
|
|
17142
|
+
outcome,
|
|
17143
|
+
cloudPolicyId: origin.cloudPolicyId,
|
|
17144
|
+
cloudVersion: origin.cloudVersion,
|
|
17145
|
+
observe: o.effect === "observe",
|
|
17146
|
+
reason: typeof o.reason === "string" ? capReason(o.reason) : null
|
|
17147
|
+
};
|
|
17148
|
+
}
|
|
17149
|
+
function capReason(reason) {
|
|
17150
|
+
return reason.length > MAX_CLOUD_REASON_CHARS ? `${reason.slice(0, MAX_CLOUD_REASON_CHARS - 1)}…` : reason;
|
|
17151
|
+
}
|
|
17152
|
+
function observedChecks(parsed, watched = null) {
|
|
17153
|
+
const firing = watched?.parsed.filter((p) => p.outcome.verdict === "deny" || p.outcome.verdict === "instruct") ?? [];
|
|
17154
|
+
const watchedReason = firing.length === 1 && typeof watched?.v.reason === "string" ? capReason(watched.v.reason) : null;
|
|
17155
|
+
const byPolicy = new Map;
|
|
17156
|
+
for (const p of parsed) {
|
|
17157
|
+
const verdict = p.outcome.verdict;
|
|
17158
|
+
if (verdict !== "deny" && verdict !== "instruct")
|
|
17159
|
+
continue;
|
|
17160
|
+
const prev = byPolicy.get(p.cloudPolicyId);
|
|
17161
|
+
if (prev && SEVERITY[prev.decision] >= SEVERITY[verdict])
|
|
17162
|
+
continue;
|
|
17163
|
+
byPolicy.set(p.cloudPolicyId, {
|
|
17164
|
+
cloudPolicyId: p.cloudPolicyId,
|
|
17165
|
+
cloudVersion: p.cloudVersion,
|
|
17166
|
+
check: p.outcome.policy,
|
|
17167
|
+
decision: verdict,
|
|
17168
|
+
reason: p.reason ?? (firing[0] === p ? watchedReason : null) ?? `Flagged by semantic review (semantic/${p.outcome.policy})`
|
|
17169
|
+
});
|
|
17170
|
+
}
|
|
17171
|
+
return [...byPolicy.values()];
|
|
17172
|
+
}
|
|
17173
|
+
function parseVerdictBlock(rawVerdict, rawAsked, what) {
|
|
17174
|
+
const v = rawVerdict;
|
|
17175
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
17176
|
+
malformed(`no ${what}`);
|
|
17177
|
+
if (typeof v.decision !== "string" || !DECISIONS2.has(v.decision))
|
|
17178
|
+
malformed(`${what} has no decision`);
|
|
17179
|
+
if (v.reason !== null && v.reason !== undefined && typeof v.reason !== "string")
|
|
17180
|
+
malformed(`${what} reason is not text`);
|
|
17181
|
+
if (!Array.isArray(v.outcomes) || v.outcomes.length > MAX_LIST)
|
|
17182
|
+
malformed(`${what} has no outcomes list`);
|
|
17183
|
+
if (v.beyondTask !== undefined && typeof v.beyondTask !== "boolean")
|
|
17184
|
+
malformed(`${what} beyondTask is not a flag`);
|
|
17185
|
+
const parsed = v.outcomes.map(parseOutcome);
|
|
17186
|
+
const asked = names(rawAsked, `${what} asked`);
|
|
17187
|
+
const askedSet = new Set(asked);
|
|
17188
|
+
const seen = new Set;
|
|
17189
|
+
parsed.forEach(({ outcome: o }, i) => {
|
|
17190
|
+
if (!askedSet.has(o.policy))
|
|
17191
|
+
malformed(`${what} outcome ${i} answers ${JSON.stringify(o.policy)}, which Cloud did not ask`);
|
|
17192
|
+
const key = `${o.origin?.packId ?? ""}\x00${o.policy}`;
|
|
17193
|
+
if (seen.has(key))
|
|
17194
|
+
malformed(`${what} outcome ${i} answers ${JSON.stringify(o.policy)} twice`);
|
|
17195
|
+
seen.add(key);
|
|
17196
|
+
});
|
|
17197
|
+
return { v, parsed, asked };
|
|
17198
|
+
}
|
|
17199
|
+
function parseCloudReply(raw, observedPolicies = []) {
|
|
17200
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
17201
|
+
malformed("no cloud block");
|
|
17202
|
+
const block = raw;
|
|
17203
|
+
const { v, parsed, asked } = parseVerdictBlock(block.verdict, block.asked, "verdict");
|
|
17204
|
+
let watched = null;
|
|
17205
|
+
if (block.observed !== undefined && block.observed !== null) {
|
|
17206
|
+
const o = block.observed;
|
|
17207
|
+
if (typeof o !== "object" || Array.isArray(o))
|
|
17208
|
+
malformed("observed is not an object");
|
|
17209
|
+
watched = parseVerdictBlock(o.verdict, o.asked, "observed verdict");
|
|
17210
|
+
if (watched.parsed.some((p) => p.outcome.verdict === "deny") && watched.v.decision !== "deny") {
|
|
17211
|
+
malformed("an observed check denied but its verdict does not");
|
|
17212
|
+
}
|
|
17213
|
+
}
|
|
17214
|
+
const observeIds = new Set(observedPolicies);
|
|
17215
|
+
const isObserved = (p) => p.observe || observeIds.has(p.cloudPolicyId);
|
|
17216
|
+
const enforced = parsed.filter((p) => !isObserved(p)).map((p) => p.outcome);
|
|
17217
|
+
const observedHere = parsed.filter(isObserved);
|
|
17218
|
+
const observed = [...observedHere, ...watched?.parsed ?? []];
|
|
17219
|
+
if (enforced.some((o) => o.verdict === "deny") && v.decision !== "deny")
|
|
17220
|
+
malformed("a check denied but the verdict does not");
|
|
17221
|
+
const cloudReason = typeof v.reason === "string" ? capReason(v.reason) : null;
|
|
17222
|
+
let decision = v.decision;
|
|
17223
|
+
let reason = cloudReason;
|
|
17224
|
+
if (observedHere.length > 0) {
|
|
17225
|
+
const own = enforced.some((o) => o.verdict === "deny") ? "deny" : enforced.some((o) => o.verdict === "instruct") || v.beyondTask === true ? "instruct" : "allow";
|
|
17226
|
+
const capped = RANK2[own] <= RANK2[decision] ? own : decision;
|
|
17227
|
+
if (capped !== decision)
|
|
17228
|
+
reason = null;
|
|
17229
|
+
decision = capped;
|
|
17230
|
+
}
|
|
17231
|
+
return {
|
|
17232
|
+
observed: observedChecks(observed, watched),
|
|
17233
|
+
observedAsked: [...observedHere.map((p) => p.outcome.policy), ...watched?.asked ?? []],
|
|
17234
|
+
verdict: {
|
|
17235
|
+
decision,
|
|
17236
|
+
reason,
|
|
17237
|
+
outcomes: enforced,
|
|
17238
|
+
injectionSuspected: nullableProbability(v.injectionSuspected, "injectionSuspected"),
|
|
17239
|
+
scopeWithinRequest: nullableProbability(v.scopeWithinRequest, "scopeWithinRequest"),
|
|
17240
|
+
beyondTask: v.beyondTask === true
|
|
17241
|
+
},
|
|
17242
|
+
asked,
|
|
17243
|
+
droppedLocal: names(block.droppedLocal, "droppedLocal"),
|
|
17244
|
+
droppedCloud: names(block.droppedCloud, "droppedCloud")
|
|
17245
|
+
};
|
|
17246
|
+
}
|
|
17247
|
+
function parseObservedOutcomes(raw) {
|
|
17248
|
+
try {
|
|
17249
|
+
const block = raw?.observed;
|
|
17250
|
+
if (!block || typeof block !== "object" || Array.isArray(block))
|
|
17251
|
+
return [];
|
|
17252
|
+
const asked = new Set(names(block.asked, "observed.asked"));
|
|
17253
|
+
const outcomes = block.verdict?.outcomes ?? [];
|
|
17254
|
+
if (!Array.isArray(outcomes) || outcomes.length > MAX_LIST)
|
|
17255
|
+
return [];
|
|
17256
|
+
const seen = new Set;
|
|
17257
|
+
const out = [];
|
|
17258
|
+
outcomes.forEach((o, i) => {
|
|
17259
|
+
const parsed = parseOutcome(o, i).outcome;
|
|
17260
|
+
if (!asked.has(parsed.policy) || seen.has(parsed.policy))
|
|
17261
|
+
return;
|
|
17262
|
+
seen.add(parsed.policy);
|
|
17263
|
+
out.push(parsed);
|
|
17264
|
+
});
|
|
17265
|
+
return out;
|
|
17266
|
+
} catch {
|
|
17267
|
+
return [];
|
|
17268
|
+
}
|
|
17269
|
+
}
|
|
17270
|
+
function joinLines(reasons) {
|
|
17271
|
+
const lines = [];
|
|
17272
|
+
for (const reason of reasons) {
|
|
17273
|
+
if (!reason)
|
|
17274
|
+
continue;
|
|
17275
|
+
for (const line of reason.split(`
|
|
17276
|
+
`))
|
|
17277
|
+
if (!lines.includes(line))
|
|
17278
|
+
lines.push(line);
|
|
17279
|
+
}
|
|
17280
|
+
return lines.length > 0 ? lines.join(`
|
|
17281
|
+
`) : null;
|
|
17282
|
+
}
|
|
17283
|
+
function mergeCloudVerdict(cloud, local) {
|
|
17284
|
+
const decision = RANK2[cloud.decision] >= RANK2[local.decision] ? cloud.decision : local.decision;
|
|
17285
|
+
let reason;
|
|
17286
|
+
if (decision === "deny") {
|
|
17287
|
+
reason = cloud.decision === "deny" ? cloud.reason ?? local.reason : local.reason;
|
|
17288
|
+
} else if (decision === "instruct") {
|
|
17289
|
+
reason = joinLines([cloud, local].filter((v) => v.decision === "instruct").map((v) => v.reason));
|
|
17290
|
+
} else {
|
|
17291
|
+
reason = joinLines([cloud.reason, local.reason]);
|
|
17292
|
+
}
|
|
17293
|
+
const beyond = cloud.beyondTask === undefined && local.beyondTask === undefined ? undefined : cloud.beyondTask === true || local.beyondTask === true;
|
|
17294
|
+
return {
|
|
17295
|
+
decision,
|
|
17296
|
+
reason,
|
|
17297
|
+
outcomes: [...cloud.outcomes, ...local.outcomes],
|
|
17298
|
+
injectionSuspected: local.injectionSuspected ?? cloud.injectionSuspected,
|
|
17299
|
+
scopeWithinRequest: local.scopeWithinRequest ?? cloud.scopeWithinRequest,
|
|
17300
|
+
...beyond !== undefined ? { beyondTask: beyond } : {}
|
|
17301
|
+
};
|
|
17302
|
+
}
|
|
17303
|
+
function envNumber2(name, fallback) {
|
|
17304
|
+
const n = Number(process.env[name]);
|
|
17305
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
17306
|
+
}
|
|
17307
|
+
function expectedQuestions(request, prepared, dropped) {
|
|
17308
|
+
if (dropped.size === 0)
|
|
17309
|
+
return request;
|
|
17310
|
+
const questions = Object.fromEntries(Object.entries(request.questions).filter(([id]) => {
|
|
17311
|
+
const owner = prepared.compiled.owners.get(id);
|
|
17312
|
+
return !(typeof owner === "string" && dropped.has(owner));
|
|
17313
|
+
}));
|
|
17314
|
+
return { ...request, questions };
|
|
17315
|
+
}
|
|
17316
|
+
function cloudCheckAnswers(response, asked) {
|
|
17317
|
+
const out = {};
|
|
17318
|
+
if (asked.length === 0)
|
|
17319
|
+
return out;
|
|
17320
|
+
const names = new Set(asked);
|
|
17321
|
+
const answers = response.answers && typeof response.answers === "object" ? response.answers : {};
|
|
17322
|
+
for (const [id, a] of Object.entries(answers)) {
|
|
17323
|
+
const dot = id.lastIndexOf(".");
|
|
17324
|
+
if (dot <= 0 || !names.has(id.slice(0, dot)) || !QUESTION_PART_RE.test(id.slice(dot + 1)))
|
|
17325
|
+
continue;
|
|
17326
|
+
const p = a?.noul;
|
|
17327
|
+
if (isProbability(p))
|
|
17328
|
+
out[id] = p;
|
|
17329
|
+
}
|
|
17330
|
+
return out;
|
|
17331
|
+
}
|
|
17332
|
+
function presentAnswers(response) {
|
|
17333
|
+
const out = {};
|
|
17334
|
+
const answers = response.answers && typeof response.answers === "object" ? response.answers : {};
|
|
17335
|
+
for (const [id, a] of Object.entries(answers)) {
|
|
17336
|
+
const p = a?.noul;
|
|
17337
|
+
if (isProbability(p))
|
|
17338
|
+
out[id] = p;
|
|
17339
|
+
}
|
|
17340
|
+
return out;
|
|
17341
|
+
}
|
|
17342
|
+
async function evaluateCloudSemantic(input, opts) {
|
|
17343
|
+
const started = performance.now();
|
|
17344
|
+
const elapsed = () => Math.round(performance.now() - started);
|
|
17345
|
+
const tool = classifyTool(input.toolName);
|
|
17346
|
+
if (tool.toolIsKnown && tool.toolClass === "other") {
|
|
17347
|
+
return {
|
|
17348
|
+
status: "ok",
|
|
17349
|
+
verdict: { ...EMPTY_ALLOW, outcomes: [] },
|
|
17350
|
+
answers: {},
|
|
17351
|
+
latencyMs: elapsed(),
|
|
17352
|
+
inputTokens: null,
|
|
17353
|
+
questionCount: 0,
|
|
17354
|
+
truncated: false,
|
|
17355
|
+
requestCut: false,
|
|
17356
|
+
redactions: 0,
|
|
17357
|
+
model: opts.model ?? DEFAULT_JEV_MODEL,
|
|
17358
|
+
modelVerified: true,
|
|
17359
|
+
via: "none",
|
|
17360
|
+
cloud: { asked: [], droppedLocal: [], droppedCloud: [], answers: {}, observed: [], observedAsked: [], observedOutcomes: [] }
|
|
17361
|
+
};
|
|
17362
|
+
}
|
|
17363
|
+
let prepared;
|
|
17364
|
+
let request;
|
|
17365
|
+
try {
|
|
17366
|
+
prepared = prepareSemantic(input, opts);
|
|
17367
|
+
request = buildCloudRequest(prepared, input, opts.machineId, opts.agent);
|
|
17368
|
+
} catch (err) {
|
|
17369
|
+
return {
|
|
17370
|
+
status: "degraded",
|
|
17371
|
+
reason: `prepare: ${err instanceof Error ? err.message : String(err)}`,
|
|
17372
|
+
latencyMs: elapsed(),
|
|
17373
|
+
questionCount: 0,
|
|
17374
|
+
truncated: false,
|
|
17375
|
+
requestCut: false
|
|
17376
|
+
};
|
|
17377
|
+
}
|
|
17378
|
+
const questionCount = Object.keys(request.questions).length;
|
|
17379
|
+
const { cloud: _block, ...forwarded } = request;
|
|
17380
|
+
const oversized = prepared.oversized || JSON.stringify(forwarded).length > MAX_REQUEST_CHARS;
|
|
17381
|
+
const truncated = prepared.truncated || oversized;
|
|
17382
|
+
const requestCut = prepared.requestCut || oversized;
|
|
17383
|
+
const degraded = (reason) => ({
|
|
17384
|
+
status: "degraded",
|
|
17385
|
+
reason,
|
|
17386
|
+
latencyMs: elapsed(),
|
|
17387
|
+
questionCount,
|
|
17388
|
+
truncated,
|
|
17389
|
+
requestCut
|
|
17390
|
+
});
|
|
17391
|
+
const transport = opts.transport;
|
|
17392
|
+
if (!transport)
|
|
17393
|
+
return degraded("no-transport");
|
|
17394
|
+
const via = opts.via ?? "failproofai";
|
|
17395
|
+
if (opts.signal?.aborted)
|
|
17396
|
+
return degraded("aborted");
|
|
17397
|
+
try {
|
|
17398
|
+
const timeout = AbortSignal.timeout(opts.timeoutMs ?? envNumber2("FAILPROOFAI_JEV_TIMEOUT_MS", DEFAULT_JEV_TIMEOUT_MS));
|
|
17399
|
+
const signal = opts.signal ? AbortSignal.any([timeout, opts.signal]) : timeout;
|
|
17400
|
+
const response = await transport(request, signal);
|
|
17401
|
+
const reply = parseCloudReply(response.cloud, opts.observedPolicies);
|
|
17402
|
+
const observedAsked = new Set(reply.observedAsked);
|
|
17403
|
+
const observedOutcomes = parseObservedOutcomes(response.cloud).filter((o) => observedAsked.has(o.policy));
|
|
17404
|
+
const own = new Map(prepared.selected.map((p) => [p.name, p]));
|
|
17405
|
+
const dropped = new Set(reply.droppedLocal.filter((name) => own.has(name)));
|
|
17406
|
+
const kept = prepared.selected.filter((p) => !dropped.has(p.name));
|
|
17407
|
+
const shortCircuit = kept.length === 0 && reply.asked.length === 0 && reply.verdict.outcomes.length === 0 && reply.observedAsked.length === 0;
|
|
17408
|
+
const answers = shortCircuit ? presentAnswers(response) : readAnswers(expectedQuestions(request, prepared, dropped), response);
|
|
17409
|
+
const local = prepared.intent === "v1" ? decideV1(kept, answers, input.toolInput, prepared.userSaid, prepared.agentLastMessage, {
|
|
17410
|
+
...opts.v1,
|
|
17411
|
+
userSaidCut: prepared.userSaidCut
|
|
17412
|
+
}) : decide(kept, answers, input.toolInput, prepared.userSaid, opts.thresholds ?? DEFAULT_THRESHOLDS, prepared.userSaidCut);
|
|
17413
|
+
return {
|
|
17414
|
+
status: "ok",
|
|
17415
|
+
verdict: mergeCloudVerdict(reply.verdict, local),
|
|
17416
|
+
answers,
|
|
17417
|
+
latencyMs: elapsed(),
|
|
17418
|
+
inputTokens: typeof response.usage?.input_tokens === "number" ? response.usage.input_tokens : null,
|
|
17419
|
+
questionCount,
|
|
17420
|
+
truncated,
|
|
17421
|
+
requestCut,
|
|
17422
|
+
redactions: prepared.envelope.redactions,
|
|
17423
|
+
model: response.model || request.model,
|
|
17424
|
+
modelVerified: response.modelUnverified !== true,
|
|
17425
|
+
via,
|
|
17426
|
+
cloud: {
|
|
17427
|
+
asked: reply.asked,
|
|
17428
|
+
droppedLocal: [...dropped].map((name) => ({ name, packId: own.get(name)?.origin?.packId ?? null })),
|
|
17429
|
+
droppedCloud: reply.droppedCloud,
|
|
17430
|
+
answers: cloudCheckAnswers(response, [...reply.asked, ...reply.observedAsked]),
|
|
17431
|
+
observed: reply.observed,
|
|
17432
|
+
observedAsked: reply.observedAsked,
|
|
17433
|
+
observedOutcomes
|
|
17434
|
+
}
|
|
17435
|
+
};
|
|
17436
|
+
} catch (err) {
|
|
17437
|
+
if (opts.signal?.aborted)
|
|
17438
|
+
return degraded("aborted");
|
|
17439
|
+
if (err instanceof JevError)
|
|
17440
|
+
return degraded(err.code);
|
|
17441
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError"))
|
|
17442
|
+
return degraded("timeout");
|
|
17443
|
+
return degraded(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
17444
|
+
}
|
|
17445
|
+
}
|
|
17446
|
+
var CLOUD_BLOCK_VERSION = 1, MAX_TARGET_SCAN_CHARS = 64000, MAX_CLOUD_BLOCK_BYTES = 240000, blockBytes = (block) => Buffer.byteLength(JSON.stringify(block), "utf8"), REDACTED_TARGET_WORD = "\x00redacted", MAX_SECRET_SCAN_CHARS, REDACTION_MARKER, REDACTED_SAID = "\x00", POLICY_ID_RE2, MAX_NAME_CHARS = 256, MAX_LIST = 4096, MAX_CLOUD_REASON_CHARS = 8192, isProbability = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 1, isName2 = (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_NAME_CHARS, DECISIONS2, VERDICTS, INTENTS, SEVERITY, RANK2, EMPTY_ALLOW, QUESTION_PART_RE;
|
|
17447
|
+
var init_cloud_jev = __esm(() => {
|
|
17448
|
+
init_compile();
|
|
17449
|
+
init_decide();
|
|
17450
|
+
init_envelope();
|
|
17451
|
+
init_facts();
|
|
17452
|
+
init_evaluator();
|
|
17453
|
+
init_jev_client();
|
|
17454
|
+
init_policies();
|
|
17455
|
+
init_redact();
|
|
17456
|
+
MAX_SECRET_SCAN_CHARS = 4 * MAX_TARGET_SCAN_CHARS;
|
|
17457
|
+
REDACTION_MARKER = /<redacted:[^>]*>/g;
|
|
17458
|
+
POLICY_ID_RE2 = /^[A-Za-z0-9._-]{1,128}$/;
|
|
17459
|
+
DECISIONS2 = new Set(["allow", "deny", "instruct"]);
|
|
17460
|
+
VERDICTS = new Set(["deny", "instruct", "overridden", "none"]);
|
|
17461
|
+
INTENTS = new Set(["op-requested", "task-step", "downgraded-task-step"]);
|
|
17462
|
+
SEVERITY = { instruct: 1, deny: 2 };
|
|
17463
|
+
RANK2 = { allow: 0, instruct: 1, deny: 2 };
|
|
17464
|
+
EMPTY_ALLOW = {
|
|
17465
|
+
decision: "allow",
|
|
17466
|
+
reason: null,
|
|
17467
|
+
outcomes: [],
|
|
17468
|
+
injectionSuspected: null,
|
|
17469
|
+
scopeWithinRequest: null,
|
|
17470
|
+
beyondTask: false
|
|
17471
|
+
};
|
|
17472
|
+
QUESTION_PART_RE = /^[a-z][a-z0-9_]{0,31}$/;
|
|
17473
|
+
});
|
|
17474
|
+
|
|
16078
17475
|
// src/hooks/semantic/jev-throttle.ts
|
|
16079
17476
|
var exports_jev_throttle = {};
|
|
16080
17477
|
__export(exports_jev_throttle, {
|
|
@@ -16285,10 +17682,10 @@ var init_jev_throttle = __esm(() => {
|
|
|
16285
17682
|
});
|
|
16286
17683
|
|
|
16287
17684
|
// src/hooks/semantic/session-root.ts
|
|
16288
|
-
import { mkdirSync as mkdirSync11, readdirSync as readdirSync14, readFileSync as
|
|
16289
|
-
import { isAbsolute as isAbsolute4, resolve as
|
|
17685
|
+
import { mkdirSync as mkdirSync11, readdirSync as readdirSync14, readFileSync as readFileSync20, statSync as statSync22, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
17686
|
+
import { isAbsolute as isAbsolute4, resolve as resolve19 } from "node:path";
|
|
16290
17687
|
function validRoot(v) {
|
|
16291
|
-
return typeof v === "string" && isAbsolute4(v) &&
|
|
17688
|
+
return typeof v === "string" && isAbsolute4(v) && resolve19(v) === v && v !== "/";
|
|
16292
17689
|
}
|
|
16293
17690
|
function storeIsOurs() {
|
|
16294
17691
|
if (process.platform === "win32")
|
|
@@ -16296,7 +17693,7 @@ function storeIsOurs() {
|
|
|
16296
17693
|
if (looseIntentStoreDir() !== null)
|
|
16297
17694
|
return false;
|
|
16298
17695
|
try {
|
|
16299
|
-
const mode =
|
|
17696
|
+
const mode = statSync22(rootsDir()).mode & 511;
|
|
16300
17697
|
return (mode & DIR_WRITABLE_BY_OTHERS4) === 0;
|
|
16301
17698
|
} catch {
|
|
16302
17699
|
return true;
|
|
@@ -16304,7 +17701,7 @@ function storeIsOurs() {
|
|
|
16304
17701
|
}
|
|
16305
17702
|
function readPinned(sessionId) {
|
|
16306
17703
|
try {
|
|
16307
|
-
const parsed = JSON.parse(
|
|
17704
|
+
const parsed = JSON.parse(readFileSync20(rootFile(sessionId), "utf8"));
|
|
16308
17705
|
return validRoot(parsed?.root) ? parsed.root : null;
|
|
16309
17706
|
} catch {
|
|
16310
17707
|
return null;
|
|
@@ -16317,9 +17714,9 @@ function pruneSessionRoots(now = Date.now()) {
|
|
|
16317
17714
|
for (const name of readdirSync14(dir)) {
|
|
16318
17715
|
if (!ROOT_FILE_RE.test(name))
|
|
16319
17716
|
continue;
|
|
16320
|
-
const path =
|
|
17717
|
+
const path = resolve19(dir, name);
|
|
16321
17718
|
try {
|
|
16322
|
-
const st =
|
|
17719
|
+
const st = statSync22(path);
|
|
16323
17720
|
if (st.isFile() && now - st.mtimeMs > SESSION_ROOT_MAX_AGE_MS) {
|
|
16324
17721
|
unlinkSync3(path);
|
|
16325
17722
|
removed++;
|
|
@@ -16344,12 +17741,12 @@ function sessionProjectRoot(sessionId, cwd, now = Date.now()) {
|
|
|
16344
17741
|
if (!validRoot(live))
|
|
16345
17742
|
return live;
|
|
16346
17743
|
mkdirSync11(rootsDir(), { recursive: true, mode: 448 });
|
|
16347
|
-
|
|
17744
|
+
writeFileSync9(rootFile(sessionId), JSON.stringify({ root: live, at: now }), { mode: 384, flag: "wx" });
|
|
16348
17745
|
pruneSessionRoots(now);
|
|
16349
17746
|
} catch {}
|
|
16350
17747
|
return live;
|
|
16351
17748
|
}
|
|
16352
|
-
var SESSION_ID_RE3, ROOT_FILE_RE, SESSION_ROOT_MAX_AGE_MS, DIR_WRITABLE_BY_OTHERS4 = 18, rootsDir = () =>
|
|
17749
|
+
var SESSION_ID_RE3, ROOT_FILE_RE, SESSION_ROOT_MAX_AGE_MS, DIR_WRITABLE_BY_OTHERS4 = 18, rootsDir = () => resolve19(semanticDir(), "roots"), rootFile = (sessionId) => resolve19(rootsDir(), `${sessionId}.json`);
|
|
16353
17750
|
var init_session_root = __esm(() => {
|
|
16354
17751
|
init_fp_home();
|
|
16355
17752
|
init_facts();
|
|
@@ -16387,6 +17784,13 @@ function resolveTimeout(cfg) {
|
|
|
16387
17784
|
return DEFAULT_JEV_TIMEOUT_MS;
|
|
16388
17785
|
return Math.min(MAX_JEV_TIMEOUT_MS2, Math.max(MIN_JEV_TIMEOUT_MS2, t));
|
|
16389
17786
|
}
|
|
17787
|
+
function reviewerKey(o) {
|
|
17788
|
+
const packId = o.origin?.packId;
|
|
17789
|
+
return packId !== undefined && packId.startsWith("cloud:") ? cloudReviewerName(packId.slice("cloud:".length), o.policy) : o.policy;
|
|
17790
|
+
}
|
|
17791
|
+
function isCloudOutcome(o) {
|
|
17792
|
+
return o.origin?.packId?.startsWith("cloud:") === true;
|
|
17793
|
+
}
|
|
16390
17794
|
function jevDecider(outcome) {
|
|
16391
17795
|
const wanted = outcome.verdict.decision === "deny" ? "deny" : "instruct";
|
|
16392
17796
|
return outcome.verdict.outcomes.find((o) => o.verdict === wanted);
|
|
@@ -16401,6 +17805,55 @@ function fallbackCode(reason) {
|
|
|
16401
17805
|
const code = reason.split(":")[0].trim();
|
|
16402
17806
|
return /^[a-z0-9][a-z0-9-]{0,63}$/.test(code) ? code : "error";
|
|
16403
17807
|
}
|
|
17808
|
+
function checkWhy(o) {
|
|
17809
|
+
switch (o.verdict) {
|
|
17810
|
+
case "none":
|
|
17811
|
+
return o.evidence < DEFAULT_THRESHOLDS_V1.fire ? "not-detected" : "exempt";
|
|
17812
|
+
case "overridden":
|
|
17813
|
+
return o.intent === "op-requested" ? "op-requested" : o.intent === "task-step" ? "task-step" : "user-asked";
|
|
17814
|
+
case "deny":
|
|
17815
|
+
return o.escalatedByInjection ? "injection" : "fired";
|
|
17816
|
+
default:
|
|
17817
|
+
if (o.intent === "downgraded-task-step")
|
|
17818
|
+
return "downgraded";
|
|
17819
|
+
return o.targetScanIncomplete ? "scan-incomplete" : "fired";
|
|
17820
|
+
}
|
|
17821
|
+
}
|
|
17822
|
+
function checkPolicy(o) {
|
|
17823
|
+
const packId = o.origin?.packId;
|
|
17824
|
+
if (!packId)
|
|
17825
|
+
return;
|
|
17826
|
+
const at = o.origin?.packVersion ? `@${o.origin.packVersion}` : "";
|
|
17827
|
+
return packId.startsWith("cloud:") ? `cloud/${packId.slice("cloud:".length)}${at}` : `pack/${packId}${at}`;
|
|
17828
|
+
}
|
|
17829
|
+
function checkRecords(outcomes, answers, watched = []) {
|
|
17830
|
+
const all = [...outcomes.map((o) => ({ o, watched: false })), ...watched.map((o) => ({ o, watched: true }))];
|
|
17831
|
+
return all.slice(0, JEV_CHECKS_MAX).map(({ o, watched: isWatched }) => {
|
|
17832
|
+
const probes = {};
|
|
17833
|
+
const prefix = `${o.policy}.`;
|
|
17834
|
+
for (const [id, p] of Object.entries(answers)) {
|
|
17835
|
+
if (Object.keys(probes).length >= JEV_CHECK_PROBES_MAX)
|
|
17836
|
+
break;
|
|
17837
|
+
if (!id.startsWith(prefix))
|
|
17838
|
+
continue;
|
|
17839
|
+
const part = id.slice(prefix.length);
|
|
17840
|
+
if (part === "exempt" || part === "user_asked" || !JEV_PROBE_ID_RE.test(part))
|
|
17841
|
+
continue;
|
|
17842
|
+
probes[part] = round2(p);
|
|
17843
|
+
}
|
|
17844
|
+
const policy = checkPolicy(o);
|
|
17845
|
+
return {
|
|
17846
|
+
check: o.policy,
|
|
17847
|
+
...policy ? { policy } : {},
|
|
17848
|
+
verdict: o.verdict,
|
|
17849
|
+
why: checkWhy(o),
|
|
17850
|
+
p: round2(o.evidence),
|
|
17851
|
+
...o.exempt !== null ? { exempt: round2(o.exempt) } : {},
|
|
17852
|
+
...Object.keys(probes).length > 0 ? { probes } : {},
|
|
17853
|
+
...isWatched ? { watched: true } : {}
|
|
17854
|
+
};
|
|
17855
|
+
});
|
|
17856
|
+
}
|
|
16404
17857
|
function toReview(outcome, cached = false) {
|
|
16405
17858
|
if (outcome.status === "degraded") {
|
|
16406
17859
|
return { kind: "fallback", reason: fallbackCode(outcome.reason), latencyMs: outcome.latencyMs, model: null };
|
|
@@ -16409,23 +17862,40 @@ function toReview(outcome, cached = false) {
|
|
|
16409
17862
|
const latencyMs = sent && !cached ? outcome.latencyMs : null;
|
|
16410
17863
|
const model = sent ? outcome.model : null;
|
|
16411
17864
|
const outcomes = outcome.verdict.outcomes;
|
|
17865
|
+
const cloudSummary = "cloud" in outcome ? outcome.cloud : null;
|
|
17866
|
+
const cloudAsked = cloudSummary ? new Set(cloudSummary.asked) : null;
|
|
17867
|
+
const unvouched = outcomes.some((o) => isCloudOutcome(o) && !(cloudAsked?.has(o.policy) ?? false));
|
|
17868
|
+
if (unvouched)
|
|
17869
|
+
return { kind: "fallback", reason: "malformed", latencyMs, model: null };
|
|
16412
17870
|
const injection = sent ? outcome.verdict.injectionSuspected : null;
|
|
16413
17871
|
const warned = (o) => o.verdict === "instruct" && o.intent !== "downgraded-task-step";
|
|
17872
|
+
const cloudAnswers = cloudSummary?.answers ?? {};
|
|
17873
|
+
const watched = cloudSummary?.observedOutcomes ?? [];
|
|
16414
17874
|
return {
|
|
16415
17875
|
kind: "answered",
|
|
16416
17876
|
decision: outcome.verdict.decision,
|
|
17877
|
+
checks: sent ? checkRecords(outcomes, { ...outcome.answers, ...cloudAnswers }, watched) : [],
|
|
16417
17878
|
reason: outcome.verdict.reason,
|
|
16418
17879
|
policyName: jevPolicyName(outcome),
|
|
16419
17880
|
...jevDecider(outcome)?.origin ? { origin: jevDecider(outcome)?.origin } : {},
|
|
16420
|
-
asked: sent ? outcomes.map(
|
|
16421
|
-
notDenied: sent ? outcomes.filter((o) => o.verdict !== "deny" && !warned(o)).map(
|
|
17881
|
+
asked: sent ? outcomes.map(reviewerKey) : [],
|
|
17882
|
+
notDenied: sent ? outcomes.filter((o) => o.verdict !== "deny" && !warned(o)).map(reviewerKey) : [],
|
|
16422
17883
|
injectionAsked: injection !== null,
|
|
16423
17884
|
injected: injection !== null && injection >= DEFAULT_THRESHOLDS_V1.injection,
|
|
16424
17885
|
unclearableWarned: outcomes.some((o) => o.mode === "deny" && warned(o)),
|
|
16425
17886
|
truncated: outcome.truncated && sent,
|
|
16426
17887
|
requestCut: outcome.requestCut && sent,
|
|
16427
17888
|
latencyMs,
|
|
16428
|
-
model
|
|
17889
|
+
model,
|
|
17890
|
+
...sent && cloudSummary?.observedAsked?.length ? { observedAsked: cloudSummary.observedAsked } : {},
|
|
17891
|
+
...cloudSummary?.observed?.length ? {
|
|
17892
|
+
observedCloud: cloudSummary.observed.map((o) => ({
|
|
17893
|
+
cloudPolicyId: o.cloudPolicyId,
|
|
17894
|
+
cloudVersion: o.cloudVersion,
|
|
17895
|
+
decision: o.decision,
|
|
17896
|
+
reason: o.reason
|
|
17897
|
+
}))
|
|
17898
|
+
} : {}
|
|
16429
17899
|
};
|
|
16430
17900
|
}
|
|
16431
17901
|
function toolInputRecord(raw) {
|
|
@@ -16444,17 +17914,29 @@ function startJevReview(cfg, call) {
|
|
|
16444
17914
|
if (!settled)
|
|
16445
17915
|
controller.abort();
|
|
16446
17916
|
},
|
|
16447
|
-
authorityOf
|
|
17917
|
+
authorityOf,
|
|
17918
|
+
...call.cloud ? { cloud: true } : {}
|
|
16448
17919
|
});
|
|
16449
17920
|
let route;
|
|
16450
17921
|
let throttled;
|
|
16451
17922
|
try {
|
|
16452
17923
|
route = transportForConfig(cfg);
|
|
16453
|
-
|
|
17924
|
+
const scope = call.cloud ? `${throttleScope(cfg, route)}\x00cloud\x00${call.cloud.machineId}\x00${call.cloud.deployment ?? "-"}` : throttleScope(cfg, route);
|
|
17925
|
+
throttled = throttle.throttleTransport(route.transport, { scope });
|
|
16454
17926
|
} catch (err) {
|
|
16455
17927
|
const reason = err instanceof JevError ? err.code : "config";
|
|
16456
17928
|
return handle(Promise.resolve({ kind: "fallback", reason, latencyMs: null, model: null }));
|
|
16457
17929
|
}
|
|
17930
|
+
const cloud = call.cloud;
|
|
17931
|
+
const healthScope = cloud ? JSON.stringify([cfg.baseUrl ?? null, cloud.machineId]) : "";
|
|
17932
|
+
let probe = false;
|
|
17933
|
+
if (cloud) {
|
|
17934
|
+
const gate = cloudJevGate(healthScope);
|
|
17935
|
+
if (!gate.ask) {
|
|
17936
|
+
return handle(Promise.resolve({ kind: "fallback", reason: BREAKER_SKIP_REASON, latencyMs: null, model: null }));
|
|
17937
|
+
}
|
|
17938
|
+
probe = gate.probe;
|
|
17939
|
+
}
|
|
16458
17940
|
let intent;
|
|
16459
17941
|
try {
|
|
16460
17942
|
intent = intentReader.readIntent(call.sessionId);
|
|
@@ -16473,6 +17955,7 @@ function startJevReview(cfg, call) {
|
|
|
16473
17955
|
agentLastMessage: intent.agentLastMessage
|
|
16474
17956
|
};
|
|
16475
17957
|
let cached = false;
|
|
17958
|
+
let reachedCloud = false;
|
|
16476
17959
|
const transport = async (request, signal) => {
|
|
16477
17960
|
const response = await throttled(request, signal);
|
|
16478
17961
|
cached = servedFromCache2(response);
|
|
@@ -16480,7 +17963,7 @@ function startJevReview(cfg, call) {
|
|
|
16480
17963
|
};
|
|
16481
17964
|
const timeoutMs = resolveTimeout(cfg);
|
|
16482
17965
|
let abandoned = false;
|
|
16483
|
-
const
|
|
17966
|
+
const options = {
|
|
16484
17967
|
transport,
|
|
16485
17968
|
cli: call.cli,
|
|
16486
17969
|
via: route.via,
|
|
@@ -16489,9 +17972,26 @@ function startJevReview(cfg, call) {
|
|
|
16489
17972
|
intent: "v1",
|
|
16490
17973
|
v1: { thresholds: DEFAULT_THRESHOLDS_V1 },
|
|
16491
17974
|
signal: controller.signal
|
|
16492
|
-
}
|
|
17975
|
+
};
|
|
17976
|
+
const evaluated = cloud ? evaluateCloudSemantic(input, {
|
|
17977
|
+
...options,
|
|
17978
|
+
machineId: cloud.machineId,
|
|
17979
|
+
agent: cloud.agent,
|
|
17980
|
+
observedPolicies: cloud.observedPolicies ?? [],
|
|
17981
|
+
...call.localPaused ? { policies: [] } : {}
|
|
17982
|
+
}) : evaluateSemantic(input, options);
|
|
17983
|
+
const answered = evaluated.then((outcome) => {
|
|
16493
17984
|
const hit = cached && outcome.status === "ok";
|
|
17985
|
+
reachedCloud = outcome.status === "ok" && !hit && outcome.via !== "none";
|
|
16494
17986
|
const review = toReview(outcome, hit);
|
|
17987
|
+
if (cloud && !abandoned && outcome.status === "ok" && "cloud" in outcome && outcome.cloud.droppedLocal.length > 0) {
|
|
17988
|
+
const cloudNames = new Set([...outcome.cloud.asked, ...outcome.cloud.droppedCloud]);
|
|
17989
|
+
recordJevBudgetDrops({ machineId: cloud.machineId, deployment: cloud.deployment, jevMode: cloud.mode }, outcome.cloud.droppedLocal.map((d) => ({
|
|
17990
|
+
packId: d.packId ?? "unknown",
|
|
17991
|
+
name: d.name,
|
|
17992
|
+
...cloudNames.has(d.name) ? { clash: true } : {}
|
|
17993
|
+
})));
|
|
17994
|
+
}
|
|
16495
17995
|
if (!(outcome.status === "degraded" && outcome.reason === "aborted")) {
|
|
16496
17996
|
const row = verdictLogRow(input, hit ? { ...outcome, inputTokens: null } : outcome, {
|
|
16497
17997
|
sessionId: call.sessionId,
|
|
@@ -16499,7 +17999,8 @@ function startJevReview(cfg, call) {
|
|
|
16499
17999
|
eventType: call.eventType,
|
|
16500
18000
|
applied: abandoned || review.kind !== "answered" ? "legacy-fallback" : mode === "observe" ? "observe" : "two-tier"
|
|
16501
18001
|
});
|
|
16502
|
-
|
|
18002
|
+
const withCloud = outcome.status === "ok" && "cloud" in outcome ? { ...row, cloud: outcome.cloud } : row;
|
|
18003
|
+
appendVerdictLog(hit ? { ...withCloud, cached: true } : withCloud);
|
|
16503
18004
|
}
|
|
16504
18005
|
return review;
|
|
16505
18006
|
}).catch(() => ({ kind: "fallback", reason: "error", latencyMs: null, model: null }));
|
|
@@ -16511,22 +18012,34 @@ function startJevReview(cfg, call) {
|
|
|
16511
18012
|
resolve({ kind: "fallback", reason: "timeout", latencyMs: timeoutMs + JEV_DEADLINE_GRACE_MS, model: null });
|
|
16512
18013
|
}, timeoutMs + JEV_DEADLINE_GRACE_MS);
|
|
16513
18014
|
});
|
|
16514
|
-
|
|
18015
|
+
const raced = Promise.race([answered, expired]).finally(() => {
|
|
16515
18016
|
if (deadline !== undefined)
|
|
16516
18017
|
clearTimeout(deadline);
|
|
18018
|
+
});
|
|
18019
|
+
if (!cloud)
|
|
18020
|
+
return handle(raced);
|
|
18021
|
+
return handle(raced.then((review) => {
|
|
18022
|
+
recordCloudJevResult(healthScope, classifyCloudJevReview(review, { reachedCloud }), review.kind === "fallback" ? review.reason : null, probe);
|
|
18023
|
+
reportCloudJevHealth();
|
|
18024
|
+
return review;
|
|
16517
18025
|
}));
|
|
16518
18026
|
}
|
|
16519
18027
|
var throttle, intentReader, ALWAYS_ON, MIN_JEV_TIMEOUT_MS2 = 100, MAX_JEV_TIMEOUT_MS2 = 1e4, JEV_DEADLINE_GRACE_MS = 250;
|
|
16520
18028
|
var init_jev_review = __esm(() => {
|
|
16521
18029
|
init_builtin_policies();
|
|
18030
|
+
init_cloud_managed_policies();
|
|
18031
|
+
init_cloud_policy_errors();
|
|
16522
18032
|
init_policy_registry();
|
|
16523
18033
|
init_decide();
|
|
18034
|
+
init_cloud_jev();
|
|
18035
|
+
init_cloud_jev_health();
|
|
16524
18036
|
init_evaluator();
|
|
16525
18037
|
init_intent();
|
|
16526
18038
|
init_jev_client();
|
|
16527
18039
|
init_jev_config();
|
|
16528
18040
|
init_jev_throttle();
|
|
16529
18041
|
init_session_root();
|
|
18042
|
+
init_jev_activity();
|
|
16530
18043
|
throttle = exports_jev_throttle;
|
|
16531
18044
|
intentReader = exports_intent;
|
|
16532
18045
|
ALWAYS_ON = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => normalizePolicyName(p.name)));
|
|
@@ -16669,7 +18182,7 @@ var init_enforcement_capability = __esm(() => {
|
|
|
16669
18182
|
});
|
|
16670
18183
|
|
|
16671
18184
|
// src/hooks/handler.ts
|
|
16672
|
-
import { existsSync as
|
|
18185
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
16673
18186
|
function canonicalizeEventType(raw, cli) {
|
|
16674
18187
|
if (cli === "codex") {
|
|
16675
18188
|
const mapped = CODEX_EVENT_MAP[raw];
|
|
@@ -16727,9 +18240,29 @@ function isHumanAuthoredGate(rawEventType, cli) {
|
|
|
16727
18240
|
function jevForcedOff(opts) {
|
|
16728
18241
|
return process.env.FAILPROOFAI_EVALUATOR === "legacy" || !!opts?.forceDecision;
|
|
16729
18242
|
}
|
|
16730
|
-
async function readJevConfig() {
|
|
18243
|
+
async function readJevConfig(agent) {
|
|
16731
18244
|
try {
|
|
16732
|
-
|
|
18245
|
+
const cloud = readCloudJevState();
|
|
18246
|
+
if (cloud.jevMode === "off")
|
|
18247
|
+
return null;
|
|
18248
|
+
if (cloudJevAsks(cloud.jevMode)) {
|
|
18249
|
+
await Promise.resolve().then(() => init_jev_config());
|
|
18250
|
+
const resolved = loadJevConfigForCloudMode(cloud.jevMode);
|
|
18251
|
+
if (!resolved.config || !resolved.machineId)
|
|
18252
|
+
return null;
|
|
18253
|
+
return {
|
|
18254
|
+
config: resolved.config,
|
|
18255
|
+
defaultMode: DEFAULT_JEV_MODE,
|
|
18256
|
+
cloud: {
|
|
18257
|
+
machineId: resolved.machineId,
|
|
18258
|
+
deployment: cloud.deployment,
|
|
18259
|
+
mode: cloud.jevMode,
|
|
18260
|
+
agent: agent ?? null,
|
|
18261
|
+
observedPolicies: (cloudJevAssignmentsFor(agent ?? null) ?? []).filter((a) => a.effect === "observe").map((a) => a.id)
|
|
18262
|
+
}
|
|
18263
|
+
};
|
|
18264
|
+
}
|
|
18265
|
+
if (!existsSync22(jevConfigFile()))
|
|
16733
18266
|
return null;
|
|
16734
18267
|
await Promise.resolve().then(() => init_jev_config());
|
|
16735
18268
|
const config = loadJevConfig();
|
|
@@ -16739,20 +18272,74 @@ async function readJevConfig() {
|
|
|
16739
18272
|
return null;
|
|
16740
18273
|
}
|
|
16741
18274
|
}
|
|
16742
|
-
|
|
18275
|
+
function jevAttribution(origin) {
|
|
18276
|
+
if (!origin)
|
|
18277
|
+
return {};
|
|
18278
|
+
if (origin.packId.startsWith("cloud:")) {
|
|
18279
|
+
const version = Number(origin.packVersion);
|
|
18280
|
+
return {
|
|
18281
|
+
cloudPolicyId: origin.packId.slice("cloud:".length),
|
|
18282
|
+
...Number.isSafeInteger(version) ? { cloudVersion: version } : {}
|
|
18283
|
+
};
|
|
18284
|
+
}
|
|
18285
|
+
return origin;
|
|
18286
|
+
}
|
|
18287
|
+
async function recordCloudPolicyErrors(input) {
|
|
18288
|
+
try {
|
|
18289
|
+
const jev = readCloudJevState();
|
|
18290
|
+
const managed = input.manifestError !== null || input.jsPolicies.length > 0 || jev.jevMode !== null || jev.errors.length > 0 || input.agentScopeUnresolved === true;
|
|
18291
|
+
if (!managed && !existsSync22(cloudPolicyErrorsPath()))
|
|
18292
|
+
return;
|
|
18293
|
+
let jevProblem = null;
|
|
18294
|
+
let budgetDrops = [];
|
|
18295
|
+
let health = [];
|
|
18296
|
+
if (cloudJevAsks(jev.jevMode)) {
|
|
18297
|
+
await Promise.resolve().then(() => init_jev_config());
|
|
18298
|
+
const resolved = loadJevConfigForCloudMode(jev.jevMode);
|
|
18299
|
+
jevProblem = resolved.problem;
|
|
18300
|
+
if (resolved.machineId) {
|
|
18301
|
+
budgetDrops = existsSync22(jevBudgetPath()) ? readJevBudgetDrops({ machineId: resolved.machineId, deployment: jev.deployment, jevMode: jev.jevMode }, installedPackIds()) : [];
|
|
18302
|
+
}
|
|
18303
|
+
await Promise.resolve().then(() => init_cloud_jev_health());
|
|
18304
|
+
health = cloudJevHealthErrors();
|
|
18305
|
+
}
|
|
18306
|
+
writeCloudPolicyErrors(collectCloudPolicyErrors({
|
|
18307
|
+
manifestError: input.manifestError,
|
|
18308
|
+
jsPolicies: input.jsPolicies,
|
|
18309
|
+
jsFailures: input.jsFailures,
|
|
18310
|
+
jev,
|
|
18311
|
+
jevProblem,
|
|
18312
|
+
budgetDrops,
|
|
18313
|
+
health,
|
|
18314
|
+
agentScopeUnresolved: input.agentScopeUnresolved
|
|
18315
|
+
}));
|
|
18316
|
+
if (input.jsPolicies.length > 0)
|
|
18317
|
+
recordCloudLoadChecked(input.jsPolicies[0].deployment, input.jsPolicies);
|
|
18318
|
+
} catch (err) {
|
|
18319
|
+
hookLogInfo(`could not record Cloud policy errors: ${err instanceof Error ? err.message : String(err)}`);
|
|
18320
|
+
}
|
|
18321
|
+
}
|
|
18322
|
+
async function startTwoTier(canonicalEventType, parsed, session, cli, opts, activePause, agent) {
|
|
16743
18323
|
if (!JEV_GATE_EVENTS.has(canonicalEventType))
|
|
16744
18324
|
return null;
|
|
16745
18325
|
if (isHumanAuthoredGate(session.rawHookEventName, cli))
|
|
16746
18326
|
return null;
|
|
16747
18327
|
if (typeof parsed.tool_name !== "string" || parsed.tool_name.length === 0)
|
|
16748
18328
|
return null;
|
|
16749
|
-
if (jevForcedOff(opts)
|
|
18329
|
+
if (jevForcedOff(opts))
|
|
18330
|
+
return null;
|
|
18331
|
+
const cloudAsks = cloudJevAsks(readCloudJevMode());
|
|
18332
|
+
if (activePause && !cloudAsks)
|
|
16750
18333
|
return null;
|
|
16751
|
-
if (!jevChecksInstalled())
|
|
18334
|
+
if (!cloudAsks && !jevChecksInstalled())
|
|
16752
18335
|
return null;
|
|
16753
|
-
|
|
18336
|
+
if (cloudAsks && cloudJevAssignmentsFor(agent)?.length === 0 && (activePause || !packJevChecksInstalled()))
|
|
18337
|
+
return null;
|
|
18338
|
+
const loaded = await readJevConfig(agent);
|
|
16754
18339
|
if (!loaded)
|
|
16755
18340
|
return null;
|
|
18341
|
+
if (activePause && !loaded.cloud)
|
|
18342
|
+
return null;
|
|
16756
18343
|
const cfg = loaded.config;
|
|
16757
18344
|
try {
|
|
16758
18345
|
await Promise.resolve().then(() => init_jev_review());
|
|
@@ -16763,7 +18350,9 @@ async function startTwoTier(canonicalEventType, parsed, session, cli, opts, acti
|
|
|
16763
18350
|
cwd: session.cwd,
|
|
16764
18351
|
permissionMode: session.permissionMode,
|
|
16765
18352
|
sessionId: session.sessionId,
|
|
16766
|
-
cli
|
|
18353
|
+
cli,
|
|
18354
|
+
...loaded.cloud ? { cloud: loaded.cloud } : {},
|
|
18355
|
+
...activePause ? { localPaused: true } : {}
|
|
16767
18356
|
});
|
|
16768
18357
|
} catch (err) {
|
|
16769
18358
|
hookLogInfo(`Jev review could not start (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -16792,7 +18381,7 @@ async function captureJevIntent(canonicalEventType, decision, parsed, session, c
|
|
|
16792
18381
|
if (canonicalEventType !== "UserPromptSubmit" || jevForcedOff(opts))
|
|
16793
18382
|
return;
|
|
16794
18383
|
try {
|
|
16795
|
-
if (!jevChecksInstalled())
|
|
18384
|
+
if (!cloudJevAsks(readCloudJevMode()) && !jevChecksInstalled())
|
|
16796
18385
|
return;
|
|
16797
18386
|
if (!await readJevConfig())
|
|
16798
18387
|
return;
|
|
@@ -16834,6 +18423,9 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16834
18423
|
});
|
|
16835
18424
|
}
|
|
16836
18425
|
}
|
|
18426
|
+
if (isHealthProbe(eventType, parsed)) {
|
|
18427
|
+
return { exitCode: 0, stdout: "", stderr: "" };
|
|
18428
|
+
}
|
|
16837
18429
|
normalizeCliPayload(cli, parsed);
|
|
16838
18430
|
const canonicalEventType = canonicalizeEventType(eventType, cli);
|
|
16839
18431
|
const rawToolName = parsed.tool_name;
|
|
@@ -16857,6 +18449,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16857
18449
|
rawHookEventName: eventType,
|
|
16858
18450
|
cli
|
|
16859
18451
|
};
|
|
18452
|
+
const runtimeAgent = readRuntimeAgentIdentity(cli, opts?.agentSettingsPath ?? runtimeAgentSettingsPath(cli, session.cwd));
|
|
16860
18453
|
let config;
|
|
16861
18454
|
let customHooksList = [];
|
|
16862
18455
|
let conventionHookNames = new Set;
|
|
@@ -16873,7 +18466,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16873
18466
|
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 }), {});
|
|
16874
18467
|
} else {
|
|
16875
18468
|
config = readMergedHooksConfig(session.cwd);
|
|
16876
|
-
clearPolicies(cli);
|
|
18469
|
+
clearPolicies(cli, runtimeAgent);
|
|
16877
18470
|
activePause = readActivePause(session.sessionId);
|
|
16878
18471
|
const packsInstalledHere = hasInstalledRegexPacks();
|
|
16879
18472
|
const legacyNames = activePause || packsInstalledHere ? [] : config.enabledPolicies;
|
|
@@ -16883,17 +18476,19 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16883
18476
|
}
|
|
16884
18477
|
const enabledBuiltinNames = new Set(legacyNames);
|
|
16885
18478
|
let cloudManagedPolicies = [];
|
|
18479
|
+
let cloudManifestError = null;
|
|
16886
18480
|
try {
|
|
16887
|
-
cloudManagedPolicies = readActiveCloudManagedPolicies();
|
|
18481
|
+
cloudManagedPolicies = readActiveCloudManagedPolicies(runtimeAgent);
|
|
16888
18482
|
} catch (err) {
|
|
16889
18483
|
const msg = err instanceof Error ? err.message : String(err);
|
|
18484
|
+
cloudManifestError = msg;
|
|
16890
18485
|
hookLogWarn(`[failproofai] cloud-managed policies could NOT be loaded and are not being enforced: ${msg}. Local policies are unaffected. Run \`failproofai config --status\` to check this machine's enrolment.`);
|
|
16891
18486
|
trackHookEvent(getInstanceId(), "cloud_managed_load_error", {
|
|
16892
18487
|
event_type: eventType,
|
|
16893
18488
|
cli
|
|
16894
18489
|
});
|
|
16895
18490
|
}
|
|
16896
|
-
cloudDeployment = cloudManagedPolicies[0]?.deployment;
|
|
18491
|
+
cloudDeployment = cloudManagedPolicies[0]?.deployment ?? readCloudJevState().deployment ?? undefined;
|
|
16897
18492
|
let installedPacks = [];
|
|
16898
18493
|
try {
|
|
16899
18494
|
const packResult = readInstalledPacks();
|
|
@@ -16911,10 +18506,12 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16911
18506
|
...cloudManagedPolicies.map((policy) => policy.path),
|
|
16912
18507
|
...installedPacks.map((pack) => pack.path)
|
|
16913
18508
|
];
|
|
18509
|
+
const cloudJevMode = readCloudJevMode();
|
|
18510
|
+
const cloudForLoading = cloudManagedPolicies.map((policy) => withCloudAuthority(policy, cloudJevMode));
|
|
16914
18511
|
const loadResult = await loadAllCustomHooks(allExplicitPaths, {
|
|
16915
18512
|
sessionCwd: session.cwd,
|
|
16916
18513
|
customPoliciesEnabled: config.customPoliciesEnabled,
|
|
16917
|
-
...
|
|
18514
|
+
...cloudForLoading.length > 0 ? { cloudManagedPolicies: cloudForLoading } : {},
|
|
16918
18515
|
...installedPacks.length > 0 ? { packs: installedPacks } : {}
|
|
16919
18516
|
});
|
|
16920
18517
|
customHooksList = loadResult.hooks;
|
|
@@ -16994,6 +18591,12 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
16994
18591
|
warnAuthority(refusedAuthorityWarning(registeredName, refused));
|
|
16995
18592
|
registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1, pack?.policies.find((p) => p.name === hook.name)?.params, authority);
|
|
16996
18593
|
}
|
|
18594
|
+
await recordCloudPolicyErrors({
|
|
18595
|
+
manifestError: cloudManifestError,
|
|
18596
|
+
jsPolicies: cloudManagedPolicies,
|
|
18597
|
+
jsFailures: loadResult.cloudFailures,
|
|
18598
|
+
agentScopeUnresolved: runtimeAgent === null && readCloudAgentScopeRequired()
|
|
18599
|
+
});
|
|
16997
18600
|
if (!activePause) {
|
|
16998
18601
|
const guards = missingGuards({
|
|
16999
18602
|
errors: packErrors,
|
|
@@ -17040,7 +18643,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
17040
18643
|
}
|
|
17041
18644
|
hookLogInfo(`event=${canonicalEventType} cli=${cli} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
|
|
17042
18645
|
}
|
|
17043
|
-
const twoTier = await startTwoTier(canonicalEventType, parsed, session, cli, opts, activePause);
|
|
18646
|
+
const twoTier = await startTwoTier(canonicalEventType, parsed, session, cli, opts, activePause, runtimeAgent);
|
|
17044
18647
|
const matchedBeforeRelease = twoTier ? getPoliciesForEvent(canonicalEventType, parsed.tool_name).map((p) => p.name) : null;
|
|
17045
18648
|
const result = twoTier ? await evaluatePolicies(canonicalEventType, parsed, session, config, {
|
|
17046
18649
|
...twoTier,
|
|
@@ -17055,6 +18658,9 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
17055
18658
|
reason: observeVerdict.reason
|
|
17056
18659
|
});
|
|
17057
18660
|
}
|
|
18661
|
+
for (const o of result.twoTier?.observedCloud ?? []) {
|
|
18662
|
+
observedResults.push({ policyId: o.cloudPolicyId, version: o.cloudVersion, decision: o.decision, reason: o.reason });
|
|
18663
|
+
}
|
|
17058
18664
|
const durationMs = Math.round(performance.now() - startTime);
|
|
17059
18665
|
hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
|
|
17060
18666
|
await captureJevIntent(canonicalEventType, result.decision, parsed, session, cli, opts);
|
|
@@ -17077,7 +18683,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
17077
18683
|
cwd: session.cwd,
|
|
17078
18684
|
permissionMode: session.permissionMode,
|
|
17079
18685
|
hookEventName: session.hookEventName,
|
|
17080
|
-
...result.policyName && result.twoTier?.decidedByJev ? { policySource: "jev", ...jevOrigin } : {},
|
|
18686
|
+
...result.policyName && result.twoTier?.decidedByJev ? { policySource: "jev", ...jevAttribution(jevOrigin) } : {},
|
|
17081
18687
|
...result.policyName && !result.twoTier?.decidedByJev ? (() => {
|
|
17082
18688
|
const attribution = policyAttribution.get(result.policyName);
|
|
17083
18689
|
return {
|
|
@@ -17088,6 +18694,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
17088
18694
|
...attribution?.packVersion ? { packVersion: attribution.packVersion } : {}
|
|
17089
18695
|
};
|
|
17090
18696
|
})() : {},
|
|
18697
|
+
...!result.policyName && observeVerdict ? { policyName: observeVerdict.policyName, ...jevAttribution(result.twoTier?.jevOrigin) } : {},
|
|
17091
18698
|
...cloudDeployment !== undefined ? { cloudDeployment } : {},
|
|
17092
18699
|
...observedResults.length > 0 ? { observed: observedResults } : {},
|
|
17093
18700
|
...activePause ? { pausedBy: activePause.setBy, pauseExpiresAt: activePause.expiresAt } : {}
|
|
@@ -17163,23 +18770,25 @@ var init_handler = __esm(() => {
|
|
|
17163
18770
|
init_telemetry_id();
|
|
17164
18771
|
init_hook_logger();
|
|
17165
18772
|
init_cloud_managed_policies();
|
|
18773
|
+
init_cloud_policy_errors();
|
|
17166
18774
|
init_pack_manifest();
|
|
17167
18775
|
init_pack_failclosed();
|
|
17168
18776
|
init_session_pause();
|
|
17169
18777
|
init_fp_home();
|
|
17170
18778
|
init_fp_reset();
|
|
17171
18779
|
init_untraced_note();
|
|
18780
|
+
init_agent_roster();
|
|
17172
18781
|
JEV_GATE_EVENTS = new Set(["PreToolUse", "PermissionRequest"]);
|
|
17173
18782
|
});
|
|
17174
18783
|
|
|
17175
18784
|
// src/hooks/worker-server.ts
|
|
17176
18785
|
import { createConnection, createServer } from "node:net";
|
|
17177
|
-
import { existsSync as
|
|
18786
|
+
import { existsSync as existsSync23, unlinkSync as unlinkSync4 } from "node:fs";
|
|
17178
18787
|
function isWorkerHookRequest(msg) {
|
|
17179
18788
|
if (!msg || typeof msg !== "object")
|
|
17180
18789
|
return false;
|
|
17181
18790
|
const m = msg;
|
|
17182
|
-
return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || m.cwd === null || typeof m.cwd === "string");
|
|
18791
|
+
return m.type === "hook" && typeof m.hookEvent === "string" && typeof m.cli === "string" && typeof m.stdin === "string" && (m.cwd === undefined || m.cwd === null || typeof m.cwd === "string") && (m.agentSettingsPath === undefined || m.agentSettingsPath === null || typeof m.agentSettingsPath === "string");
|
|
17183
18792
|
}
|
|
17184
18793
|
function encodeFrame(value) {
|
|
17185
18794
|
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
@@ -17274,6 +18883,7 @@ function handleConnection(socket, shutdown) {
|
|
|
17274
18883
|
const result = await evaluateHookEvent(request.hookEvent, request.cli, request.stdin, {
|
|
17275
18884
|
awaitTelemetryFlush: false,
|
|
17276
18885
|
fallbackCwd: request.cwd ?? undefined,
|
|
18886
|
+
agentSettingsPath: request.agentSettingsPath ?? undefined,
|
|
17277
18887
|
releaseRegistry: release
|
|
17278
18888
|
});
|
|
17279
18889
|
deliver(encodeFrame({
|
|
@@ -17340,14 +18950,14 @@ function requestExistingWorkerShutdown(socketPath) {
|
|
|
17340
18950
|
function startWorkerServer(socketPath, shutdown) {
|
|
17341
18951
|
const server = createServer((socket) => handleConnection(socket, shutdown ?? (() => server.close())));
|
|
17342
18952
|
const listen = () => {
|
|
17343
|
-
if (
|
|
18953
|
+
if (existsSync23(socketPath)) {
|
|
17344
18954
|
try {
|
|
17345
18955
|
unlinkSync4(socketPath);
|
|
17346
18956
|
} catch {}
|
|
17347
18957
|
}
|
|
17348
18958
|
server.listen(socketPath);
|
|
17349
18959
|
};
|
|
17350
|
-
if (!
|
|
18960
|
+
if (!existsSync23(socketPath))
|
|
17351
18961
|
listen();
|
|
17352
18962
|
else
|
|
17353
18963
|
requestExistingWorkerShutdown(socketPath).then(listen, (err) => server.emit("error", err));
|
|
@@ -17363,13 +18973,13 @@ var init_worker_server = __esm(() => {
|
|
|
17363
18973
|
|
|
17364
18974
|
// bin/failproofai-worker.mjs
|
|
17365
18975
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
17366
|
-
import { dirname as
|
|
18976
|
+
import { dirname as dirname11, resolve as resolve20 } from "node:path";
|
|
17367
18977
|
import { fileURLToPath } from "node:url";
|
|
17368
18978
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
17369
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
18979
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve20(dirname11(realpathSync2(fileURLToPath(import.meta.url))), "..");
|
|
17370
18980
|
}
|
|
17371
18981
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
17372
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
18982
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve20(dirname11(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
|
|
17373
18983
|
}
|
|
17374
18984
|
var socketPath = process.env.FAILPROOFAI_WORKER_SOCKET;
|
|
17375
18985
|
if (!socketPath) {
|