failproofai 1.0.0-beta.2 → 1.0.0-beta.3

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 (196) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +6 -6
  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/build-manifest.json +3 -3
  6. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  17. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  23. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  24. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  26. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  27. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
  37. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js +1 -1
  39. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/index.html +1 -1
  42. package/.next/standalone/.next/server/app/index.rsc +15 -15
  43. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  44. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  45. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  46. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  47. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  48. package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
  49. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  50. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
  53. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
  54. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  55. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
  58. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  59. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  60. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  63. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  64. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
  67. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
  75. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +2 -2
  77. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +2 -2
  78. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +2 -2
  79. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ath6v_._.js → [root-of-the-server]__0ls0gsr._.js} +3 -3
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +2 -2
  81. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +2 -2
  83. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1psjybo._.js → [root-of-the-server]__10s_z4l._.js} +2 -2
  84. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0114ewg._.js → [root-of-the-server]__1j4u3yd._.js} +3 -3
  85. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0nb0syg._.js → [root-of-the-server]__1ky3im9._.js} +2 -2
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +2 -2
  87. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  90. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0oy_-36.js → node_modules_html-to-image_es_index_0jfts82.js} +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
  93. package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
  94. package/.next/standalone/.next/server/pages/404.html +1 -1
  95. package/.next/standalone/.next/server/pages/500.html +1 -1
  96. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  97. package/.next/standalone/.next/server/server-reference-manifest.json +32 -20
  98. package/.next/standalone/.next/static/chunks/{3s7myk_co0gbh.js → 01h6lfjz1_oie.js} +1 -1
  99. package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
  100. package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 0a7k_x4rp_97z.js} +1 -1
  101. package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 0cvd6bcdwchc_.js} +1 -1
  102. package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 13-obgukzg_ke.js} +1 -1
  103. package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
  104. package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 1h0_yaoq5f3yx.js} +1 -1
  105. package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 29g8ykjk6sx_8.js} +1 -1
  106. package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 2rqm-klet_gbl.js} +1 -1
  107. package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 2uvyh8y0naofj.js} +1 -1
  108. package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
  109. package/.next/standalone/.next/static/chunks/3p-n_j80zlzi3.js +6 -0
  110. package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +2 -0
  111. package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
  112. package/.next/standalone/Cargo.lock +1992 -198
  113. package/.next/standalone/Cargo.toml +1 -1
  114. package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
  115. package/.next/standalone/app/components/pause-notices.tsx +111 -0
  116. package/.next/standalone/app/policies/hooks-client.tsx +81 -6
  117. package/.next/standalone/crates/CLOUD_POLICIES.md +136 -0
  118. package/.next/standalone/crates/failproofaid/Cargo.toml +7 -0
  119. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +484 -0
  120. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +921 -0
  121. package/.next/standalone/crates/failproofaid/src/main.rs +452 -3
  122. package/.next/standalone/crates/failproofaid/src/paths.rs +29 -0
  123. package/.next/standalone/crates/fpai-collect/Cargo.toml +43 -0
  124. package/.next/standalone/crates/fpai-collect/src/config.rs +412 -0
  125. package/.next/standalone/crates/fpai-collect/src/cursor.rs +520 -0
  126. package/.next/standalone/crates/fpai-collect/src/delivery.rs +472 -0
  127. package/.next/standalone/crates/fpai-collect/src/filetail.rs +484 -0
  128. package/.next/standalone/crates/fpai-collect/src/health.rs +369 -0
  129. package/.next/standalone/crates/fpai-collect/src/lib.rs +34 -0
  130. package/.next/standalone/crates/fpai-collect/src/redact.rs +516 -0
  131. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +279 -0
  132. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +619 -0
  133. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +156 -0
  134. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +715 -0
  135. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +158 -0
  136. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +512 -0
  137. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +511 -0
  138. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +586 -0
  139. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +318 -0
  140. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +579 -0
  141. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +310 -0
  142. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +448 -0
  143. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +23 -0
  144. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +176 -0
  145. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +500 -0
  146. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +315 -0
  147. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +493 -0
  148. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +201 -0
  149. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +533 -0
  150. package/.next/standalone/crates/fpai-collect/src/spool.rs +458 -0
  151. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +387 -0
  152. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +423 -0
  153. package/.next/standalone/crates/fpai-collect/src/uploader.rs +600 -0
  154. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +738 -0
  155. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +393 -0
  156. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +975 -0
  157. package/.next/standalone/crates/fpai-collect/tests/config.rs +286 -0
  158. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +1019 -0
  159. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +281 -0
  160. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +1008 -0
  161. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +1129 -0
  162. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +621 -0
  163. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +854 -0
  164. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +1478 -0
  165. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +1191 -0
  166. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +290 -0
  167. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +506 -0
  168. package/.next/standalone/package.json +2 -2
  169. package/.next/standalone/server.js +1 -1
  170. package/bin/failproofai.mjs +129 -0
  171. package/dist/cli.mjs +2195 -770
  172. package/dist/worker.mjs +365 -52
  173. package/package.json +2 -2
  174. package/src/hooks/builtin-policies.ts +139 -0
  175. package/src/hooks/cloud-connection.ts +205 -0
  176. package/src/hooks/cloud-enrollment-cli.ts +227 -0
  177. package/src/hooks/cloud-enrollment.ts +173 -0
  178. package/src/hooks/cloud-managed-policies.ts +138 -0
  179. package/src/hooks/collector-config.ts +226 -0
  180. package/src/hooks/configure-wizard.ts +221 -5
  181. package/src/hooks/custom-hooks-loader.ts +69 -25
  182. package/src/hooks/daemon-client.ts +18 -0
  183. package/src/hooks/handler.ts +144 -6
  184. package/src/hooks/hook-activity-store.ts +50 -0
  185. package/src/hooks/loader-utils.ts +6 -4
  186. package/src/hooks/policy-types.ts +35 -0
  187. package/src/hooks/session-pause-cli.ts +157 -0
  188. package/src/hooks/session-pause.ts +236 -0
  189. package/src/hooks/tui.ts +115 -0
  190. package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
  191. package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
  192. package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
  193. package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
  194. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → pM1veX5Llx4YKEaiFc8Sf}/_buildManifest.js +0 -0
  195. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → pM1veX5Llx4YKEaiFc8Sf}/_clientMiddlewareManifest.js +0 -0
  196. /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → pM1veX5Llx4YKEaiFc8Sf}/_ssgManifest.js +0 -0
