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.
Files changed (214) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/audit/run/route.js +22 -22
  23. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  31. package/.next/standalone/.next/server/app/audit/page.js +1 -1
  32. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/index.html +1 -1
  35. package/.next/standalone/.next/server/app/index.rsc +3 -3
  36. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  37. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  38. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  39. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  43. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  44. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  50. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  54. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  57. package/.next/standalone/.next/server/app/settings/page.js +7 -7
  58. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/chunks/{[externals]__0-ha5ydqq0mws._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
  61. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20xe05hm5u8_1._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
  62. package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/{_0kc-piux3hady._.js → _1y590mjzpkbos._.js} +2 -2
  68. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
  70. package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ts0fg5yih1c1._.js → [root-of-the-server]__00l2u6j86cb8y._.js} +2 -2
  75. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
  79. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pwj2h2efe5f-._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/ssr/_0bseae9u7g98r._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/ssr/_0mouhaflye1ro._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _0ouu886szh6yj._.js} +10 -9
  91. package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/ssr/{_1ve8y151ua7qx._.js → _1fw0_nzkae5ao._.js} +2 -2
  94. package/.next/standalone/.next/server/chunks/ssr/_20hn4gn87nsjc._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
  96. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr0vt64h._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
  102. 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
  103. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0janj0i6dqko7._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
  107. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
  108. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
  110. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
  112. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  113. package/.next/standalone/.next/server/pages/404.html +1 -1
  114. package/.next/standalone/.next/server/pages/500.html +1 -1
  115. package/.next/standalone/.next/server/preview-props.json +3 -3
  116. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  118. package/.next/standalone/.next/static/chunks/{0pjxnp927y7_j.js → 0ay__-d2h1xb5.js} +1 -1
  119. package/.next/standalone/.next/static/chunks/{2znt1784j9gd9.js → 0tbm3vjm62h-2.js} +1 -1
  120. package/.next/standalone/.next/static/chunks/1c_jf6g5wnslk.js +1 -0
  121. package/.next/standalone/.next/static/chunks/{3tn1bzk094e8e.js → 1my3-5p47t7r-.js} +1 -1
  122. package/.next/standalone/.next/static/chunks/20py8k1gd_i1m.js +1 -0
  123. package/.next/standalone/.next/static/chunks/21hckpmsc4g70.js +6 -0
  124. package/.next/standalone/.next/static/chunks/2hmex93ppj1ke.js +1 -0
  125. package/.next/standalone/.next/static/chunks/2o6d37j425uci.js +1 -0
  126. package/.next/standalone/.next/static/chunks/40oworn_-x12p.js +1 -0
  127. package/.next/standalone/.next/static/chunks/{2qz5piiir-qqm.js → 42xptz88mtq3x.js} +1 -1
  128. package/.next/standalone/app/actions/update-jev-config.ts +5 -3
  129. package/.next/standalone/app/settings/jev-panel.tsx +1 -1
  130. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +32 -0
  131. package/.next/standalone/fp-cloud-cli/README.md +40 -1
  132. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +91 -12
  133. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +78 -16
  134. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
  135. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +124 -6
  136. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
  137. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -20
  138. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
  139. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
  140. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +14 -7
  141. package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +527 -0
  142. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
  143. package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
  144. package/.next/standalone/hermes-plugin/README.md +6 -4
  145. package/.next/standalone/hermes-plugin/__init__.py +27 -0
  146. package/.next/standalone/hermes-plugin/client.py +5 -1
  147. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  148. package/.next/standalone/package.json +11 -10
  149. package/.next/standalone/sdk/python/uv.lock +31 -8
  150. package/.next/standalone/server.js +1 -1
  151. package/bin/failproofai.mjs +17 -1
  152. package/dist/cli.mjs +3507 -1250
  153. package/dist/worker.mjs +1779 -169
  154. package/hermes-plugin/README.md +6 -4
  155. package/hermes-plugin/__init__.py +27 -0
  156. package/hermes-plugin/client.py +5 -1
  157. package/hermes-plugin/plugin.yaml +1 -1
  158. package/package.json +11 -10
  159. package/src/audit/open-browser.ts +6 -7
  160. package/src/hooks/agent-roster.ts +180 -0
  161. package/src/hooks/agent-skill.ts +203 -0
  162. package/src/hooks/agent-targets.ts +60 -0
  163. package/src/hooks/cloud-connection.ts +91 -45
  164. package/src/hooks/cloud-enrollment-cli.ts +7 -4
  165. package/src/hooks/cloud-login.ts +6 -3
  166. package/src/hooks/cloud-managed-policies.ts +430 -20
  167. package/src/hooks/cloud-policy-errors.ts +481 -0
  168. package/src/hooks/codex-system-hooks.ts +10 -4
  169. package/src/hooks/config-status.ts +10 -4
  170. package/src/hooks/configure-wizard.ts +54 -26
  171. package/src/hooks/custom-hooks-loader.ts +26 -3
  172. package/src/hooks/daemon-client.ts +9 -4
  173. package/src/hooks/daemon-service.ts +2 -1
  174. package/src/hooks/delivery-health.ts +47 -2
  175. package/src/hooks/effective-reviewers.ts +36 -1
  176. package/src/hooks/fp-config.ts +19 -0
  177. package/src/hooks/fp-home.ts +6 -0
  178. package/src/hooks/handler.ts +255 -19
  179. package/src/hooks/health-probe.ts +15 -0
  180. package/src/hooks/hook-activity-store.ts +7 -1
  181. package/src/hooks/integrations.ts +36 -14
  182. package/src/hooks/jev-activity.ts +117 -1
  183. package/src/hooks/jev-cli.ts +196 -1
  184. package/src/hooks/jev-cloud-connection.ts +92 -0
  185. package/src/hooks/manager.ts +174 -34
  186. package/src/hooks/pack-failclosed.ts +2 -0
  187. package/src/hooks/pack-manifest.ts +8 -1
  188. package/src/hooks/policy-evaluator.ts +22 -1
  189. package/src/hooks/policy-registry.ts +3 -2
  190. package/src/hooks/policy-reviewability.ts +23 -5
  191. package/src/hooks/semantic/cloud-jev-health.ts +227 -0
  192. package/src/hooks/semantic/cloud-jev.ts +868 -0
  193. package/src/hooks/semantic/combine.ts +26 -1
  194. package/src/hooks/semantic/jev-client.ts +19 -2
  195. package/src/hooks/semantic/jev-config.ts +179 -30
  196. package/src/hooks/semantic/jev-review.ts +217 -10
  197. package/src/hooks/semantic/pack-policies.ts +2 -1
  198. package/src/hooks/semantic/types.ts +8 -0
  199. package/src/hooks/session-pause-cli.ts +1 -1
  200. package/src/hooks/tui.ts +13 -4
  201. package/src/hooks/uninstall-cli.ts +12 -1
  202. package/src/hooks/worker-server.ts +5 -1
  203. package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
  204. package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
  205. package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
  206. package/.next/standalone/.next/static/chunks/0a-ccsp3xmypz.js +0 -1
  207. package/.next/standalone/.next/static/chunks/13jv-xvabeb62.js +0 -6
  208. package/.next/standalone/.next/static/chunks/2ank2u3apy8m6.js +0 -1
  209. package/.next/standalone/.next/static/chunks/2meqb9kygrncb.js +0 -1
  210. package/.next/standalone/.next/static/chunks/2ohktp_-lkv3x.js +0 -1
  211. package/.next/standalone/.next/static/chunks/2py0eldhwz8ay.js +0 -1
  212. /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_buildManifest.js +0 -0
  213. /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → LHCtFxVG0mdvd4lsXAOFC}/_clientMiddlewareManifest.js +0 -0
  214. /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.0";
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 readActiveCloudManagedPolicies() {
1342
- const root = cloudManagedPolicyRoot();
1343
- const activePath = resolve3(root, "active.json");
1344
- if (!existsSync4(activePath))
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 = JSON.parse(readFileSync2(activePath, "utf8"));
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 manifest.policies.map((policy) => {
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 statSync3 } from "node:fs";
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 statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
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 statSync4,
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 ((statSync4(tmp).mode & 63) !== 0)
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 ((statSync4(filePath).mode & 63) !== 0)
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 statSync5,
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 = statSync5(dirname3(path)).mode & 511;
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|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : /timed out loading module/i.test(msg) ? "load_timeout" : "runtime_error";
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 statSync6,
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 = statSync6(lockPath);
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 statSync7 } from "node:fs";
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 fileVersion(path) {
7184
- const stat = statSync7(path);
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 = fileVersion(dbPath);
7611
+ const before = fileVersion2(dbPath);
7272
7612
  const main = readFileSync6(dbPath);
7273
- const afterMain = fileVersion(dbPath);
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 = fileVersion(dbPath);
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 statSync8 } from "node:fs";
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 = statSync8(transcriptPath);
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 statSync9 } from "node:fs";
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 = statSync9(transcriptPath);
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 statSync10 } from "node:fs";
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 = statSync10(transcriptPath);
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 (statSync10(transcriptPath).isFile())
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 statSync11 } from "node:fs";
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 statSync12 } from "node:fs";
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 statSync13 } from "node:fs";
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 statSync14 } from "node:fs";
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 existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync11, readFileSync as readFileSync13, rmSync as rmSync3 } from "node:fs";
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 resolve13 } from "node:path";
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 resolve13(override, "sessions");
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 (!existsSync17(path))
12071
+ if (!existsSync18(path))
11499
12072
  return null;