package/dist/worker.mjs CHANGED
@@ -655,6 +655,41 @@ function getFilePath(ctx) {
655
655
  function parseArgvTokens(cmd) {
656
656
  return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
657
657
  }
658
+ function decodeAnsiC(body) {
659
+ return body.replace(/\\(x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{1,4}|U[0-9A-Fa-f]{1,8}|[0-7]{1,3}|.)/g, (_, seq) => {
660
+ try {
661
+ if (seq[0] === "x")
662
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
663
+ if (seq[0] === "u" || seq[0] === "U")
664
+ return String.fromCodePoint(parseInt(seq.slice(1), 16));
665
+ if (/^[0-7]+$/.test(seq))
666
+ return String.fromCharCode(parseInt(seq, 8));
667
+ } catch {
668
+ return seq;
669
+ }
670
+ const named = {
671
+ a: "\x07",
672
+ b: "\b",
673
+ e: "\x1B",
674
+ f: "\f",
675
+ n: `
676
+ `,
677
+ r: "\r",
678
+ t: "\t",
679
+ v: "\v",
680
+ "\\": "\\",
681
+ "'": "'",
682
+ '"': '"',
683
+ "?": "?"
684
+ };
685
+ return named[seq] ?? seq;
686
+ });
687
+ }
688
+ function stripShellQuoting(command) {
689
+ const joined = command.replace(/\\\r?\n/g, "");
690
+ const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body) => decodeAnsiC(body));
691
+ return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
692
+ }
658
693
  function statGitHeadMtimeMs(cwd) {
659
694
  try {
660
695
  return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
@@ -917,6 +952,15 @@ function blockSudo(ctx) {
917
952
  }
918
953
  return allow();
919
954
  }
955
+ function blockSelfPause(ctx) {
956
+ if (ctx.toolName !== "Bash")
957
+ return allow();
958
+ const cmd = getCommand(ctx);
959
+ if (SELF_PAUSE_RE.test(cmd) || SELF_PAUSE_RE.test(stripShellQuoting(cmd))) {
960
+ return deny("Pausing failproofai enforcement is a human action, not an agent one. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
961
+ }
962
+ return allow();
963
+ }
920
964
  function blockCurlPipeSh(ctx) {
921
965
  if (ctx.toolName !== "Bash")
922
966
  return allow();
@@ -1654,7 +1698,7 @@ function registerBuiltinPolicies(enabledNames) {
1654
1698
  }
1655
1699
  }
1656
1700
  }
1657
- var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1701
+ var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SELF_PAUSE_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
1658
1702
  var init_builtin_policies = __esm(() => {
1659
1703
  init_hook_logger();
1660
1704
  isClaudeInternalPath = isAgentInternalPath;
@@ -1695,6 +1739,7 @@ var init_builtin_policies = __esm(() => {
1695
1739
  PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
1696
1740
  RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
1697
1741
  CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
1742
+ SELF_PAUSE_RE = /failproofai[^\s]*(?:\s+\S+)*?\s+(?:config|configure|setup)\b(?:\s+\S+)*?\s+--pause\b/;
1698
1743
  PS_WEB_PIPE_RE = /(?:Invoke-WebRequest|iwr|Invoke-RestMethod|irm)\s+.*\|\s*(?:Invoke-Expression|iex)/i;
1699
1744
  SHORT_FLAG_BUNDLE_RE = /^-[a-zA-Z]*f[a-zA-Z]*$/;
1700
1745
  SAFE_FORCE_PREFIXES = ["--force-with-lease", "--force-if-includes"];
@@ -1842,6 +1887,16 @@ var init_builtin_policies = __esm(() => {
1842
1887
  }
1843
1888
  }
1844
1889
  },
1890
+ {
1891
+ name: "block-self-pause",
1892
+ displayTitle: "Tried to pause failproofai enforcement",
1893
+ impact: "An agent that can pause enforcement can switch off every other policy.",
1894
+ description: "Block agents from pausing failproofai enforcement",
1895
+ fn: blockSelfPause,
1896
+ match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
1897
+ defaultEnabled: true,
1898
+ category: "Dangerous Commands"
1899
+ },
1845
1900
  {
1846
1901
  name: "block-sudo",
1847
1902
  displayTitle: "Tried to run a command with sudo",
@@ -2285,7 +2340,7 @@ var init_builtin_policies = __esm(() => {
2285
2340
  });
2286
2341
 
2287
2342
  // package.json
2288
- var version = "1.0.0-beta.2";
2343
+ var version = "1.0.0-beta.3";
2289
2344
  var init_package = () => {};
2290
2345
 
2291
2346
  // src/posthog-key.ts
@@ -3158,8 +3213,14 @@ function clearCustomHooks() {
3158
3213
  const g = globalThis;
3159
3214
  g[REGISTRY_KEY2] = [];
3160
3215
  }
3161
- var REGISTRY_KEY2 = "__failproofai_custom_hooks__";
3162
- var init_custom_hooks_registry = () => {};
3216
+ var REGISTRY_KEY2 = "__failproofai_custom_hooks__", customPolicies;
3217
+ var init_custom_hooks_registry = __esm(() => {
3218
+ customPolicies = {
3219
+ add(hook) {
3220
+ getRegistry2().push(hook);
3221
+ }
3222
+ };
3223
+ });
3163
3224
 
3164
3225
  // src/hooks/loader-utils.ts
3165
3226
  import { readFile as readFile2, writeFile as writeFile2, unlink, access } from "fs/promises";
@@ -3200,8 +3261,8 @@ async function resolveLocalImport(fromDir, specifier) {
3200
3261
  }
3201
3262
  return null;
3202
3263
  }
3203
- async function createEsmShim(distIndex, distUrl) {
3204
- const shimPath = distIndex + ".__failproofai_esm_shim__.mjs";
3264
+ async function createEsmShim(distIndex, distUrl, tmpSuffix = TMP_SUFFIX) {
3265
+ const shimPath = `${distIndex}${tmpSuffix}.shim.mjs`;
3205
3266
  const shimCode = [
3206
3267
  `import _cjs from '${distUrl}';`,
3207
3268
  `export const customPolicies = _cjs.customPolicies;`,
@@ -3216,13 +3277,13 @@ async function createEsmShim(distIndex, distUrl) {
3216
3277
  await writeFile2(shimPath, shimCode, "utf-8");
3217
3278
  return { shimPath, shimUrl: pathToFileURL(shimPath).href };
3218
3279
  }
3219
- async function rewriteFileTree(entryPath, distUrl, distIndex) {
3280
+ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SUFFIX) {
3220
3281
  const queue = [entryPath];
3221
3282
  const visited = new Set;
3222
3283
  const tmpFiles = [];
3223
3284
  let esmShimUrl = null;
3224
3285
  if (distIndex && distUrl) {
3225
- const shim = await createEsmShim(distIndex, distUrl);
3286
+ const shim = await createEsmShim(distIndex, distUrl, tmpSuffix);
3226
3287
  tmpFiles.push(shim.shimPath);
3227
3288
  esmShimUrl = shim.shimUrl;
3228
3289
  }
@@ -3253,7 +3314,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
3253
3314
  if (!visited.has(resolved2) && !queue.includes(resolved2)) {
3254
3315
  queue.push(resolved2);
3255
3316
  }
3256
- let relPath = relative(dir, resolved2 + TMP_SUFFIX).split("\\").join("/");
3317
+ let relPath = relative(dir, resolved2 + tmpSuffix).split("\\").join("/");
3257
3318
  if (!relPath.startsWith("."))
3258
3319
  relPath = "./" + relPath;
3259
3320
  rewrites.set(specifier, relPath);
@@ -3266,7 +3327,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex) {
3266
3327
  code = code.replace(new RegExp(`'${escaped}'`, "g"), `'${replacement}'`);
3267
3328
  code = code.replace(new RegExp(`"${escaped}"`, "g"), `"${replacement}"`);
3268
3329
  }
3269
- const tmpPath = filePath + TMP_SUFFIX;
3330
+ const tmpPath = filePath + tmpSuffix;
3270
3331
  await writeFile2(tmpPath, code, "utf-8");
3271
3332
  tmpFiles.push(tmpPath);
3272
3333
  }
@@ -3288,8 +3349,10 @@ var init_loader_utils = __esm(() => {
3288
3349
  // src/hooks/custom-hooks-loader.ts
3289
3350
  import { resolve as resolve4, isAbsolute, basename } from "node:path";
3290
3351
  import { existsSync as existsSync3, readdirSync } from "node:fs";
3352
+ import { readFile as readFile3 } from "node:fs/promises";
3291
3353
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3292
3354
  import { homedir as homedir4 } from "node:os";
3355
+ import { createHash } from "node:crypto";
3293
3356
  function discoverPolicyFiles(dir) {
3294
3357
  if (!existsSync3(dir))
3295
3358
  return [];
@@ -3316,10 +3379,24 @@ async function loadSingleFile(absPath, opts) {
3316
3379
  try {
3317
3380
  const distIndex = await findDistIndex();
3318
3381
  const distUrl = distIndex ? pathToFileURL2(distIndex).href : null;
3319
- tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex);
3320
- const entryTmp = absPath + TMP_SUFFIX;
3321
- const fileUrl = `${pathToFileURL2(entryTmp).href}?v=${++loadSequence}`;
3382
+ const sequence = ++loadSequence;
3383
+ const tmpSuffix = `${TMP_SUFFIX}.${process.pid}.${sequence}.mjs`;
3384
+ tmpFiles = await rewriteFileTree(absPath, distUrl, distIndex, tmpSuffix);
3385
+ const fingerprint = await fingerprintTemporaryTree(tmpFiles, tmpSuffix);
3386
+ const cached = policyModuleCache.get(absPath);
3387
+ if (cached?.fingerprint === fingerprint) {
3388
+ for (const hook of cached.hooks)
3389
+ customPolicies.add({ ...hook });
3390
+ return;
3391
+ }
3392
+ const entryTmp = absPath + tmpSuffix;
3393
+ const fileUrl = pathToFileURL2(entryTmp).href;
3394
+ const hooksBefore = getCustomHooks().length;
3322
3395
  await import(fileUrl);
3396
+ policyModuleCache.set(absPath, {
3397
+ fingerprint,
3398
+ hooks: getCustomHooks().slice(hooksBefore).map((hook) => ({ ...hook }))
3399
+ });
3323
3400
  } catch (err) {
3324
3401
  const msg = err instanceof Error ? err.message : String(err);
3325
3402
  const errorType = /Cannot find module|MODULE_NOT_FOUND|ENOENT/i.test(msg) ? "module_not_found" : /SyntaxError|Unexpected token/i.test(msg) ? "syntax_error" : "runtime_error";
@@ -3337,6 +3414,15 @@ async function loadSingleFile(absPath, opts) {
3337
3414
  await cleanupTmpFiles(tmpFiles);
3338
3415
  }
3339
3416
  }
3417
+ async function fingerprintTemporaryTree(tmpFiles, tmpSuffix) {
3418
+ const hash = createHash("sha256");
3419
+ for (const tmpFile of [...tmpFiles].sort()) {
3420
+ hash.update(tmpFile.replaceAll(tmpSuffix, TMP_SUFFIX));
3421
+ const contents = await readFile3(tmpFile, "utf8");
3422
+ hash.update(contents.replaceAll(tmpSuffix, TMP_SUFFIX));
3423
+ }
3424
+ return hash.digest("hex");
3425
+ }
3340
3426
  function customPolicyId(file, name) {
3341
3427
  return `custom:${file}:${name}`;
3342
3428
  }
@@ -3356,6 +3442,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3356
3442
  const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
3357
3443
  const conventionEnabled = opts?.customPoliciesEnabled !== false;
3358
3444
  const loadedPaths = new Set;
3445
+ const cloudManagedByPath = new Map((opts?.cloudManagedPolicies ?? []).map((policy) => [resolve4(policy.path), policy]));
3359
3446
  for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
3360
3447
  const absPath = resolve4(projectRoot, customPoliciesPath);
3361
3448
  if (existsSync3(absPath)) {
@@ -3364,7 +3451,14 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3364
3451
  const hooksBefore = getCustomHooks().length;
3365
3452
  await loadSingleFile(absPath);
3366
3453
  for (const hook of getCustomHooks().slice(hooksBefore)) {
3367
- hook.__policyId = customPolicyId(absPath, hook.name);
3454
+ const cloudManaged = cloudManagedByPath.get(absPath);
3455
+ const tagged = hook;
3456
+ if (cloudManaged) {
3457
+ tagged.__cloudManaged = cloudManaged;
3458
+ tagged.__policyId = `cloud:${cloudManaged.id}@${cloudManaged.revision}:${hook.name}`;
3459
+ } else {
3460
+ tagged.__policyId = customPolicyId(absPath, hook.name);
3461
+ }
3368
3462
  }
3369
3463
  }
3370
3464
  } else {
@@ -3436,7 +3530,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3436
3530
  }
3437
3531
  return { hooks: allHooks, conventionSources };
3438
3532
  }
3439
- var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, LOADABLE_EXT_RE;
3533
+ var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, LOADABLE_EXT_RE;
3440
3534
  var init_custom_hooks_loader = __esm(() => {
3441
3535
  init_hook_logger();
3442
3536
  init_custom_hooks_registry();
@@ -3445,6 +3539,7 @@ var init_custom_hooks_loader = __esm(() => {
3445
3539
  init_hook_telemetry();
3446
3540
  init_telemetry_id();
3447
3541
  CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
3542
+ policyModuleCache = new Map;
3448
3543
  LOADABLE_EXT_RE = /\.(js|mjs|ts)$/;
3449
3544
  });
3450
3545
 
@@ -3911,7 +4006,7 @@ __export(exports_copilot_projects, {
3911
4006
  getCachedCopilotSessionsByEncodedName: () => getCachedCopilotSessionsByEncodedName,
3912
4007
  getCachedCopilotProjects: () => getCachedCopilotProjects
3913
4008
  });
3914
- import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
4009
+ import { readdir as readdir2, readFile as readFile4, stat as stat2 } from "node:fs/promises";
3915
4010
  import { homedir as homedir8 } from "node:os";
3916
4011
  import { join as join6 } from "node:path";
3917
4012
  function getCopilotSessionStateRoot() {
@@ -3950,7 +4045,7 @@ async function scanCopilotSessions() {
3950
4045
  const settled = await batchAll(candidates.map((c) => async () => {
3951
4046
  let workspaceText;
3952
4047
  try {
3953
- workspaceText = await readFile3(c.workspacePath, "utf-8");
4048
+ workspaceText = await readFile4(c.workspacePath, "utf-8");
3954
4049
  } catch {
3955
4050
  return null;
3956
4051
  }
@@ -4059,7 +4154,7 @@ __export(exports_cursor_projects, {
4059
4154
  getCachedCursorSessionsByEncodedName: () => getCachedCursorSessionsByEncodedName,
4060
4155
  getCachedCursorProjects: () => getCachedCursorProjects
4061
4156
  });
4062
- import { readdir as readdir3, readFile as readFile4, stat as stat3 } from "node:fs/promises";
4157
+ import { readdir as readdir3, readFile as readFile5, stat as stat3 } from "node:fs/promises";
4063
4158
  import { homedir as homedir9 } from "node:os";
4064
4159
  import { join as join7 } from "node:path";
4065
4160
  function getCursorHome() {
@@ -4100,7 +4195,7 @@ async function findFirstUsableMeta(dir, candidates) {
4100
4195
  continue;
4101
4196
  let text;
4102
4197
  try {
4103
- text = await readFile4(path2, "utf-8");
4198
+ text = await readFile5(path2, "utf-8");
4104
4199
  } catch {
4105
4200
  continue;
4106
4201
  }
@@ -4432,7 +4527,7 @@ __export(exports_pi_projects, {
4432
4527
  getCachedPiSessionsByEncodedName: () => getCachedPiSessionsByEncodedName,
4433
4528
  getCachedPiProjects: () => getCachedPiProjects
4434
4529
  });
4435
- import { readdir as readdir4, readFile as readFile5, stat as stat4 } from "node:fs/promises";
4530
+ import { readdir as readdir4, readFile as readFile6, stat as stat4 } from "node:fs/promises";
4436
4531
  import { homedir as homedir10 } from "node:os";
4437
4532
  import { join as join8 } from "node:path";
4438
4533
  function getPiSessionsRoot() {
@@ -4455,7 +4550,7 @@ async function statMtime3(path2) {
4455
4550
  async function readSessionCwd(filePath) {
4456
4551
  let text;
4457
4552
  try {
4458
- text = await readFile5(filePath, "utf-8");
4553
+ text = await readFile6(filePath, "utf-8");
4459
4554
  } catch {
4460
4555
  return null;
4461
4556
  }
@@ -5037,7 +5132,7 @@ var init_hermes_projects = __esm(() => {
5037
5132
  });
5038
5133
 
5039
5134
  // lib/openclaw-sessions.ts
5040
- import { readFile as readFile6 } from "node:fs/promises";
5135
+ import { readFile as readFile7 } from "node:fs/promises";
5041
5136
  import { readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
5042
5137
  import { homedir as homedir12 } from "node:os";
5043
5138
  import { join as join11 } from "node:path";
@@ -5209,7 +5304,7 @@ async function getOpenClawSessionLog(sessionId) {
5209
5304
  return null;
5210
5305
  let content;
5211
5306
  try {
5212
- content = await readFile6(filePath, "utf-8");
5307
+ content = await readFile7(filePath, "utf-8");
5213
5308
  } catch {
5214
5309
  return null;
5215
5310
  }
@@ -5406,7 +5501,7 @@ var init_openclaw_projects = __esm(() => {
5406
5501
  });
5407
5502
 
5408
5503
  // lib/factory-sessions.ts
5409
- import { readFile as readFile7 } from "node:fs/promises";
5504
+ import { readFile as readFile8 } from "node:fs/promises";
5410
5505
  import { readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
5411
5506
  import { homedir as homedir13 } from "node:os";
5412
5507
  import { join as join13 } from "node:path";
@@ -5597,7 +5692,7 @@ async function getFactorySessionLog(sessionId) {
5597
5692
  return null;
5598
5693
  let content;
5599
5694
  try {
5600
- content = await readFile7(filePath, "utf-8");
5695
+ content = await readFile8(filePath, "utf-8");
5601
5696
  } catch {
5602
5697
  return null;
5603
5698
  }
@@ -6001,7 +6096,7 @@ var init_devin_projects = __esm(() => {
6001
6096
  });
6002
6097
 
6003
6098
  // lib/antigravity-sessions.ts
6004
- import { readFile as readFile8 } from "node:fs/promises";
6099
+ import { readFile as readFile9 } from "node:fs/promises";
6005
6100
  import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
6006
6101
  import { homedir as homedir15 } from "node:os";
6007
6102
  import { join as join15 } from "node:path";
@@ -6161,7 +6256,7 @@ async function getAntigravitySessionLog(conversationId) {
6161
6256
  return null;
6162
6257
  let content;
6163
6258
  try {
6164
- content = await readFile8(filePath, "utf-8");
6259
+ content = await readFile9(filePath, "utf-8");
6165
6260
  } catch {
6166
6261
  return null;
6167
6262
  }
@@ -6837,7 +6932,7 @@ async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId
6837
6932
  var init_resolve_subagent_path = () => {};
6838
6933
 
6839
6934
  // lib/log-entries.ts
6840
- import { readFile as readFile9 } from "fs/promises";
6935
+ import { readFile as readFile10 } from "fs/promises";
6841
6936
  import { join as join20 } from "path";
6842
6937
  function formatTimestamp(date) {
6843
6938
  const base = formatDate(date);
@@ -7033,7 +7128,7 @@ async function parseSessionLog(projectName, sessionId) {
7033
7128
  const projectDir = resolveProjectPath(projectName);
7034
7129
  const projectsPath = getClaudeProjectsPath();
7035
7130
  const filePath = join20(projectDir, `${sessionId}.jsonl`);
7036
- const fileContent = await readFile9(filePath, "utf-8");
7131
+ const fileContent = await readFile10(filePath, "utf-8");
7037
7132
  const { entries: sessionEntries, rawLines: sessionRawLines, subagentIds } = await parseFileContent(fileContent, "session");
7038
7133
  if (subagentIds.length === 0) {
7039
7134
  return { entries: sessionEntries, rawLines: sessionRawLines, subagentIds: [] };
@@ -7043,7 +7138,7 @@ async function parseSessionLog(projectName, sessionId) {
7043
7138
  const agentPath = await resolveSubagentPath(projectsPath, projectName, sessionId, agentId);
7044
7139
  if (!agentPath)
7045
7140
  return null;
7046
- const agentContent = await readFile9(agentPath, "utf-8");
7141
+ const agentContent = await readFile10(agentPath, "utf-8");
7047
7142
  const { entries, rawLines } = await parseFileContent(agentContent, agentSource);
7048
7143
  return { entries, rawLines };
7049
7144
  }), SUBAGENT_CONCURRENCY);
@@ -7070,7 +7165,7 @@ var init_log_entries = __esm(() => {
7070
7165
 
7071
7166
  // lib/codex-sessions.ts
7072
7167
  import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync8 } from "node:fs";
7073
- import { readFile as readFile10 } from "node:fs/promises";
7168
+ import { readFile as readFile11 } from "node:fs/promises";
7074
7169
  import { dirname as dirname4, join as join21 } from "node:path";
7075
7170
  import { homedir as homedir17 } from "node:os";
7076
7171
  function readCache() {
@@ -7344,7 +7439,7 @@ async function getCodexSessionLog(sessionId) {
7344
7439
  const filePath = findCodexTranscript(sessionId);
7345
7440
  if (!filePath)
7346
7441
  return null;
7347
- const fileContent = await readFile10(filePath, "utf-8");
7442
+ const fileContent = await readFile11(filePath, "utf-8");
7348
7443
  const { entries, rawLines, cwd } = await parseCodexLog(fileContent, "session");
7349
7444
  return { entries, rawLines, cwd, filePath };
7350
7445
  }
@@ -7397,7 +7492,7 @@ var init_resolve_permission_mode = __esm(() => {
7397
7492
 
7398
7493
  // lib/copilot-sessions.ts
7399
7494
  import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync9 } from "node:fs";
7400
- import { readFile as readFile11 } from "node:fs/promises";
7495
+ import { readFile as readFile12 } from "node:fs/promises";
7401
7496
  import { join as join22, resolve as resolve7, sep as sep2 } from "node:path";
7402
7497
  import { homedir as homedir18 } from "node:os";
7403
7498
  function getCopilotHome() {
@@ -7598,7 +7693,7 @@ async function getCopilotSessionLog(sessionId) {
7598
7693
  return null;
7599
7694
  let fileContent;
7600
7695
  try {
7601
- fileContent = await readFile11(filePath, "utf-8");
7696
+ fileContent = await readFile12(filePath, "utf-8");
7602
7697
  } catch {
7603
7698
  return null;
7604
7699
  }
@@ -7614,7 +7709,7 @@ var init_copilot_sessions = __esm(() => {
7614
7709
 
7615
7710
  // lib/cursor-sessions.ts
7616
7711
  import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
7617
- import { readFile as readFile12 } from "node:fs/promises";
7712
+ import { readFile as readFile13 } from "node:fs/promises";
7618
7713
  import { basename as basename3, join as join23, resolve as resolve8, sep as sep3 } from "node:path";
7619
7714
  import { homedir as homedir19 } from "node:os";
7620
7715
  function getCursorHome2() {
@@ -7853,7 +7948,7 @@ async function getCursorSessionLog(sessionId) {
7853
7948
  return null;
7854
7949
  let fileContent;
7855
7950
  try {
7856
- fileContent = await readFile12(filePath, "utf-8");
7951
+ fileContent = await readFile13(filePath, "utf-8");
7857
7952
  } catch {
7858
7953
  return null;
7859
7954
  }
@@ -7870,7 +7965,7 @@ var init_cursor_sessions = __esm(() => {
7870
7965
 
7871
7966
  // lib/pi-sessions.ts
7872
7967
  import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
7873
- import { readFile as readFile13 } from "node:fs/promises";
7968
+ import { readFile as readFile14 } from "node:fs/promises";
7874
7969
  import { join as join24, resolve as resolve9, sep as sep4 } from "node:path";
7875
7970
  import { homedir as homedir20 } from "node:os";
7876
7971
  function getPiSessionStateRoot() {
@@ -8032,7 +8127,7 @@ async function getPiSessionLog(sessionId) {
8032
8127
  return null;
8033
8128
  let fileContent;
8034
8129
  try {
8035
- fileContent = await readFile13(filePath, "utf-8");
8130
+ fileContent = await readFile14(filePath, "utf-8");
8036
8131
  } catch {
8037
8132
  return null;
8038
8133
  }
@@ -8101,6 +8196,158 @@ var init_resolve_transcript_path = __esm(() => {
8101
8196
  init_factory_sessions();
8102
8197
  init_antigravity_sessions();
8103
8198
  });
8199
+ // src/hooks/cloud-managed-policies.ts
8200
+ import { createHash as createHash2 } from "node:crypto";
8201
+ import { existsSync as existsSync10, readFileSync as readFileSync10, realpathSync } from "node:fs";
8202
+ import { homedir as homedir21 } from "node:os";
8203
+ import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve10 } from "node:path";
8204
+ function cloudManagedPolicyRoot() {
8205
+ return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? resolve10(homedir21(), ".failproofai", "policies", "cloud-managed");
8206
+ }
8207
+ function parseManifest(value) {
8208
+ if (!value || typeof value !== "object")
8209
+ throw new Error("active manifest is not an object");
8210
+ const manifest = value;
8211
+ if (manifest.schemaVersion !== ACTIVE_SCHEMA_VERSION) {
8212
+ throw new Error(`unsupported active manifest schema ${String(manifest.schemaVersion)}`);
8213
+ }
8214
+ if (!Number.isSafeInteger(manifest.generation) || (manifest.generation ?? -1) < 0) {
8215
+ throw new Error("active manifest generation is invalid");
8216
+ }
8217
+ if (!Array.isArray(manifest.policies))
8218
+ throw new Error("active manifest policies is not an array");
8219
+ return manifest;
8220
+ }
8221
+ function resolveManagedPath(root, candidate) {
8222
+ if (!candidate || isAbsolute2(candidate))
8223
+ throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
8224
+ const absolute = resolve10(root, candidate);
8225
+ const lexicalRelative = relative3(root, absolute);
8226
+ if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
8227
+ throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
8228
+ }
8229
+ const realRoot = realpathSync(root);
8230
+ const realFile = realpathSync(absolute);
8231
+ const physicalRelative = relative3(realRoot, realFile);
8232
+ if (physicalRelative.startsWith("..") || isAbsolute2(physicalRelative)) {
8233
+ throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
8234
+ }
8235
+ return realFile;
8236
+ }
8237
+ function readActiveCloudManagedPolicies() {
8238
+ const root = cloudManagedPolicyRoot();
8239
+ const activePath = resolve10(root, "active.json");
8240
+ if (!existsSync10(activePath))
8241
+ return [];
8242
+ let raw;
8243
+ try {
8244
+ raw = JSON.parse(readFileSync10(activePath, "utf8"));
8245
+ } catch (err) {
8246
+ throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
8247
+ }
8248
+ const manifest = parseManifest(raw);
8249
+ const seen = new Set;
8250
+ return manifest.policies.map((policy) => {
8251
+ if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
8252
+ throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
8253
+ }
8254
+ if (!Number.isSafeInteger(policy.revision) || policy.revision < 0) {
8255
+ throw new Error(`invalid revision for cloud-managed policy ${policy.id}`);
8256
+ }
8257
+ if (!SHA256_RE.test(policy.sha256)) {
8258
+ throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
8259
+ }
8260
+ if (seen.has(policy.id))
8261
+ throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
8262
+ seen.add(policy.id);
8263
+ const path2 = resolveManagedPath(root, policy.path);
8264
+ const bytes = readFileSync10(path2);
8265
+ const actual = createHash2("sha256").update(bytes).digest("hex");
8266
+ if (actual !== policy.sha256) {
8267
+ throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
8268
+ }
8269
+ if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
8270
+ throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
8271
+ }
8272
+ return {
8273
+ id: policy.id,
8274
+ revision: policy.revision,
8275
+ effect: policy.effect ?? "enforce",
8276
+ sha256: policy.sha256,
8277
+ path: path2,
8278
+ generation: manifest.generation
8279
+ };
8280
+ });
8281
+ }
8282
+ var ACTIVE_SCHEMA_VERSION = 1, SHA256_RE, POLICY_ID_RE;
8283
+ var init_cloud_managed_policies = __esm(() => {
8284
+ SHA256_RE = /^[a-f0-9]{64}$/;
8285
+ POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
8286
+ });
8287
+
8288
+ // lib/atomic-write.ts
8289
+ var init_atomic_write = () => {};
8290
+
8291
+ // src/hooks/session-pause.ts
8292
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync11, readFileSync as readFileSync11, rmSync } from "node:fs";
8293
+ import { homedir as homedir22 } from "node:os";
8294
+ import { createHash as createHash3 } from "node:crypto";
8295
+ import { join as join25, resolve as resolve11 } from "node:path";
8296
+ function pauseStateDir() {
8297
+ const override = process.env.FAILPROOFAI_STATE_DIR;
8298
+ if (override)
8299
+ return resolve11(override, "sessions");
8300
+ return join25(homedir22(), ".failproofai", "state", "sessions");
8301
+ }
8302
+ function pauseFilePath(sessionId) {
8303
+ const digest = createHash3("sha256").update(sessionId).digest("hex").slice(0, 32);
8304
+ return join25(pauseStateDir(), `${digest}.json`);
8305
+ }
8306
+ function parseStored(raw) {
8307
+ if (!raw || typeof raw !== "object")
8308
+ return null;
8309
+ const p = raw;
8310
+ if (p.schemaVersion !== SCHEMA_VERSION)
8311
+ return null;
8312
+ if (typeof p.sessionId !== "string" || p.sessionId.length === 0)
8313
+ return null;
8314
+ if (!Number.isFinite(p.pausedAt) || !Number.isFinite(p.expiresAt))
8315
+ return null;
8316
+ return {
8317
+ schemaVersion: SCHEMA_VERSION,
8318
+ sessionId: p.sessionId,
8319
+ pausedAt: p.pausedAt,
8320
+ expiresAt: p.expiresAt,
8321
+ setBy: typeof p.setBy === "string" ? p.setBy : "unknown",
8322
+ ...typeof p.cwd === "string" ? { cwd: p.cwd } : {}
8323
+ };
8324
+ }
8325
+ function readActivePause(sessionId, now = Date.now()) {
8326
+ if (!sessionId)
8327
+ return null;
8328
+ try {
8329
+ const path2 = pauseFilePath(sessionId);
8330
+ if (!existsSync11(path2))
8331
+ return null;
8332
+ const stored = parseStored(JSON.parse(readFileSync11(path2, "utf8")));
8333
+ if (!stored)
8334
+ return null;
8335
+ if (stored.expiresAt <= now)
8336
+ return null;
8337
+ const { schemaVersion: _ignored, ...pause } = stored;
8338
+ return pause;
8339
+ } catch {
8340
+ return null;
8341
+ }
8342
+ }
8343
+ var SCHEMA_VERSION = 1, PAUSE_DEFAULT_MS, PAUSE_CEILING_MS;
8344
+ var init_session_pause = __esm(() => {
8345
+ init_atomic_write();
8346
+ init_hook_activity_store();
8347
+ PAUSE_DEFAULT_MS = 30 * 60000;
8348
+ PAUSE_CEILING_MS = 8 * 60 * 60000;
8349
+ });
8350
+
8104
8351
  // src/hooks/handler.ts
8105
8352
  function canonicalizeEventType(raw, cli) {
8106
8353
  if (cli === "codex") {
@@ -8135,6 +8382,24 @@ function canonicalizeEventType(raw, cli) {
8135
8382
  }
8136
8383
  return raw;
8137
8384
  }
8385
+ async function runObserved(hook, ctx, hookName, eventType, cli) {
8386
+ try {
8387
+ return await Promise.race([
8388
+ hook.fn(ctx),
8389
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
8390
+ ]);
8391
+ } catch (err) {
8392
+ const msg = err instanceof Error ? err.message : String(err);
8393
+ trackHookEvent(getInstanceId(), "custom_hook_error", {
8394
+ hook_name: hookName,
8395
+ error_type: msg === "timeout" ? "timeout" : "exception",
8396
+ event_type: eventType,
8397
+ cli,
8398
+ is_observe_mode: true
8399
+ });
8400
+ return { decision: "allow" };
8401
+ }
8402
+ }
8138
8403
  async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts) {
8139
8404
  const startTime = performance.now();
8140
8405
  try {
@@ -8176,6 +8441,10 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8176
8441
  let config;
8177
8442
  let customHooksList = [];
8178
8443
  let conventionHookNames = new Set;
8444
+ let activePause = null;
8445
+ const policyAttribution = new Map;
8446
+ let cloudGeneration;
8447
+ const observedResults = [];
8179
8448
  if (opts?.forceDecision) {
8180
8449
  config = { enabledPolicies: [] };
8181
8450
  clearPolicies();
@@ -8184,23 +8453,49 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8184
8453
  } else {
8185
8454
  config = readMergedHooksConfig(session.cwd);
8186
8455
  clearPolicies();
8187
- registerBuiltinPolicies(config.enabledPolicies);
8188
- const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
8456
+ activePause = readActivePause(session.sessionId);
8457
+ registerBuiltinPolicies(activePause ? [] : config.enabledPolicies);
8458
+ const cloudManagedPolicies = readActiveCloudManagedPolicies();
8459
+ cloudGeneration = cloudManagedPolicies[0]?.generation;
8460
+ const configuredCustomPaths = config.customPoliciesPaths ?? config.customPoliciesPath;
8461
+ const allExplicitPaths = cloudManagedPolicies.length === 0 ? configuredCustomPaths : [
8462
+ ...typeof configuredCustomPaths === "string" ? [configuredCustomPaths] : configuredCustomPaths ?? [],
8463
+ ...cloudManagedPolicies.map((policy) => policy.path)
8464
+ ];
8465
+ const loadResult = await loadAllCustomHooks(allExplicitPaths, {
8189
8466
  sessionCwd: session.cwd,
8190
- customPoliciesEnabled: config.customPoliciesEnabled
8467
+ customPoliciesEnabled: config.customPoliciesEnabled,
8468
+ ...cloudManagedPolicies.length > 0 ? { cloudManagedPolicies } : {}
8191
8469
  });
8192
8470
  customHooksList = loadResult.hooks;
8193
8471
  const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
8194
8472
  conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
8195
8473
  for (const hook of customHooksList) {
8196
- const policyId = hook.__policyId;
8197
- if (policyId && disabledCustomPolicies.has(policyId))
8474
+ const taggedHook = hook;
8475
+ const policyId = taggedHook.__policyId;
8476
+ const cloudManaged = taggedHook.__cloudManaged;
8477
+ if (!cloudManaged && policyId && disabledCustomPolicies.has(policyId))
8478
+ continue;
8479
+ if (!cloudManaged && activePause)
8198
8480
  continue;
8199
8481
  const hookName = hook.name;
8200
8482
  const conventionScope = hook.__conventionScope;
8201
8483
  const isConvention = !!conventionScope;
8202
- const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
8484
+ const prefix = cloudManaged ? `cloud/${cloudManaged.id}@${cloudManaged.revision}` : isConvention ? `.failproofai-${conventionScope}` : "custom";
8485
+ const observeOnly = cloudManaged?.effect === "observe";
8203
8486
  const fn = async (ctx) => {
8487
+ if (observeOnly) {
8488
+ const shadow = await runObserved(hook, ctx, hookName, eventType, cli);
8489
+ if (shadow.decision !== "allow") {
8490
+ observedResults.push({
8491
+ policyId: cloudManaged.id,
8492
+ revision: cloudManaged.revision,
8493
+ decision: shadow.decision,
8494
+ reason: shadow.reason ?? null
8495
+ });
8496
+ }
8497
+ return { decision: "allow" };
8498
+ }
8204
8499
  try {
8205
8500
  const result2 = await Promise.race([
8206
8501
  hook.fn(ctx),
@@ -8222,7 +8517,12 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8222
8517
  return { decision: "allow" };
8223
8518
  }
8224
8519
  };
8225
- registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
8520
+ const registeredName = `${prefix}/${hookName}`;
8521
+ policyAttribution.set(registeredName, {
8522
+ source: cloudManaged ? "cloud" : isConvention ? "convention" : "custom",
8523
+ ...cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudRevision: cloudManaged.revision } : {}
8524
+ });
8525
+ registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1);
8226
8526
  }
8227
8527
  if (customHooksList.length > 0) {
8228
8528
  trackHookEvent(getInstanceId(), "custom_hooks_loaded", {
@@ -8263,7 +8563,18 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
8263
8563
  transcriptPath: session.transcriptPath,
8264
8564
  cwd: session.cwd,
8265
8565
  permissionMode: session.permissionMode,
8266
- hookEventName: session.hookEventName
8566
+ hookEventName: session.hookEventName,
8567
+ ...result.policyName ? (() => {
8568
+ const attribution = policyAttribution.get(result.policyName);
8569
+ return {
8570
+ policySource: attribution?.source ?? "builtin",
8571
+ ...attribution?.cloudPolicyId ? { cloudPolicyId: attribution.cloudPolicyId } : {},
8572
+ ...attribution?.cloudRevision !== undefined ? { cloudRevision: attribution.cloudRevision } : {}
8573
+ };
8574
+ })() : {},
8575
+ ...cloudGeneration !== undefined ? { cloudGeneration } : {},
8576
+ ...observedResults.length > 0 ? { observed: observedResults } : {},
8577
+ ...activePause ? { pausedBy: activePause.setBy, pauseExpiresAt: activePause.expiresAt } : {}
8267
8578
  };
8268
8579
  try {
8269
8580
  persistHookActivity(activityEntry);
@@ -8315,6 +8626,8 @@ var init_handler = __esm(() => {
8315
8626
  init_resolve_transcript_path();
8316
8627
  init_telemetry_id();
8317
8628
  init_hook_logger();
8629
+ init_cloud_managed_policies();
8630
+ init_session_pause();
8318
8631
  });
8319
8632
 
8320
8633
  // src/hooks/worker-server.ts
@@ -8323,7 +8636,7 @@ __export(exports_worker_server, {
8323
8636
  startWorkerServer: () => startWorkerServer
8324
8637
  });
8325
8638
  import { createServer } from "node:net";
8326
- import { existsSync as existsSync10, unlinkSync as unlinkSync2 } from "node:fs";
8639
+ import { existsSync as existsSync12, unlinkSync as unlinkSync2 } from "node:fs";
8327
8640
  function isWorkerHookRequest(msg) {
8328
8641
  if (!msg || typeof msg !== "object")
8329
8642
  return false;
@@ -8395,7 +8708,7 @@ function handleConnection(socket) {
8395
8708
  socket.on("error", () => {});
8396
8709
  }
8397
8710
  function startWorkerServer(socketPath) {
8398
- if (existsSync10(socketPath)) {
8711
+ if (existsSync12(socketPath)) {
8399
8712
  try {
8400
8713
  unlinkSync2(socketPath);
8401
8714
  } catch {}
@@ -8413,14 +8726,14 @@ var init_worker_server = __esm(() => {
8413
8726
  });
8414
8727
 
8415
8728
  // bin/failproofai-worker.mjs
8416
- import { realpathSync } from "node:fs";
8417
- import { dirname as dirname5, resolve as resolve10 } from "node:path";
8729
+ import { realpathSync as realpathSync2 } from "node:fs";
8730
+ import { dirname as dirname5, resolve as resolve12 } from "node:path";
8418
8731
  import { fileURLToPath } from "node:url";
8419
8732
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
8420
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve10(dirname5(realpathSync(fileURLToPath(import.meta.url))), "..");
8733
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve12(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..");
8421
8734
  }
8422
8735
  if (!process.env.FAILPROOFAI_DIST_PATH) {
8423
- process.env.FAILPROOFAI_DIST_PATH = resolve10(dirname5(realpathSync(fileURLToPath(import.meta.url))), "..", "dist");
8736
+ process.env.FAILPROOFAI_DIST_PATH = resolve12(dirname5(realpathSync2(fileURLToPath(import.meta.url))), "..", "dist");
8424
8737
  }
8425
8738
  var socketPath = process.env.FAILPROOFAI_WORKER_SOCKET;
8426
8739
  if (!socketPath) {