11500
- const stored = parseStored(JSON.parse(readFileSync13(path, "utf8")));
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 existsSync18,
12118
+ existsSync as existsSync19,
11540
12119
  mkdirSync as mkdirSync8,
11541
- readFileSync as readFileSync14,
12120
+ readFileSync as readFileSync15,
11542
12121
  readdirSync as readdirSync12,
11543
- renameSync as renameSync4,
11544
- rmSync as rmSync4,
12122
+ renameSync as renameSync5,
12123
+ rmSync as rmSync5,
11545
12124
  rmdirSync,
11546
- statSync as statSync15,
11547
- writeFileSync as writeFileSync6
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 (existsSync18(globalPolicyConfigFile()))
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 existsSync19, readFileSync as readFileSync15 } from "node:fs";
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 (existsSync19(path)) {
11581
- const parsed = JSON.parse(readFileSync15(path, "utf8"));
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/semantic/jev-config.ts
11603
- import { closeSync as closeSync2, constants as fsConstants2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, statSync as statSync16 } from "node:fs";
11604
- import { dirname as dirname7 } from "node:path";
11605
- function parseJevMode(raw) {
11606
- return raw === "off" || raw === "observe" || raw === "enforce" ? raw : null;
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 jevConfigPath() {
11609
- return jevConfigFile();
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 = { provider: kind, apiKey, mode: DEFAULT_JEV_MODE, timeoutMs: JEV_CONFIG_DEFAULT_TIMEOUT_MS };
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 = statSync16(dirname7(path)).mode & 511;
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 ${dirname7(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 ${dirname7(path)})`,
11897
- fix: `chmod 700 ${dirname7(path)}`
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
- var DEFAULT_JEV_MODE = "enforce", JEV_PROVIDER_KINDS, JEV_CLOUD_PROVIDER = "failproofai", JEV_API_KEY_ENV = "FAILPROOFAI_JEV_API_KEY", JEV_CONFIG_DEFAULT_TIMEOUT_MS = 3000, MIN_JEV_TIMEOUT_MS = 100, MAX_JEV_TIMEOUT_MS = 1e4, JEV_CALIBRATED_FAMILY, MAX_CONFIG_BYTES, OPEN_FLAGS, VERSIONED_MODEL_RE, CLOUDFLARE_ACCOUNT_ID_RE, API_KEY_RE, MODEL_RE, LOOPBACK_HOSTS, CREDENTIAL_PARAM_PARTS, CREDENTIAL_PARAM_NAMES, QUOTABLE_PARAM_RE, CREDENTIAL_PREFIX_RE, KEY_STAND_IN = "supplied-by-the-environment", DIR_WRITABLE_BY_OTHERS2 = 18, KEY_LACKS_JEV_PROBLEM = "this machine is connected to FailproofAI Cloud, but no Jev key is stored for that connection (its key lacks jev:evaluate, or the connect could not confirm it)";
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 existsSync20, readFileSync as readFileSync16, statSync as statSync17 } from "node:fs";
12020
- import { homedir as homedir20 } from "node:os";
12021
- import { dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve14 } from "node:path";
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) ? resolve14(t) : resolve14(base, 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 = homedir20()) {
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 = resolve14(dir, ".git");
12246
- if (existsSync20(candidate))
13142
+ const candidate = resolve16(dir, ".git");
13143
+ if (existsSync21(candidate))
12247
13144
  return candidate;
12248
- const parent = dirname8(dir);
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 ? dirname8(gitDir) : cwd;
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 = resolve14(gitPath, "HEAD");
12269
- if (statSync17(gitPath).isFile()) {
12270
- const pointer = readFileSync16(gitPath, "utf8").trim();
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 = resolve14(dirname8(gitPath), m[1], "HEAD");
13171
+ headFile = resolve16(dirname9(gitPath), m[1], "HEAD");
12275
13172
  }
12276
- const head = readFileSync16(headFile, "utf8").trim();
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 chmodSync3,
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 readFileSync17,
15720
+ readFileSync as readFileSync19,
14824
15721
  readSync as readSync3,
14825
- renameSync as renameSync5,
14826
- statSync as statSync18,
15722
+ renameSync as renameSync6,
15723
+ statSync as statSync20,
14827
15724
  unlinkSync as unlinkSync2,
14828
- writeFileSync as writeFileSync7
15725
+ writeFileSync as writeFileSync8
14829
15726
  } from "node:fs";
14830
- import { dirname as dirname9, resolve as resolve15 } from "node:path";
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 = resolve15(failproofaiHome());
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 = dirname9(dir);
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 = statSync18(dir).mode & 511;
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
- chmodSync3(dir, mode & ~DIR_WRITABLE_BY_OTHERS3);
14863
- if ((statSync18(dir).mode & DIR_WRITABLE_BY_OTHERS3) !== 0)
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(readFileSync17(intentFile(sessionId), "utf8"));
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
- writeFileSync7(tmp, JSON.stringify(file), { mode: 384 });
14891
- renameSync5(tmp, target);
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
- statSync18(path);
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 = resolve15(dir, name);
15811
+ const path = resolve17(dir, name);
14915
15812
  try {
14916
- const st = statSync18(path);
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 = statSync18(path);
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 = () => resolve15(semanticDir(), "sessions"), intentFile = (sessionId) => resolve15(sessionsDir(), `${sessionId}.json`), DIR_WRITABLE_BY_OTHERS3 = 18, SESSION_FILE_RE3, NON_HUMAN_PREFIXES, CONTINUATION_PREFIX = "This session is being continued from a previous conversation", IDE_MACHINE_OPENERS, IDE_AMBIGUOUS_OPENERS, IDE_REQUEST_HEADINGS, obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : undefined, CLAUDE_MACHINE_SOURCES, claudeMachineTurn = (payload) => typeof payload.source === "string" && CLAUDE_MACHINE_SOURCES.has(payload.source), piMachineTurn = (payload) => payload.input_source === "extension" || payload.source === "extension", openclawMachineRun = (payload) => {
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 renameSync6, statSync as statSync19 } from "node:fs";
15884
- import { resolve as resolve16 } from "node:path";
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 (statSync19(file).size > VERDICT_LOG_MAX_BYTES)
16058
- renameSync6(file, `${file}.1`);
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 = () => resolve16(semanticDir(), "verdicts.jsonl");
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 readFileSync18, statSync as statSync20, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "node:fs";
16289
- import { isAbsolute as isAbsolute4, resolve as resolve17 } from "node:path";
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) && resolve17(v) === v && 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 = statSync20(rootsDir()).mode & 511;
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(readFileSync18(rootFile(sessionId), "utf8"));
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 = resolve17(dir, name);
17717
+ const path = resolve19(dir, name);
16321
17718
  try {
16322
- const st = statSync20(path);
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
- writeFileSync8(rootFile(sessionId), JSON.stringify({ root: live, at: now }), { mode: 384, flag: "wx" });
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 = () => resolve17(semanticDir(), "roots"), rootFile = (sessionId) => resolve17(rootsDir(), `${sessionId}.json`);
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((o) => o.policy) : [],
16421
- notDenied: sent ? outcomes.filter((o) => o.verdict !== "deny" && !warned(o)).map((o) => o.policy) : [],
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
- throttled = throttle.throttleTransport(route.transport, { scope: throttleScope(cfg, route) });
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 answered = evaluateSemantic(input, {
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
- }).then((outcome) => {
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
- appendVerdictLog(hit ? { ...row, cached: true } : row);
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
- return handle(Promise.race([answered, expired]).finally(() => {
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 existsSync21 } from "node:fs";
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
- if (!existsSync21(jevConfigFile()))
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
- async function startTwoTier(canonicalEventType, parsed, session, cli, opts, activePause) {
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) || activePause)
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
- const loaded = await readJevConfig();
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
- ...cloudManagedPolicies.length > 0 ? { cloudManagedPolicies } : {},
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 existsSync22, unlinkSync as unlinkSync4 } from "node:fs";
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 (existsSync22(socketPath)) {
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 (!existsSync22(socketPath))
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 dirname10, resolve as resolve18 } from "node:path";
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 = resolve18(dirname10(realpathSync2(fileURLToPath(import.meta.url))), "..");
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 = resolve18(dirname10(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
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) {