failproofai 1.0.12-beta.1 → 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 (168) 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.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  30. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/index.html +1 -1
  33. package/.next/standalone/.next/server/app/index.rsc +3 -3
  34. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  35. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  36. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  37. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  38. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  41. package/.next/standalone/.next/server/app/policies/page.js +2 -2
  42. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  52. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  55. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
  58. package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
  59. package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
  60. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
  61. package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +1 -1
  62. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00l2u6j86cb8y._.js +3 -0
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pwj2h2efe5f-._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0b78b0j8iyv6l._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/_0bseae9u7g98r._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/ssr/_0mouhaflye1ro._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/ssr/{_1a3tubds7s7ow._.js → _0ouu886szh6yj._.js} +4 -4
  78. package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/_1fw0_nzkae5ao._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/ssr/_20hn4gn87nsjc._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
  83. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr0vt64h._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
  88. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1i8oid1gc7s1f.js → node_modules_html-to-image_es_index_1fv03uigni8s5.js} +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_0janj0i6dqko7._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
  96. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  97. package/.next/standalone/.next/server/pages/404.html +1 -1
  98. package/.next/standalone/.next/server/pages/500.html +1 -1
  99. package/.next/standalone/.next/server/preview-props.json +3 -3
  100. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  102. package/.next/standalone/.next/static/chunks/{2-tf6_7u623kd.js → 0ay__-d2h1xb5.js} +1 -1
  103. package/.next/standalone/.next/static/chunks/{18433qz-8njv3.js → 0tbm3vjm62h-2.js} +1 -1
  104. package/.next/standalone/.next/static/chunks/{1b32-afqacaen.js → 1c_jf6g5wnslk.js} +1 -1
  105. package/.next/standalone/.next/static/chunks/{0_zfad4co-t2z.js → 1my3-5p47t7r-.js} +1 -1
  106. package/.next/standalone/.next/static/chunks/{0jutamwqz8pg5.js → 20py8k1gd_i1m.js} +1 -1
  107. package/.next/standalone/.next/static/chunks/21hckpmsc4g70.js +6 -0
  108. package/.next/standalone/.next/static/chunks/2hmex93ppj1ke.js +1 -0
  109. package/.next/standalone/.next/static/chunks/{42e8zab314654.js → 2o6d37j425uci.js} +1 -1
  110. package/.next/standalone/.next/static/chunks/40oworn_-x12p.js +1 -0
  111. package/.next/standalone/.next/static/chunks/{24w3x2da9sk49.js → 42xptz88mtq3x.js} +1 -1
  112. package/.next/standalone/app/actions/update-jev-config.ts +5 -3
  113. package/.next/standalone/app/settings/jev-panel.tsx +1 -1
  114. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +9 -18
  115. package/.next/standalone/fp-cloud-cli/README.md +7 -9
  116. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1 -27
  117. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +12 -216
  118. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +7 -53
  119. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +2 -71
  120. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +1 -1
  121. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +6 -7
  122. package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +32 -197
  123. package/.next/standalone/hermes-plugin/__init__.py +11 -0
  124. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  125. package/.next/standalone/package.json +11 -10
  126. package/.next/standalone/sdk/python/uv.lock +31 -8
  127. package/.next/standalone/server.js +1 -1
  128. package/dist/cli.mjs +1686 -612
  129. package/dist/worker.mjs +354 -49
  130. package/hermes-plugin/__init__.py +11 -0
  131. package/hermes-plugin/plugin.yaml +1 -1
  132. package/package.json +11 -10
  133. package/src/audit/open-browser.ts +24 -7
  134. package/src/hooks/agent-roster.ts +9 -1
  135. package/src/hooks/agent-skill.ts +203 -0
  136. package/src/hooks/cloud-connection.ts +27 -51
  137. package/src/hooks/cloud-introspect.ts +5 -2
  138. package/src/hooks/cloud-login.ts +248 -0
  139. package/src/hooks/cloud-managed-policies.ts +54 -4
  140. package/src/hooks/codex-system-hooks.ts +10 -4
  141. package/src/hooks/config-status.ts +9 -3
  142. package/src/hooks/configure-wizard.ts +356 -32
  143. package/src/hooks/delivery-health.ts +47 -2
  144. package/src/hooks/effective-reviewers.ts +14 -0
  145. package/src/hooks/fp-config.ts +19 -0
  146. package/src/hooks/handler.ts +21 -2
  147. package/src/hooks/hook-activity-store.ts +7 -1
  148. package/src/hooks/integrations.ts +18 -3
  149. package/src/hooks/jev-activity.ts +117 -1
  150. package/src/hooks/jev-cli.ts +7 -9
  151. package/src/hooks/manager.ts +80 -46
  152. package/src/hooks/policy-evaluator.ts +17 -0
  153. package/src/hooks/policy-reviewability.ts +4 -2
  154. package/src/hooks/semantic/cloud-jev.ts +219 -28
  155. package/src/hooks/semantic/combine.ts +26 -1
  156. package/src/hooks/semantic/jev-client.ts +1 -1
  157. package/src/hooks/semantic/jev-config.ts +6 -27
  158. package/src/hooks/semantic/jev-review.ts +90 -2
  159. package/src/hooks/tui.ts +280 -0
  160. package/src/hooks/uninstall-cli.ts +12 -1
  161. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +0 -3
  162. package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +0 -3
  163. package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +0 -1
  164. package/.next/standalone/.next/static/chunks/1p-hsi58i-6h0.js +0 -6
  165. package/.next/standalone/.next/static/chunks/3-pku9qwv1yfu.js +0 -1
  166. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → LHCtFxVG0mdvd4lsXAOFC}/_buildManifest.js +0 -0
  167. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → LHCtFxVG0mdvd4lsXAOFC}/_clientMiddlewareManifest.js +0 -0
  168. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → LHCtFxVG0mdvd4lsXAOFC}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -29,7 +29,7 @@ var __esm = (fn, res, err) => () => {
29
29
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
30
 
31
31
  // package.json
32
- var version2 = "1.0.12-beta.1", package_default;
32
+ var version2 = "1.0.12-beta.2", package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "failproofai",
@@ -151,23 +151,24 @@ var init_package = __esm(() => {
151
151
  postcss: "8.5.26",
152
152
  "eslint-plugin-react-hooks": "7.0.1",
153
153
  vite: "8.1.6",
154
- undici: "7.29.1",
154
+ undici: "7.30.1",
155
+ zod: "4.6.6",
155
156
  "brace-expansion": "5.0.12",
156
157
  sharp: "0.35.5",
157
158
  "source-map-js": "1.2.2",
158
159
  browserslist: "4.28.8"
159
160
  },
160
161
  optionalDependencies: {
161
- "@failproofai/failproofaid-linux-x64": "1.0.12-beta.1",
162
- "@failproofai/failproofaid-linux-arm64": "1.0.12-beta.1",
163
- "@failproofai/failproofaid-darwin-x64": "1.0.12-beta.1",
164
- "@failproofai/failproofaid-darwin-arm64": "1.0.12-beta.1"
162
+ "@failproofai/failproofaid-linux-x64": "1.0.12-beta.2",
163
+ "@failproofai/failproofaid-linux-arm64": "1.0.12-beta.2",
164
+ "@failproofai/failproofaid-darwin-x64": "1.0.12-beta.2",
165
+ "@failproofai/failproofaid-darwin-arm64": "1.0.12-beta.2"
165
166
  },
166
167
  failproofaidBinaries: {
167
- "linux-x64": "df15e65b60066248038c8ac6d423bffcb0e38637e8a42fa22250afa111fd46ac",
168
- "linux-arm64": "59d7ae758c8981c5e0a89eb64be18ebd278213525dd4447891a51be6bb018ed9",
169
- "darwin-x64": "b0d3d39abaa02ed0a14dddc68277975a7a23e200830b7edbffdedde3af81ce73",
170
- "darwin-arm64": "a6d343b5926e84c0eb437af307f85b69b3128f94c6e34acbf9d0b13a7c7e9d63"
168
+ "linux-x64": "4c09117fdb80d87792d1d2b77d9359cdbd0c522b5a6222ab7b927659130ddf54",
169
+ "linux-arm64": "dd390d76561267fc519ceb96fb90be340044e2f564bef36a44e284f227d93f29",
170
+ "darwin-x64": "62381f7ffe21d02289c36c4b205166b2867dbde986444d1ea185241b94fd04ef",
171
+ "darwin-arm64": "9ad798817d3048a5126e80dc71883538d37c1b6b6443e9c19389dc2583c8f675"
171
172
  }
172
173
  };
173
174
  });
@@ -607,6 +608,14 @@ function readCredentials() {
607
608
  return {};
608
609
  }
609
610
  }
611
+ function cloudManagesJevMode() {
612
+ try {
613
+ const cloud = readCredentials().cloud;
614
+ return Boolean(cloud?.url && cloud.machineId);
615
+ } catch {
616
+ return false;
617
+ }
618
+ }
610
619
  function projectCredentials(parsed) {
611
620
  {
612
621
  const cloud = parsed.cloud;
@@ -816,7 +825,7 @@ function writeFileAt(path, contents) {
816
825
  mkdirSync2(dirname2(path), { recursive: true });
817
826
  writeFileSync2(path, contents, "utf8");
818
827
  }
819
- var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG, OWNED_CONFIG_KEYS, isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v), OWNED_CREDENTIAL_KEYS, MAX_CREDENTIALS_BYTES, credentialsOpenFlags = () => fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0), DIR_WRITABLE_BY_OTHERS = 18;
828
+ var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG, OWNED_CONFIG_KEYS, isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v), OWNED_CREDENTIAL_KEYS, CLOUD_MANAGED_JEV_MODE = "Jev mode is managed by FailproofAI Cloud on this machine: enforce while a Jev policy is deployed here, off when none is.", MAX_CREDENTIALS_BYTES, credentialsOpenFlags = () => fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0), DIR_WRITABLE_BY_OTHERS = 18;
820
829
  var init_fp_config = __esm(() => {
821
830
  init_atomic_write();
822
831
  init_package();
@@ -912,6 +921,57 @@ function normalizeJevFallbackReason(raw) {
912
921
  const code = JEV_FREE_TEXT_PREFIXES.get(head) ?? head;
913
922
  return JEV_REASON_CODES.has(code) ? code : JEV_REASON_OTHER;
914
923
  }
924
+ function sanitizeCheck(raw) {
925
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
926
+ return null;
927
+ const r = raw;
928
+ if (typeof r.check !== "string" || !JEV_CHECK_NAME_RE.test(r.check))
929
+ return null;
930
+ if (typeof r.verdict !== "string" || !CHECK_VERDICT_SET.has(r.verdict))
931
+ return null;
932
+ if (typeof r.why !== "string" || !CHECK_WHY_SET.has(r.why))
933
+ return null;
934
+ if (!isP(r.p))
935
+ return null;
936
+ const out = {
937
+ check: r.check,
938
+ verdict: r.verdict,
939
+ why: r.why,
940
+ p: round2(r.p)
941
+ };
942
+ if (typeof r.policy === "string" && isJevPolicyName(r.policy) && !/\s/.test(r.policy) && /^(?:cloud|pack)\//.test(r.policy)) {
943
+ out.policy = r.policy;
944
+ }
945
+ if (isP(r.exempt))
946
+ out.exempt = round2(r.exempt);
947
+ if (r.watched === true)
948
+ out.watched = true;
949
+ if (r.probes && typeof r.probes === "object" && !Array.isArray(r.probes)) {
950
+ const probes = {};
951
+ for (const [id, v] of Object.entries(r.probes)) {
952
+ if (Object.keys(probes).length >= JEV_CHECK_PROBES_MAX)
953
+ break;
954
+ if (JEV_PROBE_ID_RE.test(id) && isP(v))
955
+ probes[id] = round2(v);
956
+ }
957
+ if (Object.keys(probes).length > 0)
958
+ out.probes = probes;
959
+ }
960
+ return out;
961
+ }
962
+ function sanitizeJevChecks(raw) {
963
+ if (!Array.isArray(raw))
964
+ return;
965
+ const out = [];
966
+ for (const item of raw) {
967
+ if (out.length >= JEV_CHECKS_MAX)
968
+ break;
969
+ const rec = sanitizeCheck(item);
970
+ if (rec)
971
+ out.push(rec);
972
+ }
973
+ return raw.length > 0 && out.length === 0 ? undefined : out;
974
+ }
915
975
  function sanitizeJevActivity(entry) {
916
976
  if (!JEV_ACTIVITY_KEYS.some((k) => entry[k] !== undefined))
917
977
  return entry;
@@ -950,6 +1010,11 @@ function sanitizeJevActivity(entry) {
950
1010
  out.jevModel = out.jevModel.trim();
951
1011
  else
952
1012
  drop("jevModel");
1013
+ const checks = out.evaluator === "jev" ? sanitizeJevChecks(out.jevChecks) : undefined;
1014
+ if (checks === undefined)
1015
+ drop("jevChecks");
1016
+ else
1017
+ out.jevChecks = checks;
953
1018
  return out;
954
1019
  }
955
1020
  function jevOutcome(raw) {
@@ -960,6 +1025,8 @@ function outcomeOf(e) {
960
1025
  return "fallback";
961
1026
  if (e.evaluator !== "jev")
962
1027
  return null;
1028
+ if (Array.isArray(e.jevChecks) && e.jevChecks.length === 0)
1029
+ return "no-request";
963
1030
  if (e.jevCleared !== undefined || e.jevLatencyMs !== undefined || e.jevModel !== undefined)
964
1031
  return "answered";
965
1032
  if (e.jevDecision === "deny" || e.jevDecision === "instruct")
@@ -968,7 +1035,7 @@ function outcomeOf(e) {
968
1035
  return "no-request";
969
1036
  return "not-consulted";
970
1037
  }
971
- 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;
1038
+ 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;
972
1039
  var init_jev_activity = __esm(() => {
973
1040
  JEV_ACTIVITY_KEYS = [
974
1041
  "evaluator",
@@ -977,7 +1044,8 @@ var init_jev_activity = __esm(() => {
977
1044
  "jevFallbackReason",
978
1045
  "jevLatencyMs",
979
1046
  "jevModel",
980
- "jevMode"
1047
+ "jevMode",
1048
+ "jevChecks"
981
1049
  ];
982
1050
  EVALUATORS = new Set(["jev", "jev-fallback"]);
983
1051
  DECISIONS = new Set(["allow", "instruct", "deny"]);
@@ -1024,6 +1092,22 @@ var init_jev_activity = __esm(() => {
1024
1092
  JEV_MODEL_RE = /^[A-Za-z0-9._:/@~+-]{1,200}$/;
1025
1093
  NAME_FORBIDDEN_RE = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
1026
1094
  REGISTERED_NAMESPACE_RE = /^(?:custom|pack|cloud|failproofai|\.failproofai-[A-Za-z0-9_-]{1,32})\//;
1095
+ JEV_CHECK_VERDICTS = ["none", "deny", "instruct", "overridden"];
1096
+ JEV_CHECK_WHY = [
1097
+ "not-detected",
1098
+ "exempt",
1099
+ "fired",
1100
+ "injection",
1101
+ "user-asked",
1102
+ "op-requested",
1103
+ "task-step",
1104
+ "downgraded",
1105
+ "scan-incomplete"
1106
+ ];
1107
+ JEV_CHECK_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
1108
+ JEV_PROBE_ID_RE = /^[a-z][a-z0-9_]{0,31}$/;
1109
+ CHECK_VERDICT_SET = new Set(JEV_CHECK_VERDICTS);
1110
+ CHECK_WHY_SET = new Set(JEV_CHECK_WHY);
1027
1111
  ANSWERED_BUT_NOT_APPLIED_REASONS = new Set([
1028
1112
  JEV_REASON_REQUEST_CUT,
1029
1113
  "truncated",
@@ -1625,7 +1709,13 @@ function runtimeAgentSettingsPath(cli, cwd, userHome = homedir2(), hookScope) {
1625
1709
  if (config?.trim())
1626
1710
  return resolve2(config);
1627
1711
  const state = process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_HOME;
1628
- return resolve2(state?.trim() ? state : resolve2(userHome, ".openclaw"), "openclaw.json");
1712
+ if (state?.trim())
1713
+ return resolve2(state, "openclaw.json");
1714
+ const profile = process.env.OPENCLAW_PROFILE?.trim();
1715
+ if (profile && profile !== "default" && /^[A-Za-z0-9._-]{1,80}$/.test(profile) && !profile.startsWith(".")) {
1716
+ return resolve2(userHome, `.openclaw-${profile}`, "openclaw.json");
1717
+ }
1718
+ return resolve2(userHome, ".openclaw", "openclaw.json");
1629
1719
  }
1630
1720
  const relative = USER_SETTINGS[cli];
1631
1721
  if (!relative)
@@ -3049,6 +3139,40 @@ function readCloudJevState() {
3049
3139
  const deployment = Number.isSafeInteger(active.deployment) ? active.deployment : null;
3050
3140
  return { jevMode, deployment, errors };
3051
3141
  }
3142
+ function readCloudJevAssignments() {
3143
+ try {
3144
+ const active = readActiveRaw();
3145
+ if (!active || !Array.isArray(active.jevPolicies))
3146
+ return null;
3147
+ const schema = active.schemaVersion;
3148
+ const out = [];
3149
+ for (const raw of active.jevPolicies) {
3150
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
3151
+ return null;
3152
+ const entry = raw;
3153
+ if (typeof entry.id !== "string" || !POLICY_ID_RE.test(entry.id))
3154
+ return null;
3155
+ if (!Number.isSafeInteger(entry.version) || entry.version < 0)
3156
+ return null;
3157
+ if (entry.effect !== undefined && entry.effect !== "enforce" && entry.effect !== "observe")
3158
+ return null;
3159
+ const targets = parseAgentTargets(entry.agentTargets, schema);
3160
+ out.push({
3161
+ id: entry.id,
3162
+ version: entry.version,
3163
+ effect: entry.effect === "observe" ? "observe" : "enforce",
3164
+ ...targets ? { agentTargets: targets } : {}
3165
+ });
3166
+ }
3167
+ return out;
3168
+ } catch {
3169
+ return null;
3170
+ }
3171
+ }
3172
+ function cloudJevAssignmentsFor(agent) {
3173
+ const all = readCloudJevAssignments();
3174
+ return all === null ? null : all.filter((a) => agentTargetsMatch(a.agentTargets, agent));
3175
+ }
3052
3176
  function cloudReviewerName(policyId, name) {
3053
3177
  return `cloud:${policyId}/${name}`;
3054
3178
  }
@@ -3693,9 +3817,11 @@ function effectiveReviewerNames() {
3693
3817
  return cached;
3694
3818
  let names = NO_REVIEWERS;
3695
3819
  cachedContested = new Map;
3820
+ cachedPackChecks = false;
3696
3821
  try {
3697
3822
  const packs = jevPacks(readInstalledPacks().packs, reviewerCli);
3698
3823
  names = reviewerNamesFor(packs);
3824
+ cachedPackChecks = names.size > 0;
3699
3825
  cachedContested = contestedSemanticNames(packs);
3700
3826
  } catch {}
3701
3827
  try {
@@ -3713,12 +3839,9 @@ function contestedReviewerNames() {
3713
3839
  function jevChecksInstalled() {
3714
3840
  return effectiveReviewerNames().size > 0;
3715
3841
  }
3716
- function jevChecksDeclared() {
3717
- try {
3718
- return reviewerNamesFor(jevPacks(readInstalledPacks().packs)).size > 0;
3719
- } catch {
3720
- return false;
3721
- }
3842
+ function packJevChecksInstalled() {
3843
+ effectiveReviewerNames();
3844
+ return cachedPackChecks;
3722
3845
  }
3723
3846
  function reviewerNamesFor(packs) {
3724
3847
  const contested = contestedSemanticNames(packs);
@@ -3732,7 +3855,7 @@ function forgetEffectiveReviewerNames(cli, agent = null) {
3732
3855
  reviewerCli = cli;
3733
3856
  reviewerAgent = agent;
3734
3857
  }
3735
- var cached = null, cachedContested, reviewerCli, reviewerAgent = null;
3858
+ var cached = null, cachedPackChecks = false, cachedContested, reviewerCli, reviewerAgent = null;
3736
3859
  var init_effective_reviewers = __esm(() => {
3737
3860
  init_pack_manifest();
3738
3861
  init_policy_authority();
@@ -14299,11 +14422,15 @@ function removeCodexSystemHooks(deps = {}) {
14299
14422
  }
14300
14423
  return { status: "removed", path, hooks };
14301
14424
  }
14425
+ function codexHome() {
14426
+ const dir = process.env.CODEX_HOME?.trim();
14427
+ return dir ? resolve9(dir) : resolve9(homedir6(), ".codex");
14428
+ }
14302
14429
  function codexHookTrust(deps = {}) {
14303
14430
  const systemPath = resolvedPath(deps);
14304
14431
  if (systemPath && codexSystemHooksPresent({ path: systemPath }))
14305
14432
  return { kind: "system", path: systemPath };
14306
- const hooksJsonPath = deps.hooksJsonPath ?? resolve9(homedir6(), ".codex", "hooks.json");
14433
+ const hooksJsonPath = deps.hooksJsonPath ?? resolve9(codexHome(), "hooks.json");
14307
14434
  const ours = failproofaiCodexHookIdentities(hooksJsonPath);
14308
14435
  if (ours.length === 0)
14309
14436
  return { kind: "none" };
@@ -14312,7 +14439,7 @@ function codexHookTrust(deps = {}) {
14312
14439
  return { kind: "ungated", path: hooksJsonPath, version };
14313
14440
  let trustedHashes = new Map;
14314
14441
  try {
14315
- trustedHashes = parseCodexTrustedHashes(readFileSync8(deps.userConfigPath ?? resolve9(homedir6(), ".codex", "config.toml"), "utf8"));
14442
+ trustedHashes = parseCodexTrustedHashes(readFileSync8(deps.userConfigPath ?? resolve9(codexHome(), "config.toml"), "utf8"));
14316
14443
  } catch {}
14317
14444
  const untrusted = ours.filter((hook) => trustedHashes.get(hook.key) !== hook.hash).length;
14318
14445
  return untrusted === 0 ? { kind: "trusted", path: hooksJsonPath } : { kind: "untrusted", path: hooksJsonPath, untrusted, total: ours.length };
@@ -14788,6 +14915,10 @@ function settingsPathsFor(integration, scope, cwd) {
14788
14915
  const paths = integration.getSettingsPaths?.(scope, cwd);
14789
14916
  return paths && paths.length > 0 ? paths : [integration.getSettingsPath(scope, cwd)];
14790
14917
  }
14918
+ function claudeConfigDir() {
14919
+ const dir = process.env.CLAUDE_CONFIG_DIR?.trim();
14920
+ return dir ? resolve12(dir) : resolve12(homedir9(), ".claude");
14921
+ }
14791
14922
  function codexSystemHookEntries(binaryPath) {
14792
14923
  return CODEX_HOOK_EVENT_TYPES.map((eventType) => {
14793
14924
  const entry = codex.buildHookEntry(binaryPath, eventType, "user");
@@ -15490,7 +15621,7 @@ var init_integrations = __esm(() => {
15490
15621
  const base = cwd ? resolve12(cwd) : process.cwd();
15491
15622
  switch (scope) {
15492
15623
  case "user":
15493
- return resolve12(homedir9(), ".claude", "settings.json");
15624
+ return resolve12(claudeConfigDir(), "settings.json");
15494
15625
  case "project":
15495
15626
  return resolve12(base, ".claude", "settings.json");
15496
15627
  case "local":
@@ -15622,7 +15753,7 @@ var init_integrations = __esm(() => {
15622
15753
  const base = cwd ? resolve12(cwd) : process.cwd();
15623
15754
  switch (scope) {
15624
15755
  case "user":
15625
- return resolve12(homedir9(), ".codex", "hooks.json");
15756
+ return resolve12(codexHome(), "hooks.json");
15626
15757
  case "project":
15627
15758
  return resolve12(base, ".codex", "hooks.json");
15628
15759
  case "local":
@@ -20503,6 +20634,204 @@ function promptCloudKey(opts) {
20503
20634
  });
20504
20635
  });
20505
20636
  }
20637
+ function slowedDown(interval) {
20638
+ return Math.max(interval, Math.min(10, interval + 2));
20639
+ }
20640
+ function promptBrowserLogin(opts) {
20641
+ const stdin = opts.stdin ?? process.stdin;
20642
+ const stdout = opts.stdout ?? process.stdout;
20643
+ if (!stdin.isTTY || !stdout.isTTY)
20644
+ return Promise.resolve(null);
20645
+ const now = opts.now ?? Date.now;
20646
+ const c = paint(colorsEnabled(stdout));
20647
+ const kit = screenKit({ cols: stdout.columns || 80, color: colorsEnabled(stdout) });
20648
+ const spinnerFrames = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
20649
+ const region = { lastCount: 0 };
20650
+ let phase = "starting";
20651
+ let failLine = "";
20652
+ let cautionLine = "";
20653
+ let expiresAt = 0;
20654
+ let interval = 2;
20655
+ let frame = 0;
20656
+ let settled = false;
20657
+ let headingShown = false;
20658
+ const above = [];
20659
+ let pollTimer = null;
20660
+ let spinTimer = null;
20661
+ let attempt = 0;
20662
+ const countdown = () => {
20663
+ const left = Math.max(0, Math.ceil((expiresAt - now()) / 1000));
20664
+ return `${Math.floor(left / 60)}:${String(left % 60).padStart(2, "0")}`;
20665
+ };
20666
+ const build = () => {
20667
+ const spin = c.ink3(spinnerFrames[frame % spinnerFrames.length]);
20668
+ switch (phase) {
20669
+ case "starting":
20670
+ return [`${INDENT}${spin} Getting a link…`];
20671
+ case "waiting":
20672
+ return [
20673
+ `${INDENT}${spin} Waiting for you to authorize in the browser ${c.ink3(`· expires in ${countdown()}`)}`,
20674
+ ...cautionLine ? [kit.caution(cautionLine)] : [],
20675
+ "",
20676
+ kit.keys(["tab more options", "ctrl-c cancel"])
20677
+ ];
20678
+ case "expired":
20679
+ return [kit.fail("That link expired."), "", kit.keys(["enter new link", "tab more options"])];
20680
+ case "denied":
20681
+ return [kit.fail("Not authorized in the browser."), "", kit.keys(["enter try again", "tab more options"])];
20682
+ case "failed":
20683
+ return [kit.fail(failLine), "", kit.keys(["enter try again", "tab more options"])];
20684
+ }
20685
+ };
20686
+ const draw = () => {
20687
+ if (settled)
20688
+ return;
20689
+ repaint(stdout, region, headingShown ? build() : [promptHeading(c, opts.message, opts.meta), ...build()]);
20690
+ };
20691
+ const printAbove = (lines) => {
20692
+ const clear = region.lastCount > 0 ? `${ESC}[${region.lastCount}A${ESC}[J` : "";
20693
+ stdout.write(`${clear}${lines.join(`
20694
+ `)}
20695
+ `);
20696
+ region.lastCount = 0;
20697
+ above.push(...lines);
20698
+ };
20699
+ const forgetAbove = () => {
20700
+ const cols = stdout.columns || 80;
20701
+ for (const line of above)
20702
+ region.lastCount += Math.max(1, Math.ceil(visibleWidth(line) / cols));
20703
+ above.length = 0;
20704
+ headingShown = false;
20705
+ };
20706
+ const clearPoll = () => {
20707
+ if (pollTimer)
20708
+ clearTimeout(pollTimer);
20709
+ pollTimer = null;
20710
+ };
20711
+ const restoreTerminal = () => showCursor(stdout);
20712
+ return new Promise((resolve) => {
20713
+ const wasRaw = stdin.isRaw;
20714
+ const finish = (answer) => {
20715
+ if (settled)
20716
+ return;
20717
+ clearPoll();
20718
+ if (spinTimer)
20719
+ clearInterval(spinTimer);
20720
+ stdin.removeListener("keypress", onKey);
20721
+ stdin.setRawMode?.(wasRaw ?? false);
20722
+ stdin.pause();
20723
+ process.removeListener("exit", restoreTerminal);
20724
+ if (answer === BACK)
20725
+ forgetAbove();
20726
+ const lines = answer === null ? [c.ink3("cancelled")] : answer === BACK ? [] : opts.collapsed(answer.value);
20727
+ repaint(stdout, region, lines, { cut: false });
20728
+ settled = true;
20729
+ restoreTerminal();
20730
+ resolve(answer);
20731
+ };
20732
+ const schedulePoll = (forAttempt) => {
20733
+ clearPoll();
20734
+ pollTimer = setTimeout(() => void pollOnce(forAttempt), interval * 1000);
20735
+ };
20736
+ const pollOnce = async (forAttempt) => {
20737
+ let answer;
20738
+ try {
20739
+ answer = await opts.poll();
20740
+ } catch (err) {
20741
+ answer = { state: "unreachable", line: err instanceof Error ? err.message : String(err) };
20742
+ }
20743
+ if (settled || forAttempt !== attempt)
20744
+ return;
20745
+ switch (answer.state) {
20746
+ case "approved":
20747
+ return finish({ value: answer.value });
20748
+ case "denied":
20749
+ phase = "denied";
20750
+ return draw();
20751
+ case "expired":
20752
+ phase = "expired";
20753
+ return draw();
20754
+ case "slow_down":
20755
+ interval = slowedDown(interval);
20756
+ break;
20757
+ case "unreachable":
20758
+ cautionLine = `Can't reach the server right now (${answer.line}) — still trying.`;
20759
+ break;
20760
+ case "pending":
20761
+ cautionLine = "";
20762
+ if (answer.interval && answer.interval > 0)
20763
+ interval = answer.interval;
20764
+ break;
20765
+ }
20766
+ if (now() > expiresAt + interval * 1000) {
20767
+ phase = "expired";
20768
+ return draw();
20769
+ }
20770
+ draw();
20771
+ schedulePoll(forAttempt);
20772
+ };
20773
+ const begin = async () => {
20774
+ const mine = ++attempt;
20775
+ clearPoll();
20776
+ cautionLine = "";
20777
+ phase = "starting";
20778
+ forgetAbove();
20779
+ draw();
20780
+ let started;
20781
+ try {
20782
+ started = await opts.start();
20783
+ } catch (err) {
20784
+ started = { ok: false, line: err instanceof Error ? err.message : String(err), retryable: true };
20785
+ }
20786
+ if (settled || mine !== attempt)
20787
+ return;
20788
+ if (!started.ok) {
20789
+ if (!started.retryable)
20790
+ return finish(BACK);
20791
+ phase = "failed";
20792
+ failLine = started.line;
20793
+ return draw();
20794
+ }
20795
+ expiresAt = now() + started.expiresIn * 1000;
20796
+ interval = started.interval > 0 ? started.interval : 2;
20797
+ const opened = opts.open(started.url);
20798
+ printAbove([
20799
+ ...headingShown ? [] : [promptHeading(c, opts.message, opts.meta)],
20800
+ opened ? `${INDENT}Opening your browser to connect ${opts.machineLabel}. If it doesn't open, use this link:` : `${INDENT}Open this link in any browser to connect ${opts.machineLabel}:`,
20801
+ `${INDENT}${c.bold(started.url)}`,
20802
+ ""
20803
+ ]);
20804
+ headingShown = true;
20805
+ phase = "waiting";
20806
+ draw();
20807
+ schedulePoll(mine);
20808
+ };
20809
+ function onKey(_str, key) {
20810
+ if (!key || settled)
20811
+ return;
20812
+ if (key.ctrl && (key.name === "c" || key.name === "d"))
20813
+ return finish(null);
20814
+ if (key.name === "escape" || key.name === "tab")
20815
+ return finish(BACK);
20816
+ if ((key.name === "return" || key.name === "enter") && phase !== "waiting" && phase !== "starting") {
20817
+ begin();
20818
+ }
20819
+ }
20820
+ process.once("exit", restoreTerminal);
20821
+ readline.emitKeypressEvents(stdin);
20822
+ stdin.setRawMode?.(true);
20823
+ stdin.resume();
20824
+ hideCursor(stdout);
20825
+ stdin.on("keypress", onKey);
20826
+ spinTimer = setInterval(() => {
20827
+ frame++;
20828
+ if (phase === "waiting" || phase === "starting")
20829
+ draw();
20830
+ }, 120);
20831
+ spinTimer.unref?.();
20832
+ begin();
20833
+ });
20834
+ }
20506
20835
  function ctx(opts) {
20507
20836
  return { cols: Math.max(20, opts?.cols ?? 80), c: paint(opts?.color ?? false) };
20508
20837
  }
@@ -21746,7 +22075,8 @@ function combineTwoTier(verdicts, review, mode) {
21746
22075
  ...cleared.length > 0 && RANK[enforced.final.decision] < RANK[legacy.decision] ? { jevCleared: cleared } : {},
21747
22076
  ...review.latencyMs !== null ? { jevLatencyMs: review.latencyMs } : {},
21748
22077
  ...review.model ? { jevModel: review.model } : {},
21749
- jevMode: mode
22078
+ jevMode: mode,
22079
+ ...review.checks ? { jevChecks: [...review.checks] } : {}
21750
22080
  };
21751
22081
  if (mode === "observe") {
21752
22082
  const observeVerdict = review.decision === "deny" || review.decision === "instruct" ? {
@@ -21847,6 +22177,12 @@ async function evaluatePolicies(eventType, payload, session, config, twoTier) {
21847
22177
  twoTier.releaseRegistry?.();
21848
22178
  review = await twoTier.review;
21849
22179
  }
22180
+ if (review.kind === "answered" && review.asked.length === 0 && review.decision === "allow" && !review.observedAsked?.length) {
22181
+ return formatVerdict(eventType, session, toolName, regexOnly(collected.verdicts));
22182
+ }
22183
+ if (review.kind === "not-consulted" && twoTier.cloud) {
22184
+ return formatVerdict(eventType, session, toolName, regexOnly(collected.verdicts));
22185
+ }
21850
22186
  const combined = combineTwoTier(collected.verdicts, review, twoTier.mode);
21851
22187
  if (combined.cleared.length > 0) {
21852
22188
  hookLogInfo(`jev ${twoTier.mode === "observe" ? "would clear" : "cleared"}: ${combined.cleared.join(", ")}`);
@@ -21860,7 +22196,8 @@ async function evaluatePolicies(eventType, payload, session, config, twoTier) {
21860
22196
  activity: combined.activity,
21861
22197
  decidedByJev: combined.decidedByJev,
21862
22198
  ...(combined.decidedByJev || combined.observeVerdict) && review.kind === "answered" && review.origin ? { jevOrigin: review.origin } : {},
21863
- ...combined.observeVerdict ? { observeVerdict: combined.observeVerdict } : {}
22199
+ ...combined.observeVerdict ? { observeVerdict: combined.observeVerdict } : {},
22200
+ ...review.kind === "answered" && review.observedCloud?.length ? { observedCloud: review.observedCloud } : {}
21864
22201
  }
21865
22202
  };
21866
22203
  }
@@ -22862,7 +23199,7 @@ var init_hook_activity_store = __esm(() => {
22862
23199
  });
22863
23200
 
22864
23201
  // src/hooks/delivery-health.ts
22865
- import { existsSync as existsSync19, readdirSync as readdirSync6, statSync as statSync12 } from "node:fs";
23202
+ import { existsSync as existsSync19, readFileSync as readFileSync12, readdirSync as readdirSync6, statSync as statSync12 } from "node:fs";
22866
23203
  import { join as join8 } from "node:path";
22867
23204
  function parseParkedName(name) {
22868
23205
  const poison = name.endsWith(POISON_SUFFIX);
@@ -22889,7 +23226,16 @@ function parseParkedName(name) {
22889
23226
  }
22890
23227
  return { base, attempt, clientStatus, poison };
22891
23228
  }
22892
- function deliveryHealth(home, now = Date.now(), connectedAt = home ? undefined : readCredentials().connectedAt) {
23229
+ function readLastDeliveredAt(home) {
23230
+ try {
23231
+ const raw = JSON.parse(readFileSync12(collectorHealthFile(home), "utf8"));
23232
+ const ts = raw?.delivery?.last_ok_ts;
23233
+ return typeof ts === "number" && Number.isFinite(ts) && ts > 0 ? ts * 1000 : undefined;
23234
+ } catch {
23235
+ return;
23236
+ }
23237
+ }
23238
+ function deliveryHealth(home, now = Date.now(), connectedAt = home ? undefined : readCredentials().connectedAt, lastDeliveredAt = home ? undefined : readLastDeliveredAt()) {
22893
23239
  const dir = failedDir(home);
22894
23240
  if (!existsSync19(dir))
22895
23241
  return EMPTY;
@@ -22903,6 +23249,7 @@ function deliveryHealth(home, now = Date.now(), connectedAt = home ? undefined :
22903
23249
  let total = 0;
22904
23250
  let rejected = 0;
22905
23251
  let credentialRejected = 0;
23252
+ let resending = 0;
22906
23253
  let oldestMtime;
22907
23254
  for (const name of entries) {
22908
23255
  if (!name.includes(".jsonl"))
@@ -22916,6 +23263,10 @@ function deliveryHealth(home, now = Date.now(), connectedAt = home ? undefined :
22916
23263
  }
22917
23264
  if (connectedAt !== undefined && parsed.clientStatus !== undefined && CREDENTIAL_STATUSES.has(parsed.clientStatus) && mtime < connectedAt)
22918
23265
  continue;
23266
+ if (lastDeliveredAt !== undefined && parsed.clientStatus !== undefined && CREDENTIAL_STATUSES.has(parsed.clientStatus) && mtime <= lastDeliveredAt) {
23267
+ resending += 1;
23268
+ continue;
23269
+ }
22919
23270
  total += 1;
22920
23271
  if (parsed.clientStatus !== undefined) {
22921
23272
  rejected += 1;
@@ -22931,6 +23282,7 @@ function deliveryHealth(home, now = Date.now(), connectedAt = home ? undefined :
22931
23282
  rejected,
22932
23283
  credentialRejected,
22933
23284
  byStatus,
23285
+ resending,
22934
23286
  ...oldestMtime !== undefined ? { oldestAgeMs: Math.max(0, now - oldestMtime) } : {}
22935
23287
  };
22936
23288
  }
@@ -22966,7 +23318,8 @@ var init_delivery_health = __esm(() => {
22966
23318
  total: 0,
22967
23319
  rejected: 0,
22968
23320
  credentialRejected: 0,
22969
- byStatus: {}
23321
+ byStatus: {},
23322
+ resending: 0
22970
23323
  };
22971
23324
  });
22972
23325
 
@@ -23662,7 +24015,7 @@ var init_cursor_projects = __esm(() => {
23662
24015
  });
23663
24016
 
23664
24017
  // lib/sqlite-reader.ts
23665
- import { existsSync as existsSync20, readFileSync as readFileSync12, statSync as statSync13 } from "node:fs";
24018
+ import { existsSync as existsSync20, readFileSync as readFileSync13, statSync as statSync13 } from "node:fs";
23666
24019
  import initSqlJs from "sql.js/dist/sql-asm.js";
23667
24020
  async function tryNodeSqlite(dbPath) {
23668
24021
  try {
@@ -23775,13 +24128,13 @@ function readPortableSnapshot(dbPath) {
23775
24128
  const walPath = `${dbPath}-wal`;
23776
24129
  for (let attempt = 0;attempt < 3; attempt += 1) {
23777
24130
  const before = fileVersion2(dbPath);
23778
- const main = readFileSync12(dbPath);
24131
+ const main = readFileSync13(dbPath);
23779
24132
  const afterMain = fileVersion2(dbPath);
23780
24133
  if (!sameVersion(before, afterMain))
23781
24134
  continue;
23782
24135
  let wal = null;
23783
24136
  try {
23784
- wal = readFileSync12(walPath);
24137
+ wal = readFileSync13(walPath);
23785
24138
  } catch (error) {
23786
24139
  if (error.code !== "ENOENT")
23787
24140
  throw error;
@@ -24868,7 +25221,7 @@ __export(exports_openclaw_projects, {
24868
25221
  getOpenClawSessions: () => getOpenClawSessions,
24869
25222
  openClawProjectPath: () => openClawProjectPath
24870
25223
  });
24871
- import { readFileSync as readFileSync13 } from "node:fs";
25224
+ import { readFileSync as readFileSync14 } from "node:fs";
24872
25225
  import { join as join18 } from "node:path";
24873
25226
  function str2(v) {
24874
25227
  return typeof v === "string" && v.length > 0 ? v : undefined;
@@ -24878,7 +25231,7 @@ function readSessionsIndex(agentId) {
24878
25231
  const indexPath = join18(openclawHome(), "agents", agentId, "sessions", "sessions.json");
24879
25232
  let raw;
24880
25233
  try {
24881
- raw = JSON.parse(readFileSync13(indexPath, "utf-8"));
25234
+ raw = JSON.parse(readFileSync14(indexPath, "utf-8"));
24882
25235
  } catch {
24883
25236
  return out;
24884
25237
  }
@@ -26687,7 +27040,7 @@ var init_log_entries = __esm(() => {
26687
27040
  });
26688
27041
 
26689
27042
  // lib/codex-sessions.ts
26690
- import { readFileSync as readFileSync14, readdirSync as readdirSync10, existsSync as existsSync22, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11, statSync as statSync17 } from "node:fs";
27043
+ import { readFileSync as readFileSync15, readdirSync as readdirSync10, existsSync as existsSync22, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11, statSync as statSync17 } from "node:fs";
26691
27044
  import { readFile as readFile11 } from "node:fs/promises";
26692
27045
  import { dirname as dirname13, join as join27 } from "node:path";
26693
27046
  import { homedir as homedir21 } from "node:os";
@@ -26695,7 +27048,7 @@ function readCache() {
26695
27048
  try {
26696
27049
  if (!existsSync22(CACHE_PATH))
26697
27050
  return {};
26698
- return JSON.parse(readFileSync14(CACHE_PATH, "utf-8"));
27051
+ return JSON.parse(readFileSync15(CACHE_PATH, "utf-8"));
26699
27052
  } catch {
26700
27053
  return {};
26701
27054
  }
@@ -26975,7 +27328,7 @@ var init_codex_sessions = __esm(() => {
26975
27328
  });
26976
27329
 
26977
27330
  // src/hooks/resolve-permission-mode.ts
26978
- import { readFileSync as readFileSync15 } from "node:fs";
27331
+ import { readFileSync as readFileSync16 } from "node:fs";
26979
27332
  function resolvePermissionMode(integration, parsed, sessionId) {
26980
27333
  if (integration === "claude") {
26981
27334
  return parsed.permission_mode ?? "default";
@@ -26990,7 +27343,7 @@ function resolveCodexMode(sessionId) {
26990
27343
  const path = findCodexTranscript(sessionId);
26991
27344
  if (!path)
26992
27345
  return;
26993
- for (const line of readFileSync15(path, "utf-8").split(`
27346
+ for (const line of readFileSync16(path, "utf-8").split(`
26994
27347
  `)) {
26995
27348
  if (!line.includes("turn_context"))
26996
27349
  continue;
@@ -27015,7 +27368,7 @@ var init_resolve_permission_mode = __esm(() => {
27015
27368
  });
27016
27369
 
27017
27370
  // lib/copilot-sessions.ts
27018
- import { readFileSync as readFileSync16, readdirSync as readdirSync11, existsSync as existsSync23, statSync as statSync18 } from "node:fs";
27371
+ import { readFileSync as readFileSync17, readdirSync as readdirSync11, existsSync as existsSync23, statSync as statSync18 } from "node:fs";
27019
27372
  import { readFile as readFile12 } from "node:fs/promises";
27020
27373
  import { join as join28, resolve as resolve17, sep as sep2 } from "node:path";
27021
27374
  import { homedir as homedir22 } from "node:os";
@@ -27054,7 +27407,7 @@ function findCopilotWorkspace(sessionId) {
27054
27407
  }
27055
27408
  function readYamlScalar(path, key) {
27056
27409
  try {
27057
- const text = readFileSync16(path, "utf-8");
27410
+ const text = readFileSync17(path, "utf-8");
27058
27411
  const re = new RegExp(`^${key}\\s*:\\s*(.+?)\\s*$`, "m");
27059
27412
  const m = text.match(re);
27060
27413
  if (!m)
@@ -27232,7 +27585,7 @@ var init_copilot_sessions = __esm(() => {
27232
27585
  });
27233
27586
 
27234
27587
  // lib/cursor-sessions.ts
27235
- import { readFileSync as readFileSync17, readdirSync as readdirSync12, existsSync as existsSync24, statSync as statSync19 } from "node:fs";
27588
+ import { readFileSync as readFileSync18, readdirSync as readdirSync12, existsSync as existsSync24, statSync as statSync19 } from "node:fs";
27236
27589
  import { readFile as readFile13 } from "node:fs/promises";
27237
27590
  import { basename as basename6, join as join29, resolve as resolve18, sep as sep3 } from "node:path";
27238
27591
  import { homedir as homedir23 } from "node:os";
@@ -27488,7 +27841,7 @@ var init_cursor_sessions = __esm(() => {
27488
27841
  });
27489
27842
 
27490
27843
  // lib/pi-sessions.ts
27491
- import { readFileSync as readFileSync18, readdirSync as readdirSync13, existsSync as existsSync25, statSync as statSync20 } from "node:fs";
27844
+ import { readFileSync as readFileSync19, readdirSync as readdirSync13, existsSync as existsSync25, statSync as statSync20 } from "node:fs";
27492
27845
  import { readFile as readFile14 } from "node:fs/promises";
27493
27846
  import { join as join30, resolve as resolve19, sep as sep4 } from "node:path";
27494
27847
  import { homedir as homedir24 } from "node:os";
@@ -27798,7 +28151,7 @@ var STDIN_TIMEOUT_MS2 = 1e4;
27798
28151
 
27799
28152
  // src/hooks/cloud-policy-errors.ts
27800
28153
  import { randomUUID as randomUUID2 } from "node:crypto";
27801
- import { chmodSync as chmodSync6, existsSync as existsSync26, readFileSync as readFileSync19, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync10 } from "node:fs";
28154
+ import { chmodSync as chmodSync6, existsSync as existsSync26, readFileSync as readFileSync20, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync10 } from "node:fs";
27802
28155
  import { homedir as homedir25 } from "node:os";
27803
28156
  import { resolve as resolve20 } from "node:path";
27804
28157
  function isPackDropEntry(e) {
@@ -27835,7 +28188,7 @@ function cloudPolicyErrorsPath() {
27835
28188
  }
27836
28189
  function readCloudPolicyErrors() {
27837
28190
  try {
27838
- const raw = JSON.parse(readFileSync19(cloudPolicyErrorsPath(), "utf8"));
28191
+ const raw = JSON.parse(readFileSync20(cloudPolicyErrorsPath(), "utf8"));
27839
28192
  if (!Array.isArray(raw.errors))
27840
28193
  return null;
27841
28194
  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)));
@@ -27859,7 +28212,7 @@ function writeCloudPolicyErrors(errors) {
27859
28212
  return "unchanged";
27860
28213
  let current = null;
27861
28214
  try {
27862
- current = readFileSync19(path, "utf8");
28215
+ current = readFileSync20(path, "utf8");
27863
28216
  } catch {
27864
28217
  current = null;
27865
28218
  }
@@ -27881,7 +28234,7 @@ function cloudLoadCheckedPath() {
27881
28234
  }
27882
28235
  function readCloudLoadChecked() {
27883
28236
  try {
27884
- const raw = JSON.parse(readFileSync19(cloudLoadCheckedPath(), "utf8"));
28237
+ const raw = JSON.parse(readFileSync20(cloudLoadCheckedPath(), "utf8"));
27885
28238
  if (!Number.isSafeInteger(raw.deployment) || !Array.isArray(raw.checked))
27886
28239
  return null;
27887
28240
  return { deployment: raw.deployment, checked: raw.checked.filter((c) => typeof c === "string") };
@@ -27946,7 +28299,7 @@ function jevBudgetPath() {
27946
28299
  }
27947
28300
  function readJevBudgetRecord() {
27948
28301
  try {
27949
- const raw = JSON.parse(readFileSync19(jevBudgetPath(), "utf8"));
28302
+ const raw = JSON.parse(readFileSync20(jevBudgetPath(), "utf8"));
27950
28303
  if (typeof raw.machineId !== "string" || !(raw.deployment === null || Number.isSafeInteger(raw.deployment)) || raw.jevMode !== "observe" && raw.jevMode !== "enforce" || !Array.isArray(raw.dropped)) {
27951
28304
  return null;
27952
28305
  }
@@ -27969,7 +28322,7 @@ function installedPackIds() {
27969
28322
  const path = resolve20(packsRoot(), "installed.json");
27970
28323
  if (!existsSync26(path))
27971
28324
  return new Set;
27972
- const raw = JSON.parse(readFileSync19(path, "utf8"));
28325
+ const raw = JSON.parse(readFileSync20(path, "utf8"));
27973
28326
  if (!Array.isArray(raw.packs))
27974
28327
  return null;
27975
28328
  return new Set(raw.packs.map((p) => p && typeof p === "object" ? p.id : null).filter((id) => typeof id === "string"));
@@ -28164,7 +28517,7 @@ var init_pack_failclosed = __esm(() => {
28164
28517
  });
28165
28518
 
28166
28519
  // src/hooks/session-pause.ts
28167
- import { existsSync as existsSync27, mkdirSync as mkdirSync12, readdirSync as readdirSync14, readFileSync as readFileSync20, rmSync as rmSync10 } from "node:fs";
28520
+ import { existsSync as existsSync27, mkdirSync as mkdirSync12, readdirSync as readdirSync14, readFileSync as readFileSync21, rmSync as rmSync10 } from "node:fs";
28168
28521
  import { createHash as createHash8 } from "node:crypto";
28169
28522
  import { join as join31, resolve as resolve21 } from "node:path";
28170
28523
  function pauseStateDir() {
@@ -28233,7 +28586,7 @@ function readActivePause(sessionId, now = Date.now()) {
28233
28586
  const path = pauseFilePath(sessionId);
28234
28587
  if (!existsSync27(path))
28235
28588
  return null;
28236
- const stored = parseStored(JSON.parse(readFileSync20(path, "utf8")));
28589
+ const stored = parseStored(JSON.parse(readFileSync21(path, "utf8")));
28237
28590
  if (!stored)
28238
28591
  return null;
28239
28592
  if (stored.expiresAt <= now)
@@ -28286,7 +28639,7 @@ function listActivePauses(now = Date.now()) {
28286
28639
  if (!name.endsWith(".json"))
28287
28640
  continue;
28288
28641
  try {
28289
- const stored = parseStored(JSON.parse(readFileSync20(join31(dir, name), "utf8")));
28642
+ const stored = parseStored(JSON.parse(readFileSync21(join31(dir, name), "utf8")));
28290
28643
  if (!stored || stored.expiresAt <= now)
28291
28644
  continue;
28292
28645
  const { schemaVersion: _ignored, ...pause } = stored;
@@ -28328,7 +28681,7 @@ import {
28328
28681
  copyFileSync as copyFileSync2,
28329
28682
  existsSync as existsSync28,
28330
28683
  mkdirSync as mkdirSync13,
28331
- readFileSync as readFileSync21,
28684
+ readFileSync as readFileSync22,
28332
28685
  readdirSync as readdirSync15,
28333
28686
  renameSync as renameSync9,
28334
28687
  rmSync as rmSync11,
@@ -28455,7 +28808,7 @@ function retirePolicyConfigKeys() {
28455
28808
  const at = globalPolicyConfigFile();
28456
28809
  let parsed;
28457
28810
  try {
28458
- const raw = JSON.parse(readFileSync21(at, "utf8"));
28811
+ const raw = JSON.parse(readFileSync22(at, "utf8"));
28459
28812
  if (!raw || typeof raw !== "object" || Array.isArray(raw))
28460
28813
  return [];
28461
28814
  parsed = raw;
@@ -28484,7 +28837,7 @@ function readCarriedPolicyConfig() {
28484
28837
  return null;
28485
28838
  let parsed;
28486
28839
  try {
28487
- parsed = JSON.parse(readFileSync21(from, "utf8"));
28840
+ parsed = JSON.parse(readFileSync22(from, "utf8"));
28488
28841
  } catch {
28489
28842
  return null;
28490
28843
  }
@@ -28528,7 +28881,7 @@ function writeCarriedPolicyConfig(carried) {
28528
28881
  const to = globalPolicyConfigFile();
28529
28882
  let existing = {};
28530
28883
  try {
28531
- const parsed = JSON.parse(readFileSync21(to, "utf8"));
28884
+ const parsed = JSON.parse(readFileSync22(to, "utf8"));
28532
28885
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
28533
28886
  existing = parsed;
28534
28887
  }
@@ -28547,7 +28900,7 @@ function readCarriedTelemetryOptOut() {
28547
28900
  if (!existsSync28(from))
28548
28901
  return false;
28549
28902
  try {
28550
- const text = readFileSync21(from, "utf8");
28903
+ const text = readFileSync22(from, "utf8");
28551
28904
  const start = text.search(/^\s*\[telemetry\]\s*$/m);
28552
28905
  if (start === -1)
28553
28906
  return false;
@@ -28602,7 +28955,7 @@ function readCarriedLegacyConfig() {
28602
28955
  if (!existsSync28(from))
28603
28956
  return null;
28604
28957
  try {
28605
- return projectConfig(parseLegacyToml(readFileSync21(from, "utf8")));
28958
+ return projectConfig(parseLegacyToml(readFileSync22(from, "utf8")));
28606
28959
  } catch {
28607
28960
  return null;
28608
28961
  }
@@ -28611,14 +28964,14 @@ function readCarriedLegacyCredentials() {
28611
28964
  const toml = legacy.credentialsToml();
28612
28965
  if (existsSync28(toml)) {
28613
28966
  try {
28614
- const creds = projectCredentials(parseLegacyToml(readFileSync21(toml, "utf8")));
28967
+ const creds = projectCredentials(parseLegacyToml(readFileSync22(toml, "utf8")));
28615
28968
  if (Object.keys(creds).length > 0)
28616
28969
  return creds;
28617
28970
  } catch {}
28618
28971
  }
28619
28972
  const readJson = (path) => {
28620
28973
  try {
28621
- const parsed = JSON.parse(readFileSync21(path, "utf8"));
28974
+ const parsed = JSON.parse(readFileSync22(path, "utf8"));
28622
28975
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
28623
28976
  } catch {
28624
28977
  return null;
@@ -28691,7 +29044,7 @@ var init_fp_reset = __esm(() => {
28691
29044
  });
28692
29045
 
28693
29046
  // src/hooks/untraced-note.ts
28694
- import { existsSync as existsSync29, readFileSync as readFileSync22 } from "node:fs";
29047
+ import { existsSync as existsSync29, readFileSync as readFileSync23 } from "node:fs";
28695
29048
  import { join as join32 } from "node:path";
28696
29049
  function untracedNotePath(home) {
28697
29050
  return join32(stateDir(home), "untraced-notes.json");
@@ -28704,7 +29057,7 @@ function noteUntracedAgent(cli, sessionId, config) {
28704
29057
  const path = untracedNotePath();
28705
29058
  let seen = [];
28706
29059
  if (existsSync29(path)) {
28707
- const parsed = JSON.parse(readFileSync22(path, "utf8"));
29060
+ const parsed = JSON.parse(readFileSync23(path, "utf8"));
28708
29061
  if (Array.isArray(parsed))
28709
29062
  seen = parsed.filter((k) => typeof k === "string");
28710
29063
  }
@@ -28726,7 +29079,7 @@ var init_untraced_note = __esm(() => {
28726
29079
  });
28727
29080
 
28728
29081
  // src/hooks/agent-roster.ts
28729
- import { lstatSync as lstatSync4, readFileSync as readFileSync23, statSync as statSync22 } from "node:fs";
29082
+ import { lstatSync as lstatSync4, readFileSync as readFileSync24, statSync as statSync22 } from "node:fs";
28730
29083
  import { homedir as homedir26 } from "node:os";
28731
29084
  import { dirname as dirname15, resolve as resolve23 } from "node:path";
28732
29085
  function installedHookAt2(path, cli) {
@@ -28734,7 +29087,7 @@ function installedHookAt2(path, cli) {
28734
29087
  const stat = statSync22(path);
28735
29088
  if (!stat.isFile() || stat.size > 131072)
28736
29089
  return false;
28737
- const text = readFileSync23(path, "utf8");
29090
+ const text = readFileSync24(path, "utf8");
28738
29091
  if (text.includes("failproofai"))
28739
29092
  return true;
28740
29093
  if (cli !== "pi")
@@ -28762,7 +29115,13 @@ function runtimeAgentSettingsPath2(cli, cwd, userHome = homedir26(), hookScope)
28762
29115
  if (config?.trim())
28763
29116
  return resolve23(config);
28764
29117
  const state = process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_HOME;
28765
- return resolve23(state?.trim() ? state : resolve23(userHome, ".openclaw"), "openclaw.json");
29118
+ if (state?.trim())
29119
+ return resolve23(state, "openclaw.json");
29120
+ const profile = process.env.OPENCLAW_PROFILE?.trim();
29121
+ if (profile && profile !== "default" && /^[A-Za-z0-9._-]{1,80}$/.test(profile) && !profile.startsWith(".")) {
29122
+ return resolve23(userHome, `.openclaw-${profile}`, "openclaw.json");
29123
+ }
29124
+ return resolve23(userHome, ".openclaw", "openclaw.json");
28766
29125
  }
28767
29126
  const relative = USER_SETTINGS2[cli];
28768
29127
  if (!relative)
@@ -28802,7 +29161,7 @@ function readRuntimeAgentIdentity(cli, settingsPath) {
28802
29161
  const stat = lstatSync4(path);
28803
29162
  if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > MAX_ROSTER_BYTES)
28804
29163
  return null;
28805
- const roster = JSON.parse(readFileSync23(path, "utf8"));
29164
+ const roster = JSON.parse(readFileSync24(path, "utf8"));
28806
29165
  if (!roster || typeof roster !== "object" || Array.isArray(roster))
28807
29166
  return null;
28808
29167
  const data = roster;
@@ -28828,7 +29187,7 @@ function readRosterLabels() {
28828
29187
  const stat = lstatSync4(path);
28829
29188
  if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > MAX_ROSTER_BYTES)
28830
29189
  return out;
28831
- const roster = JSON.parse(readFileSync23(path, "utf8"));
29190
+ const roster = JSON.parse(readFileSync24(path, "utf8"));
28832
29191
  if (!roster || typeof roster !== "object" || Array.isArray(roster))
28833
29192
  return out;
28834
29193
  const data = roster;
@@ -28970,13 +29329,6 @@ function providerHostConflict(provider, url) {
28970
29329
  return `${host} is ${known}'s endpoint, not ${provider}'s`;
28971
29330
  return known === "cloudflare" ? `${host} is Cloudflare Workers AI, which provider custom cannot reach — its endpoint is per-account and its answers are wrapped, and a custom endpoint is asked in TypeSafe's own shape at <url>/systemone` : null;
28972
29331
  }
28973
- function isPlainHttp(url) {
28974
- try {
28975
- return new URL(url).protocol === "http:";
28976
- } catch {
28977
- return false;
28978
- }
28979
- }
28980
29332
  function originOf(raw) {
28981
29333
  try {
28982
29334
  const url = new URL(raw);
@@ -29114,12 +29466,6 @@ function validateJevConfig(raw, envKey, cloud) {
29114
29466
  return { ok: false, problem: 'mode must be "off", "observe" or "enforce"' };
29115
29467
  cfg.mode = mode;
29116
29468
  }
29117
- if (cfg.baseUrl !== undefined && isPlainHttp(cfg.baseUrl) && cfg.mode === "enforce") {
29118
- return {
29119
- ok: false,
29120
- 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")
29121
- };
29122
- }
29123
29469
  return { ok: true, value: cfg };
29124
29470
  }
29125
29471
  function validateLoadedJevConfig(cfg) {
@@ -29566,7 +29912,7 @@ var init_cloud_jev_health = __esm(() => {
29566
29912
  });
29567
29913
 
29568
29914
  // src/hooks/semantic/facts.ts
29569
- import { existsSync as existsSync30, readFileSync as readFileSync24, statSync as statSync24 } from "node:fs";
29915
+ import { existsSync as existsSync30, readFileSync as readFileSync25, statSync as statSync24 } from "node:fs";
29570
29916
  import { homedir as homedir27 } from "node:os";
29571
29917
  import { dirname as dirname17, isAbsolute as isAbsolute4, resolve as resolve24 } from "node:path";
29572
29918
  function classifyTool(toolName) {
@@ -29817,13 +30163,13 @@ function readCurrentBranch(cwd) {
29817
30163
  return null;
29818
30164
  let headFile = resolve24(gitPath, "HEAD");
29819
30165
  if (statSync24(gitPath).isFile()) {
29820
- const pointer = readFileSync24(gitPath, "utf8").trim();
30166
+ const pointer = readFileSync25(gitPath, "utf8").trim();
29821
30167
  const m = /^gitdir:\s*(.+)$/.exec(pointer);
29822
30168
  if (!m)
29823
30169
  return null;
29824
30170
  headFile = resolve24(dirname17(gitPath), m[1], "HEAD");
29825
30171
  }
29826
- const head = readFileSync24(headFile, "utf8").trim();
30172
+ const head = readFileSync25(headFile, "utf8").trim();
29827
30173
  const ref = /^ref:\s*refs\/heads\/(.+)$/.exec(head);
29828
30174
  return ref ? ref[1] : null;
29829
30175
  } catch {
@@ -30216,7 +30562,7 @@ import {
30216
30562
  mkdirSync as mkdirSync14,
30217
30563
  openSync as openSync4,
30218
30564
  readdirSync as readdirSync16,
30219
- readFileSync as readFileSync25,
30565
+ readFileSync as readFileSync26,
30220
30566
  readSync as readSync3,
30221
30567
  renameSync as renameSync10,
30222
30568
  statSync as statSync25,
@@ -30266,7 +30612,7 @@ function looseIntentStoreDir() {
30266
30612
  }
30267
30613
  function readIntentFile(sessionId) {
30268
30614
  try {
30269
- const parsed = JSON.parse(readFileSync25(intentFile(sessionId), "utf8"));
30615
+ const parsed = JSON.parse(readFileSync26(intentFile(sessionId), "utf8"));
30270
30616
  return Array.isArray(parsed?.prompts) ? parsed : { prompts: [] };
30271
30617
  } catch {
30272
30618
  return { prompts: [] };
@@ -31589,7 +31935,11 @@ function parseOutcome(raw, i) {
31589
31935
  if (!origin || typeof origin !== "object" || typeof origin.cloudPolicyId !== "string" || !POLICY_ID_RE2.test(origin.cloudPolicyId) || !Number.isSafeInteger(origin.cloudVersion) || origin.cloudVersion < 0) {
31590
31936
  malformed(`outcome ${i} has no Cloud origin`);
31591
31937
  }
31592
- return {
31938
+ if (o.effect !== undefined && o.effect !== "enforce" && o.effect !== "observe")
31939
+ malformed(`outcome ${i} has an unknown effect`);
31940
+ if (o.reason !== undefined && o.reason !== null && typeof o.reason !== "string")
31941
+ malformed(`outcome ${i} reason is not text`);
31942
+ const outcome = {
31593
31943
  policy: o.policy,
31594
31944
  mode: o.mode,
31595
31945
  ...typeof o.userCanOverride === "boolean" ? { userCanOverride: o.userCanOverride } : {},
@@ -31603,41 +31953,103 @@ function parseOutcome(raw, i) {
31603
31953
  ...o.intent !== undefined ? { intent: o.intent } : {},
31604
31954
  ...o.targetScanIncomplete === true ? { targetScanIncomplete: true } : {}
31605
31955
  };
31956
+ return {
31957
+ outcome,
31958
+ cloudPolicyId: origin.cloudPolicyId,
31959
+ cloudVersion: origin.cloudVersion,
31960
+ observe: o.effect === "observe",
31961
+ reason: typeof o.reason === "string" ? capReason(o.reason) : null
31962
+ };
31963
+ }
31964
+ function capReason(reason) {
31965
+ return reason.length > MAX_CLOUD_REASON_CHARS ? `${reason.slice(0, MAX_CLOUD_REASON_CHARS - 1)}…` : reason;
31966
+ }
31967
+ function observedChecks(parsed, watched = null) {
31968
+ const firing = watched?.parsed.filter((p) => p.outcome.verdict === "deny" || p.outcome.verdict === "instruct") ?? [];
31969
+ const watchedReason = firing.length === 1 && typeof watched?.v.reason === "string" ? capReason(watched.v.reason) : null;
31970
+ const byPolicy = new Map;
31971
+ for (const p of parsed) {
31972
+ const verdict = p.outcome.verdict;
31973
+ if (verdict !== "deny" && verdict !== "instruct")
31974
+ continue;
31975
+ const prev = byPolicy.get(p.cloudPolicyId);
31976
+ if (prev && SEVERITY[prev.decision] >= SEVERITY[verdict])
31977
+ continue;
31978
+ byPolicy.set(p.cloudPolicyId, {
31979
+ cloudPolicyId: p.cloudPolicyId,
31980
+ cloudVersion: p.cloudVersion,
31981
+ check: p.outcome.policy,
31982
+ decision: verdict,
31983
+ reason: p.reason ?? (firing[0] === p ? watchedReason : null) ?? `Flagged by semantic review (semantic/${p.outcome.policy})`
31984
+ });
31985
+ }
31986
+ return [...byPolicy.values()];
31606
31987
  }
31607
- function parseCloudReply(raw) {
31608
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
31609
- malformed("no cloud block");
31610
- const block = raw;
31611
- const v = block.verdict;
31988
+ function parseVerdictBlock(rawVerdict, rawAsked, what) {
31989
+ const v = rawVerdict;
31612
31990
  if (!v || typeof v !== "object" || Array.isArray(v))
31613
- malformed("no verdict");
31991
+ malformed(`no ${what}`);
31614
31992
  if (typeof v.decision !== "string" || !DECISIONS2.has(v.decision))
31615
- malformed("verdict has no decision");
31993
+ malformed(`${what} has no decision`);
31616
31994
  if (v.reason !== null && v.reason !== undefined && typeof v.reason !== "string")
31617
- malformed("verdict reason is not text");
31995
+ malformed(`${what} reason is not text`);
31618
31996
  if (!Array.isArray(v.outcomes) || v.outcomes.length > MAX_LIST)
31619
- malformed("verdict has no outcomes list");
31997
+ malformed(`${what} has no outcomes list`);
31620
31998
  if (v.beyondTask !== undefined && typeof v.beyondTask !== "boolean")
31621
- malformed("verdict beyondTask is not a flag");
31622
- const outcomes = v.outcomes.map(parseOutcome);
31623
- const asked = names(block.asked, "asked");
31999
+ malformed(`${what} beyondTask is not a flag`);
32000
+ const parsed = v.outcomes.map(parseOutcome);
32001
+ const asked = names(rawAsked, `${what} asked`);
31624
32002
  const askedSet = new Set(asked);
31625
32003
  const seen = new Set;
31626
- outcomes.forEach((o, i) => {
32004
+ parsed.forEach(({ outcome: o }, i) => {
31627
32005
  if (!askedSet.has(o.policy))
31628
- malformed(`outcome ${i} answers ${JSON.stringify(o.policy)}, which Cloud did not ask`);
32006
+ malformed(`${what} outcome ${i} answers ${JSON.stringify(o.policy)}, which Cloud did not ask`);
31629
32007
  const key = `${o.origin?.packId ?? ""}\x00${o.policy}`;
31630
32008
  if (seen.has(key))
31631
- malformed(`outcome ${i} answers ${JSON.stringify(o.policy)} twice`);
32009
+ malformed(`${what} outcome ${i} answers ${JSON.stringify(o.policy)} twice`);
31632
32010
  seen.add(key);
31633
32011
  });
31634
- if (outcomes.some((o) => o.verdict === "deny") && v.decision !== "deny")
32012
+ return { v, parsed, asked };
32013
+ }
32014
+ function parseCloudReply(raw, observedPolicies = []) {
32015
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
32016
+ malformed("no cloud block");
32017
+ const block = raw;
32018
+ const { v, parsed, asked } = parseVerdictBlock(block.verdict, block.asked, "verdict");
32019
+ let watched = null;
32020
+ if (block.observed !== undefined && block.observed !== null) {
32021
+ const o = block.observed;
32022
+ if (typeof o !== "object" || Array.isArray(o))
32023
+ malformed("observed is not an object");
32024
+ watched = parseVerdictBlock(o.verdict, o.asked, "observed verdict");
32025
+ if (watched.parsed.some((p) => p.outcome.verdict === "deny") && watched.v.decision !== "deny") {
32026
+ malformed("an observed check denied but its verdict does not");
32027
+ }
32028
+ }
32029
+ const observeIds = new Set(observedPolicies);
32030
+ const isObserved = (p) => p.observe || observeIds.has(p.cloudPolicyId);
32031
+ const enforced = parsed.filter((p) => !isObserved(p)).map((p) => p.outcome);
32032
+ const observedHere = parsed.filter(isObserved);
32033
+ const observed = [...observedHere, ...watched?.parsed ?? []];
32034
+ if (enforced.some((o) => o.verdict === "deny") && v.decision !== "deny")
31635
32035
  malformed("a check denied but the verdict does not");
32036
+ const cloudReason = typeof v.reason === "string" ? capReason(v.reason) : null;
32037
+ let decision = v.decision;
32038
+ let reason = cloudReason;
32039
+ if (observedHere.length > 0) {
32040
+ const own = enforced.some((o) => o.verdict === "deny") ? "deny" : enforced.some((o) => o.verdict === "instruct") || v.beyondTask === true ? "instruct" : "allow";
32041
+ const capped = RANK2[own] <= RANK2[decision] ? own : decision;
32042
+ if (capped !== decision)
32043
+ reason = null;
32044
+ decision = capped;
32045
+ }
31636
32046
  return {
32047
+ observed: observedChecks(observed, watched),
32048
+ observedAsked: [...observedHere.map((p) => p.outcome.policy), ...watched?.asked ?? []],
31637
32049
  verdict: {
31638
- decision: v.decision,
31639
- reason: typeof v.reason === "string" ? v.reason.length > MAX_CLOUD_REASON_CHARS ? `${v.reason.slice(0, MAX_CLOUD_REASON_CHARS - 1)}…` : v.reason : null,
31640
- outcomes,
32050
+ decision,
32051
+ reason,
32052
+ outcomes: enforced,
31641
32053
  injectionSuspected: nullableProbability(v.injectionSuspected, "injectionSuspected"),
31642
32054
  scopeWithinRequest: nullableProbability(v.scopeWithinRequest, "scopeWithinRequest"),
31643
32055
  beyondTask: v.beyondTask === true
@@ -31647,6 +32059,29 @@ function parseCloudReply(raw) {
31647
32059
  droppedCloud: names(block.droppedCloud, "droppedCloud")
31648
32060
  };
31649
32061
  }
32062
+ function parseObservedOutcomes(raw) {
32063
+ try {
32064
+ const block = raw?.observed;
32065
+ if (!block || typeof block !== "object" || Array.isArray(block))
32066
+ return [];
32067
+ const asked = new Set(names(block.asked, "observed.asked"));
32068
+ const outcomes = block.verdict?.outcomes ?? [];
32069
+ if (!Array.isArray(outcomes) || outcomes.length > MAX_LIST)
32070
+ return [];
32071
+ const seen = new Set;
32072
+ const out = [];
32073
+ outcomes.forEach((o, i) => {
32074
+ const parsed = parseOutcome(o, i).outcome;
32075
+ if (!asked.has(parsed.policy) || seen.has(parsed.policy))
32076
+ return;
32077
+ seen.add(parsed.policy);
32078
+ out.push(parsed);
32079
+ });
32080
+ return out;
32081
+ } catch {
32082
+ return [];
32083
+ }
32084
+ }
31650
32085
  function joinLines(reasons) {
31651
32086
  const lines = [];
31652
32087
  for (const reason of reasons) {
@@ -31693,6 +32128,22 @@ function expectedQuestions(request, prepared, dropped) {
31693
32128
  }));
31694
32129
  return { ...request, questions };
31695
32130
  }
32131
+ function cloudCheckAnswers(response, asked) {
32132
+ const out = {};
32133
+ if (asked.length === 0)
32134
+ return out;
32135
+ const names = new Set(asked);
32136
+ const answers = response.answers && typeof response.answers === "object" ? response.answers : {};
32137
+ for (const [id, a] of Object.entries(answers)) {
32138
+ const dot = id.lastIndexOf(".");
32139
+ if (dot <= 0 || !names.has(id.slice(0, dot)) || !QUESTION_PART_RE.test(id.slice(dot + 1)))
32140
+ continue;
32141
+ const p = a?.noul;
32142
+ if (isProbability(p))
32143
+ out[id] = p;
32144
+ }
32145
+ return out;
32146
+ }
31696
32147
  function presentAnswers(response) {
31697
32148
  const out = {};
31698
32149
  const answers = response.answers && typeof response.answers === "object" ? response.answers : {};
@@ -31721,7 +32172,7 @@ async function evaluateCloudSemantic(input, opts) {
31721
32172
  model: opts.model ?? DEFAULT_JEV_MODEL,
31722
32173
  modelVerified: true,
31723
32174
  via: "none",
31724
- cloud: { asked: [], droppedLocal: [], droppedCloud: [] }
32175
+ cloud: { asked: [], droppedLocal: [], droppedCloud: [], answers: {}, observed: [], observedAsked: [], observedOutcomes: [] }
31725
32176
  };
31726
32177
  }
31727
32178
  let prepared;
@@ -31762,11 +32213,13 @@ async function evaluateCloudSemantic(input, opts) {
31762
32213
  const timeout = AbortSignal.timeout(opts.timeoutMs ?? envNumber2("FAILPROOFAI_JEV_TIMEOUT_MS", DEFAULT_JEV_TIMEOUT_MS));
31763
32214
  const signal = opts.signal ? AbortSignal.any([timeout, opts.signal]) : timeout;
31764
32215
  const response = await transport(request, signal);
31765
- const reply = parseCloudReply(response.cloud);
32216
+ const reply = parseCloudReply(response.cloud, opts.observedPolicies);
32217
+ const observedAsked = new Set(reply.observedAsked);
32218
+ const observedOutcomes = parseObservedOutcomes(response.cloud).filter((o) => observedAsked.has(o.policy));
31766
32219
  const own = new Map(prepared.selected.map((p) => [p.name, p]));
31767
32220
  const dropped = new Set(reply.droppedLocal.filter((name) => own.has(name)));
31768
32221
  const kept = prepared.selected.filter((p) => !dropped.has(p.name));
31769
- const shortCircuit = kept.length === 0 && reply.asked.length === 0 && reply.verdict.outcomes.length === 0;
32222
+ const shortCircuit = kept.length === 0 && reply.asked.length === 0 && reply.verdict.outcomes.length === 0 && reply.observedAsked.length === 0;
31770
32223
  const answers = shortCircuit ? presentAnswers(response) : readAnswers(expectedQuestions(request, prepared, dropped), response);
31771
32224
  const local = prepared.intent === "v1" ? decideV1(kept, answers, input.toolInput, prepared.userSaid, prepared.agentLastMessage, {
31772
32225
  ...opts.v1,
@@ -31788,7 +32241,11 @@ async function evaluateCloudSemantic(input, opts) {
31788
32241
  cloud: {
31789
32242
  asked: reply.asked,
31790
32243
  droppedLocal: [...dropped].map((name) => ({ name, packId: own.get(name)?.origin?.packId ?? null })),
31791
- droppedCloud: reply.droppedCloud
32244
+ droppedCloud: reply.droppedCloud,
32245
+ answers: cloudCheckAnswers(response, [...reply.asked, ...reply.observedAsked]),
32246
+ observed: reply.observed,
32247
+ observedAsked: reply.observedAsked,
32248
+ observedOutcomes
31792
32249
  }
31793
32250
  };
31794
32251
  } catch (err) {
@@ -31801,7 +32258,7 @@ async function evaluateCloudSemantic(input, opts) {
31801
32258
  return degraded(`error: ${err instanceof Error ? err.message : String(err)}`);
31802
32259
  }
31803
32260
  }
31804
- 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, RANK2, EMPTY_ALLOW;
32261
+ 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;
31805
32262
  var init_cloud_jev = __esm(() => {
31806
32263
  init_compile();
31807
32264
  init_decide();
@@ -31817,6 +32274,7 @@ var init_cloud_jev = __esm(() => {
31817
32274
  DECISIONS2 = new Set(["allow", "deny", "instruct"]);
31818
32275
  VERDICTS = new Set(["deny", "instruct", "overridden", "none"]);
31819
32276
  INTENTS = new Set(["op-requested", "task-step", "downgraded-task-step"]);
32277
+ SEVERITY = { instruct: 1, deny: 2 };
31820
32278
  RANK2 = { allow: 0, instruct: 1, deny: 2 };
31821
32279
  EMPTY_ALLOW = {
31822
32280
  decision: "allow",
@@ -31826,6 +32284,7 @@ var init_cloud_jev = __esm(() => {
31826
32284
  scopeWithinRequest: null,
31827
32285
  beyondTask: false
31828
32286
  };
32287
+ QUESTION_PART_RE = /^[a-z][a-z0-9_]{0,31}$/;
31829
32288
  });
31830
32289
 
31831
32290
  // src/hooks/semantic/jev-throttle.ts
@@ -32038,7 +32497,7 @@ var init_jev_throttle = __esm(() => {
32038
32497
  });
32039
32498
 
32040
32499
  // src/hooks/semantic/session-root.ts
32041
- import { mkdirSync as mkdirSync16, readdirSync as readdirSync17, readFileSync as readFileSync26, statSync as statSync27, unlinkSync as unlinkSync5, writeFileSync as writeFileSync13 } from "node:fs";
32500
+ import { mkdirSync as mkdirSync16, readdirSync as readdirSync17, readFileSync as readFileSync27, statSync as statSync27, unlinkSync as unlinkSync5, writeFileSync as writeFileSync13 } from "node:fs";
32042
32501
  import { isAbsolute as isAbsolute5, resolve as resolve27 } from "node:path";
32043
32502
  function validRoot(v) {
32044
32503
  return typeof v === "string" && isAbsolute5(v) && resolve27(v) === v && v !== "/";
@@ -32057,7 +32516,7 @@ function storeIsOurs() {
32057
32516
  }
32058
32517
  function readPinned(sessionId) {
32059
32518
  try {
32060
- const parsed = JSON.parse(readFileSync26(rootFile(sessionId), "utf8"));
32519
+ const parsed = JSON.parse(readFileSync27(rootFile(sessionId), "utf8"));
32061
32520
  return validRoot(parsed?.root) ? parsed.root : null;
32062
32521
  } catch {
32063
32522
  return null;
@@ -32161,6 +32620,55 @@ function fallbackCode(reason) {
32161
32620
  const code = reason.split(":")[0].trim();
32162
32621
  return /^[a-z0-9][a-z0-9-]{0,63}$/.test(code) ? code : "error";
32163
32622
  }
32623
+ function checkWhy(o) {
32624
+ switch (o.verdict) {
32625
+ case "none":
32626
+ return o.evidence < DEFAULT_THRESHOLDS_V1.fire ? "not-detected" : "exempt";
32627
+ case "overridden":
32628
+ return o.intent === "op-requested" ? "op-requested" : o.intent === "task-step" ? "task-step" : "user-asked";
32629
+ case "deny":
32630
+ return o.escalatedByInjection ? "injection" : "fired";
32631
+ default:
32632
+ if (o.intent === "downgraded-task-step")
32633
+ return "downgraded";
32634
+ return o.targetScanIncomplete ? "scan-incomplete" : "fired";
32635
+ }
32636
+ }
32637
+ function checkPolicy(o) {
32638
+ const packId = o.origin?.packId;
32639
+ if (!packId)
32640
+ return;
32641
+ const at = o.origin?.packVersion ? `@${o.origin.packVersion}` : "";
32642
+ return packId.startsWith("cloud:") ? `cloud/${packId.slice("cloud:".length)}${at}` : `pack/${packId}${at}`;
32643
+ }
32644
+ function checkRecords(outcomes, answers, watched = []) {
32645
+ const all = [...outcomes.map((o) => ({ o, watched: false })), ...watched.map((o) => ({ o, watched: true }))];
32646
+ return all.slice(0, JEV_CHECKS_MAX).map(({ o, watched: isWatched }) => {
32647
+ const probes = {};
32648
+ const prefix = `${o.policy}.`;
32649
+ for (const [id, p] of Object.entries(answers)) {
32650
+ if (Object.keys(probes).length >= JEV_CHECK_PROBES_MAX)
32651
+ break;
32652
+ if (!id.startsWith(prefix))
32653
+ continue;
32654
+ const part = id.slice(prefix.length);
32655
+ if (part === "exempt" || part === "user_asked" || !JEV_PROBE_ID_RE.test(part))
32656
+ continue;
32657
+ probes[part] = round2(p);
32658
+ }
32659
+ const policy = checkPolicy(o);
32660
+ return {
32661
+ check: o.policy,
32662
+ ...policy ? { policy } : {},
32663
+ verdict: o.verdict,
32664
+ why: checkWhy(o),
32665
+ p: round2(o.evidence),
32666
+ ...o.exempt !== null ? { exempt: round2(o.exempt) } : {},
32667
+ ...Object.keys(probes).length > 0 ? { probes } : {},
32668
+ ...isWatched ? { watched: true } : {}
32669
+ };
32670
+ });
32671
+ }
32164
32672
  function toReview(outcome, cached = false) {
32165
32673
  if (outcome.status === "degraded") {
32166
32674
  return { kind: "fallback", reason: fallbackCode(outcome.reason), latencyMs: outcome.latencyMs, model: null };
@@ -32169,15 +32677,19 @@ function toReview(outcome, cached = false) {
32169
32677
  const latencyMs = sent && !cached ? outcome.latencyMs : null;
32170
32678
  const model = sent ? outcome.model : null;
32171
32679
  const outcomes = outcome.verdict.outcomes;
32172
- const cloudAsked = "cloud" in outcome ? new Set(outcome.cloud.asked) : null;
32680
+ const cloudSummary = "cloud" in outcome ? outcome.cloud : null;
32681
+ const cloudAsked = cloudSummary ? new Set(cloudSummary.asked) : null;
32173
32682
  const unvouched = outcomes.some((o) => isCloudOutcome(o) && !(cloudAsked?.has(o.policy) ?? false));
32174
32683
  if (unvouched)
32175
32684
  return { kind: "fallback", reason: "malformed", latencyMs, model: null };
32176
32685
  const injection = sent ? outcome.verdict.injectionSuspected : null;
32177
32686
  const warned = (o) => o.verdict === "instruct" && o.intent !== "downgraded-task-step";
32687
+ const cloudAnswers = cloudSummary?.answers ?? {};
32688
+ const watched = cloudSummary?.observedOutcomes ?? [];
32178
32689
  return {
32179
32690
  kind: "answered",
32180
32691
  decision: outcome.verdict.decision,
32692
+ checks: sent ? checkRecords(outcomes, { ...outcome.answers, ...cloudAnswers }, watched) : [],
32181
32693
  reason: outcome.verdict.reason,
32182
32694
  policyName: jevPolicyName(outcome),
32183
32695
  ...jevDecider(outcome)?.origin ? { origin: jevDecider(outcome)?.origin } : {},
@@ -32189,7 +32701,16 @@ function toReview(outcome, cached = false) {
32189
32701
  truncated: outcome.truncated && sent,
32190
32702
  requestCut: outcome.requestCut && sent,
32191
32703
  latencyMs,
32192
- model
32704
+ model,
32705
+ ...sent && cloudSummary?.observedAsked?.length ? { observedAsked: cloudSummary.observedAsked } : {},
32706
+ ...cloudSummary?.observed?.length ? {
32707
+ observedCloud: cloudSummary.observed.map((o) => ({
32708
+ cloudPolicyId: o.cloudPolicyId,
32709
+ cloudVersion: o.cloudVersion,
32710
+ decision: o.decision,
32711
+ reason: o.reason
32712
+ }))
32713
+ } : {}
32193
32714
  };
32194
32715
  }
32195
32716
  function toolInputRecord(raw) {
@@ -32208,7 +32729,8 @@ function startJevReview(cfg, call) {
32208
32729
  if (!settled)
32209
32730
  controller.abort();
32210
32731
  },
32211
- authorityOf
32732
+ authorityOf,
32733
+ ...call.cloud ? { cloud: true } : {}
32212
32734
  });
32213
32735
  let route;
32214
32736
  let throttled;
@@ -32270,6 +32792,7 @@ function startJevReview(cfg, call) {
32270
32792
  ...options,
32271
32793
  machineId: cloud.machineId,
32272
32794
  agent: cloud.agent,
32795
+ observedPolicies: cloud.observedPolicies ?? [],
32273
32796
  ...call.localPaused ? { policies: [] } : {}
32274
32797
  }) : evaluateSemantic(input, options);
32275
32798
  const answered = evaluated.then((outcome) => {
@@ -32331,6 +32854,7 @@ var init_jev_review = __esm(() => {
32331
32854
  init_jev_config();
32332
32855
  init_jev_throttle();
32333
32856
  init_session_root();
32857
+ init_jev_activity();
32334
32858
  throttle = exports_jev_throttle;
32335
32859
  intentReader = exports_intent;
32336
32860
  ALWAYS_ON = new Set(BUILTIN_POLICIES.filter((p) => p.alwaysOn).map((p) => normalizePolicyName(p.name)));
@@ -32544,7 +33068,13 @@ async function readJevConfig(agent) {
32544
33068
  return {
32545
33069
  config: resolved.config,
32546
33070
  defaultMode: DEFAULT_JEV_MODE,
32547
- cloud: { machineId: resolved.machineId, deployment: cloud.deployment, mode: cloud.jevMode, agent: agent ?? null }
33071
+ cloud: {
33072
+ machineId: resolved.machineId,
33073
+ deployment: cloud.deployment,
33074
+ mode: cloud.jevMode,
33075
+ agent: agent ?? null,
33076
+ observedPolicies: (cloudJevAssignmentsFor(agent ?? null) ?? []).filter((a) => a.effect === "observe").map((a) => a.id)
33077
+ }
32548
33078
  };
32549
33079
  }
32550
33080
  if (!existsSync31(jevConfigFile()))
@@ -32618,6 +33148,8 @@ async function startTwoTier(canonicalEventType, parsed, session, cli, opts, acti
32618
33148
  return null;
32619
33149
  if (!cloudAsks && !jevChecksInstalled())
32620
33150
  return null;
33151
+ if (cloudAsks && cloudJevAssignmentsFor(agent)?.length === 0 && (activePause || !packJevChecksInstalled()))
33152
+ return null;
32621
33153
  const loaded = await readJevConfig(agent);
32622
33154
  if (!loaded)
32623
33155
  return null;
@@ -32941,6 +33473,9 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
32941
33473
  reason: observeVerdict.reason
32942
33474
  });
32943
33475
  }
33476
+ for (const o of result.twoTier?.observedCloud ?? []) {
33477
+ observedResults.push({ policyId: o.cloudPolicyId, version: o.cloudVersion, decision: o.decision, reason: o.reason });
33478
+ }
32944
33479
  const durationMs = Math.round(performance.now() - startTime);
32945
33480
  hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
32946
33481
  await captureJevIntent(canonicalEventType, result.decision, parsed, session, cli, opts);
@@ -34070,7 +34605,7 @@ import {
34070
34605
  copyFileSync as copyFileSync3,
34071
34606
  existsSync as existsSync33,
34072
34607
  mkdirSync as mkdirSync18,
34073
- readFileSync as readFileSync27,
34608
+ readFileSync as readFileSync28,
34074
34609
  renameSync as renameSync12,
34075
34610
  rmSync as rmSync12,
34076
34611
  writeFileSync as writeFileSync15
@@ -34140,7 +34675,7 @@ function planMigration(from, registry = MIGRATIONS) {
34140
34675
  }
34141
34676
  function readLedger() {
34142
34677
  try {
34143
- const parsed = JSON.parse(readFileSync27(migrationLedgerFile(), "utf8"));
34678
+ const parsed = JSON.parse(readFileSync28(migrationLedgerFile(), "utf8"));
34144
34679
  return Array.isArray(parsed) ? parsed : [];
34145
34680
  } catch {
34146
34681
  return [];
@@ -34449,7 +34984,7 @@ var init_fp_reset2 = __esm(() => {
34449
34984
  // scripts/install-telemetry.mjs
34450
34985
  import { createHmac, randomUUID as randomUUID3 } from "node:crypto";
34451
34986
  import { execSync as execSync6 } from "node:child_process";
34452
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync16, mkdirSync as mkdirSync19 } from "node:fs";
34987
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, mkdirSync as mkdirSync19 } from "node:fs";
34453
34988
  import { homedir as homedir28, platform, arch, hostname, cpus } from "node:os";
34454
34989
  import { join as join35 } from "node:path";
34455
34990
  function hashToId3(raw) {
@@ -34459,7 +34994,7 @@ function getInstanceId3() {
34459
34994
  const idDir = join35(homedir28(), ".failproofai");
34460
34995
  const idFile = join35(idDir, "instance-id");
34461
34996
  try {
34462
- const existing = readFileSync28(idFile, "utf-8").trim();
34997
+ const existing = readFileSync29(idFile, "utf-8").trim();
34463
34998
  if (existing)
34464
34999
  return existing;
34465
35000
  } catch {}
@@ -34468,7 +35003,7 @@ function getInstanceId3() {
34468
35003
  if (p === "linux") {
34469
35004
  for (const machineIdPath of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
34470
35005
  try {
34471
- const id = readFileSync28(machineIdPath, "utf-8").trim();
35006
+ const id = readFileSync29(machineIdPath, "utf-8").trim();
34472
35007
  if (id)
34473
35008
  return hashToId3(id);
34474
35009
  } catch {}
@@ -34533,7 +35068,7 @@ var NAMESPACE3 = "failproofai-telemetry-v1", API_KEY3 = "phc_Ac1Ww1GqKc0z1SyrRWb
34533
35068
  var init_install_telemetry = () => {};
34534
35069
 
34535
35070
  // lib/install-check.ts
34536
- import { existsSync as existsSync34, readFileSync as readFileSync29, writeFileSync as writeFileSync17, mkdirSync as mkdirSync20 } from "node:fs";
35071
+ import { existsSync as existsSync34, readFileSync as readFileSync30, writeFileSync as writeFileSync17, mkdirSync as mkdirSync20 } from "node:fs";
34537
35072
  import { dirname as dirname21, resolve as resolve29 } from "node:path";
34538
35073
  import { platform as platform2, arch as arch2, release, homedir as homedir29, hostname as hostname2 } from "node:os";
34539
35074
  import { createHmac as createHmac2 } from "node:crypto";
@@ -34594,7 +35129,7 @@ function checkHooks() {
34594
35129
  }
34595
35130
  let config;
34596
35131
  try {
34597
- config = JSON.parse(readFileSync29(hooksConfigPath, "utf8"));
35132
+ config = JSON.parse(readFileSync30(hooksConfigPath, "utf8"));
34598
35133
  } catch {
34599
35134
  return { configured: false, registered: false, policyCount: 0 };
34600
35135
  }
@@ -34607,7 +35142,7 @@ function checkHooks() {
34607
35142
  return { configured: true, registered: false, policyCount };
34608
35143
  let settings;
34609
35144
  try {
34610
- settings = JSON.parse(readFileSync29(settingsPath, "utf8"));
35145
+ settings = JSON.parse(readFileSync30(settingsPath, "utf8"));
34611
35146
  } catch {
34612
35147
  return { configured: true, registered: false, policyCount };
34613
35148
  }
@@ -34632,7 +35167,7 @@ async function maybeReportInstall(version) {
34632
35167
  let previousVersion = null;
34633
35168
  try {
34634
35169
  if (existsSync34(versionFile)) {
34635
- previousVersion = readFileSync29(versionFile, "utf8").trim() || null;
35170
+ previousVersion = readFileSync30(versionFile, "utf8").trim() || null;
34636
35171
  }
34637
35172
  } catch {}
34638
35173
  if (previousVersion === version)
@@ -34705,6 +35240,196 @@ var init_first_run_gate = __esm(() => {
34705
35240
  ];
34706
35241
  });
34707
35242
 
35243
+ // src/hooks/cloud-login.ts
35244
+ async function postJson2(origin, path, body, fetchImpl) {
35245
+ const controller = new AbortController;
35246
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
35247
+ try {
35248
+ const res = await fetchImpl(`${origin.replace(/\/+$/, "")}${path}`, {
35249
+ method: "POST",
35250
+ headers: { "Content-Type": "application/json" },
35251
+ body: JSON.stringify(body),
35252
+ redirect: "manual",
35253
+ signal: controller.signal
35254
+ });
35255
+ let json = null;
35256
+ try {
35257
+ const parsed = await res.json();
35258
+ json = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
35259
+ } catch {
35260
+ json = null;
35261
+ }
35262
+ return { status: res.status, json };
35263
+ } catch (err) {
35264
+ return { error: err instanceof Error ? err.name === "AbortError" ? "timed out" : err.message : String(err) };
35265
+ } finally {
35266
+ clearTimeout(timer);
35267
+ }
35268
+ }
35269
+ function displayText(v, max = 200) {
35270
+ if (typeof v !== "string")
35271
+ return;
35272
+ const t = v.replace(INVISIBLE, "").trim().slice(0, max);
35273
+ return t.length > 0 ? t : undefined;
35274
+ }
35275
+ function org(v) {
35276
+ const o = v && typeof v === "object" ? v : {};
35277
+ return { id: str3(o.id), slug: displayText(o.slug), name: displayText(o.name) };
35278
+ }
35279
+ function permissions(v) {
35280
+ return Array.isArray(v) ? v.map((p) => displayText(p, 64)).filter((p) => p !== undefined) : [];
35281
+ }
35282
+ function isOpenableUrl(raw) {
35283
+ try {
35284
+ const u = new URL(raw);
35285
+ return u.protocol === "https:" || u.protocol === "http:";
35286
+ } catch {
35287
+ return false;
35288
+ }
35289
+ }
35290
+ function machineOs(platform = process.platform) {
35291
+ return platform === "darwin" ? "macOS" : platform === "win32" ? "windows" : platform;
35292
+ }
35293
+ async function startLogin(origin, machine, fetchImpl = fetch) {
35294
+ const r = await postJson2(origin, LOGIN_START_PATH, { machine_label: machine.label, machine_os: machine.os, cli_version: machine.cliVersion }, fetchImpl);
35295
+ if ("error" in r)
35296
+ return { kind: "unreachable", reason: r.error };
35297
+ if (r.status === 404 || r.status >= 300 && r.status < 400 || r.status === 200 && !r.json) {
35298
+ return { kind: "unsupported" };
35299
+ }
35300
+ if (r.status === 429)
35301
+ return { kind: "refused", reason: "too many login attempts from here — wait a minute" };
35302
+ if (r.status !== 201 || !r.json) {
35303
+ return r.status >= 500 ? { kind: "unreachable", reason: `the server answered ${r.status}` } : { kind: "refused", reason: displayText(r.json?.error, 120) ?? `the server answered ${r.status}` };
35304
+ }
35305
+ const verificationUrl = str3(r.json.verification_url);
35306
+ const pollSecret = str3(r.json.poll_secret);
35307
+ if (!verificationUrl || !pollSecret || !isOpenableUrl(verificationUrl)) {
35308
+ return { kind: "unsupported" };
35309
+ }
35310
+ return {
35311
+ kind: "ok",
35312
+ verificationUrl: new URL(verificationUrl).href,
35313
+ pollSecret,
35314
+ expiresIn: within(r.json.expires_in, 30, 3600, 600),
35315
+ interval: within(r.json.interval, 1, 30, 2)
35316
+ };
35317
+ }
35318
+ async function pollLogin(origin, pollSecret, fetchImpl = fetch) {
35319
+ const r = await postJson2(origin, LOGIN_POLL_PATH, { poll_secret: pollSecret }, fetchImpl);
35320
+ if ("error" in r)
35321
+ return { kind: "unreachable", reason: r.error };
35322
+ if (r.status === 404)
35323
+ return { kind: "unknown" };
35324
+ if (r.status !== 200 || !r.json)
35325
+ return { kind: "unreachable", reason: `the server answered ${r.status}` };
35326
+ const j = r.json;
35327
+ switch (j.status) {
35328
+ case "pending":
35329
+ return { kind: "pending", interval: typeof j.interval === "number" ? within(j.interval, 1, 30, 2) : undefined };
35330
+ case "slow_down":
35331
+ return { kind: "slow_down" };
35332
+ case "approved":
35333
+ return {
35334
+ kind: "approved",
35335
+ org: org(j.org),
35336
+ approvedBy: displayText(j.approved_by),
35337
+ keyName: displayText(j.key_name),
35338
+ permissions: permissions(j.permissions)
35339
+ };
35340
+ case "denied":
35341
+ return { kind: "denied" };
35342
+ case "expired":
35343
+ return { kind: "expired" };
35344
+ case "claimed":
35345
+ return { kind: "claimed" };
35346
+ default:
35347
+ return { kind: "unknown" };
35348
+ }
35349
+ }
35350
+ async function claimLogin(origin, pollSecret, fetchImpl = fetch) {
35351
+ const r = await postJson2(origin, LOGIN_CLAIM_PATH, { poll_secret: pollSecret }, fetchImpl);
35352
+ if ("error" in r)
35353
+ return { kind: "unreachable", reason: r.error };
35354
+ const j = r.json ?? {};
35355
+ if (r.status === 201) {
35356
+ const key = str3(j.api_key);
35357
+ if (!key)
35358
+ return { kind: "refused", reason: "the server answered without a key" };
35359
+ return {
35360
+ kind: "ok",
35361
+ key,
35362
+ keyId: str3(j.key_id),
35363
+ keyName: displayText(j.key_name),
35364
+ org: org(j.org),
35365
+ permissions: permissions(j.permissions),
35366
+ approvedBy: displayText(j.approved_by)
35367
+ };
35368
+ }
35369
+ if (r.status === 410)
35370
+ return { kind: "expired" };
35371
+ if (r.status === 403 && j.error === "approval_revoked")
35372
+ return { kind: "revoked" };
35373
+ if (r.status >= 500)
35374
+ return { kind: "unreachable", reason: `the server answered ${r.status}` };
35375
+ return {
35376
+ kind: "refused",
35377
+ reason: displayText(j.status, 40) ? `the login request is ${displayText(j.status, 40)}` : `the server answered ${r.status}`
35378
+ };
35379
+ }
35380
+ var LOGIN_START_PATH = "/api/auth/cli/start", LOGIN_POLL_PATH = "/api/auth/cli/poll", LOGIN_CLAIM_PATH = "/api/auth/cli/claim", TIMEOUT_MS = 1e4, str3 = (v) => typeof v === "string" && v.length > 0 ? v : undefined, INVISIBLE, within = (v, lo, hi, fallback) => typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.min(hi, Math.max(lo, v)) : fallback;
35381
+ var init_cloud_login = __esm(() => {
35382
+ INVISIBLE = /[\u0000-\u001f\u007f-\u009f\u061c\u180e\u200b-\u200f\u202a-\u202e\u2060-\u2069\ufeff]/g;
35383
+ });
35384
+
35385
+ // src/audit/open-browser.ts
35386
+ import { spawn } from "node:child_process";
35387
+ function canOpenBrowser(env = process.env, platform = process.platform) {
35388
+ if (env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY)
35389
+ return false;
35390
+ if (platform === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY)
35391
+ return false;
35392
+ return true;
35393
+ }
35394
+ function openUrl(url, platform = process.platform) {
35395
+ let cmd;
35396
+ let args;
35397
+ if (platform === "darwin") {
35398
+ cmd = "open";
35399
+ args = [url];
35400
+ } else if (platform === "win32") {
35401
+ cmd = "rundll32.exe";
35402
+ args = ["url.dll,FileProtocolHandler", url];
35403
+ } else {
35404
+ cmd = "xdg-open";
35405
+ args = [url];
35406
+ }
35407
+ try {
35408
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
35409
+ child.on("error", () => {});
35410
+ child.unref();
35411
+ } catch {}
35412
+ }
35413
+ function openWhenReady(port, path) {
35414
+ const base = `http://localhost:${port}`;
35415
+ const target = `${base}${path}`;
35416
+ (async () => {
35417
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
35418
+ while (Date.now() < deadline) {
35419
+ try {
35420
+ await fetch(base, { method: "GET", redirect: "manual" });
35421
+ openUrl(target);
35422
+ return;
35423
+ } catch {
35424
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
35425
+ }
35426
+ }
35427
+ openUrl(target);
35428
+ })();
35429
+ }
35430
+ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
35431
+ var init_open_browser = () => {};
35432
+
34708
35433
  // src/cli-error.ts
34709
35434
  var CliError;
34710
35435
  var init_cli_error = __esm(() => {
@@ -34719,7 +35444,7 @@ var init_cli_error = __esm(() => {
34719
35444
  });
34720
35445
 
34721
35446
  // src/hooks/backfill-request.ts
34722
- import { existsSync as existsSync35, readFileSync as readFileSync30 } from "node:fs";
35447
+ import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
34723
35448
  import { join as join36 } from "node:path";
34724
35449
  function backfillRequestPath(home) {
34725
35450
  return join36(failproofaiHome(home), "state", "backfill-request.json");
@@ -34771,7 +35496,7 @@ function writeBackfillRequest(next, home) {
34771
35496
  let pending = null;
34772
35497
  if (existsSync35(path)) {
34773
35498
  try {
34774
- pending = readPendingRequest(JSON.parse(readFileSync30(path, "utf8")), next.requestedAtMs);
35499
+ pending = readPendingRequest(JSON.parse(readFileSync31(path, "utf8")), next.requestedAtMs);
34775
35500
  } catch {
34776
35501
  pending = null;
34777
35502
  }
@@ -35033,6 +35758,34 @@ function integrationsInstalledAt(scope, cwd) {
35033
35758
  }
35034
35759
  });
35035
35760
  }
35761
+ function cloudPolicyStatus() {
35762
+ let count = 0;
35763
+ let observing = false;
35764
+ const jsIds = new Set;
35765
+ try {
35766
+ for (const artifact of listActiveCloudManagedPolicies()) {
35767
+ jsIds.add(artifact.id);
35768
+ if (artifact.effect === "observe")
35769
+ observing = true;
35770
+ else
35771
+ count++;
35772
+ }
35773
+ } catch {}
35774
+ try {
35775
+ const { jevMode } = readCloudJevState();
35776
+ if (jevMode === "enforce" || jevMode === "observe") {
35777
+ for (const assignment of readCloudJevAssignments() ?? []) {
35778
+ if (jsIds.has(assignment.id))
35779
+ continue;
35780
+ if (jevMode === "observe" || assignment.effect === "observe")
35781
+ observing = true;
35782
+ else
35783
+ count++;
35784
+ }
35785
+ }
35786
+ } catch {}
35787
+ return { count, observing };
35788
+ }
35036
35789
  function notEnforcingReason(cwd) {
35037
35790
  const wired = deduplicateScopes(HOOK_SCOPES, cwd).some((scope) => hooksInstalledInSettings(scope, cwd));
35038
35791
  if (!wired)
@@ -35055,14 +35808,10 @@ function notEnforcingReason(cwd) {
35055
35808
  return null;
35056
35809
  }
35057
35810
  } catch {}
35058
- try {
35059
- for (const artifact of listActiveCloudManagedPolicies()) {
35060
- if (artifact.effect === "observe")
35061
- observing = true;
35062
- else
35063
- return null;
35064
- }
35065
- } catch {}
35811
+ const cloud = cloudPolicyStatus();
35812
+ if (cloud.count > 0)
35813
+ return null;
35814
+ observing ||= cloud.observing;
35066
35815
  if (configuredCustomPolicyPaths(config).length > 0)
35067
35816
  return null;
35068
35817
  const projectDir = resolve30(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
@@ -35085,12 +35834,7 @@ function enforcingPolicyCount(cwd) {
35085
35834
  count += taken.filter((name) => !disabled.has(`pack:${pack.id}@${pack.version}:${name}`)).length;
35086
35835
  }
35087
35836
  } catch {}
35088
- try {
35089
- for (const artifact of listActiveCloudManagedPolicies()) {
35090
- if (artifact.effect !== "observe")
35091
- count++;
35092
- }
35093
- } catch {}
35837
+ count += cloudPolicyStatus().count;
35094
35838
  if (!hasInstalledRegexPacks())
35095
35839
  count += config.enabledPolicies.length;
35096
35840
  const projectDir = resolve30(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
@@ -35569,6 +36313,136 @@ var init_manager = __esm(() => {
35569
36313
  VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
35570
36314
  });
35571
36315
 
36316
+ // src/hooks/agent-skill.ts
36317
+ import { existsSync as existsSync38, mkdirSync as mkdirSync21, rmdirSync as rmdirSync2, rmSync as rmSync13 } from "node:fs";
36318
+ import { homedir as homedir30 } from "node:os";
36319
+ import { join as join37, resolve as resolve31 } from "node:path";
36320
+ function envDir(name, fallback) {
36321
+ const value = (process.env[name] ?? "").trim();
36322
+ return value ? resolve31(value) : fallback;
36323
+ }
36324
+ function agentSkillRoots(id) {
36325
+ const home = homedir30();
36326
+ const config = envDir("XDG_CONFIG_HOME", join37(home, ".config"));
36327
+ switch (id) {
36328
+ case "claude":
36329
+ return [join37(envDir("CLAUDE_CONFIG_DIR", join37(home, ".claude")), "skills")];
36330
+ case "codex":
36331
+ return [join37(envDir("CODEX_HOME", join37(home, ".codex")), "skills")];
36332
+ case "copilot":
36333
+ return [join37(envDir("COPILOT_HOME", join37(home, ".copilot")), "skills")];
36334
+ case "cursor":
36335
+ return [join37(home, ".cursor", "skills")];
36336
+ case "opencode":
36337
+ return [join37(config, "opencode", "skills")];
36338
+ case "pi":
36339
+ return [join37(envDir("PI_CODING_AGENT_DIR", join37(home, ".pi", "agent")), "skills")];
36340
+ case "hermes":
36341
+ return listHermesProfiles().map((p) => join37(p.home, "skills"));
36342
+ case "openclaw":
36343
+ return listOpenClawProfiles().map((p) => join37(p.home, "skills"));
36344
+ case "factory":
36345
+ return [join37(envDir("FACTORY_HOME_OVERRIDE", home), ".factory", "skills")];
36346
+ case "devin":
36347
+ return [join37(config, "devin", "skills")];
36348
+ case "antigravity":
36349
+ return [join37(home, ".gemini", "config", "skills")];
36350
+ case "goose": {
36351
+ const root = (process.env.GOOSE_PATH_ROOT ?? "").trim();
36352
+ return [root ? join37(resolve31(root), "config", "skills") : join37(config, "goose", "skills")];
36353
+ }
36354
+ }
36355
+ }
36356
+ function listedTwice(id) {
36357
+ return id === "codex" && existsSync38(join37(homedir30(), ".agents", "skills", AGENT_SKILL_NAME, "SKILL.md"));
36358
+ }
36359
+ function installAgentSkill(ids) {
36360
+ const installed = [];
36361
+ for (const id of ids) {
36362
+ let wrote = false;
36363
+ const covered = listedTwice(id);
36364
+ for (const root of agentSkillRoots(id)) {
36365
+ const dir = join37(root, AGENT_SKILL_NAME);
36366
+ const ours = existsSync38(join37(dir, AGENT_SKILL_MARKER));
36367
+ if (covered) {
36368
+ if (ours)
36369
+ removeSkillDir(dir);
36370
+ continue;
36371
+ }
36372
+ if (existsSync38(dir) && !ours)
36373
+ continue;
36374
+ let created = false;
36375
+ let marked = false;
36376
+ try {
36377
+ mkdirSync21(root, { recursive: true });
36378
+ if (!existsSync38(dir)) {
36379
+ mkdirSync21(dir);
36380
+ created = true;
36381
+ }
36382
+ writeConfigFileAtomic(join37(dir, AGENT_SKILL_MARKER), "", { backup: false, symlinks: "replace" });
36383
+ marked = true;
36384
+ writeConfigFileAtomic(join37(dir, "SKILL.md"), AGENT_SKILL_MD, { backup: false, symlinks: "replace" });
36385
+ wrote = true;
36386
+ } catch (err) {
36387
+ if (created && !marked) {
36388
+ try {
36389
+ rmdirSync2(dir);
36390
+ } catch {}
36391
+ }
36392
+ hookLogWarn(`the failproofai skill was not written to ${dir}: ${err instanceof Error ? err.message : String(err)}`);
36393
+ }
36394
+ }
36395
+ if (wrote)
36396
+ installed.push(id);
36397
+ }
36398
+ return installed;
36399
+ }
36400
+ function installedAgentSkillDirs() {
36401
+ const dirs = INTEGRATION_TYPES.flatMap((id) => agentSkillRoots(id).map((root) => join37(root, AGENT_SKILL_NAME)));
36402
+ return [...new Set(dirs)].filter((dir) => existsSync38(join37(dir, AGENT_SKILL_MARKER)));
36403
+ }
36404
+ function removeSkillDir(dir) {
36405
+ try {
36406
+ rmSync13(dir, { recursive: true, force: true });
36407
+ return true;
36408
+ } catch (err) {
36409
+ hookLogWarn(`the failproofai skill was not removed from ${dir}: ${err instanceof Error ? err.message : String(err)}`);
36410
+ return false;
36411
+ }
36412
+ }
36413
+ function removeAgentSkill() {
36414
+ return installedAgentSkillDirs().filter(removeSkillDir);
36415
+ }
36416
+ var AGENT_SKILL_NAME = "failproofai", AGENT_SKILL_MARKER = ".failproofai-managed", SKILLS_RAW = "https://raw.githubusercontent.com/FailproofAI/skills/main/skills", AGENT_SKILL_MD;
36417
+ var init_agent_skill = __esm(() => {
36418
+ init_hermes_profiles();
36419
+ init_openclaw_profiles();
36420
+ init_hook_logger();
36421
+ init_safe_config_write();
36422
+ init_types();
36423
+ AGENT_SKILL_MD = `---
36424
+ name: ${AGENT_SKILL_NAME}
36425
+ description: FailproofAI hooks, policies, daemon, Cloud, the fp CLI and the SDK. Use for any question about setting up, running or fixing FailproofAI.
36426
+ ---
36427
+
36428
+ # FailproofAI
36429
+
36430
+ This skill is a pointer. Before acting on anything FailproofAI, read the full skill, which is kept current online (use your web fetch tool, or \`curl -fsSL <url>\`):
36431
+
36432
+ ${SKILLS_RAW}/${AGENT_SKILL_NAME}/SKILL.md
36433
+
36434
+ Files it names by a relative path, such as \`references/cli.md\`, are in that same folder: ${SKILLS_RAW}/${AGENT_SKILL_NAME}/<path>
36435
+
36436
+ For focused work, read the matching skill too. Each has the same layout, with its own \`references/\` beside it.
36437
+
36438
+ - Writing or testing a policy: ${SKILLS_RAW}/failproofai-policy-author/SKILL.md
36439
+ - Publishing a policy pack: ${SKILLS_RAW}/failproofai-policy-publish/SKILL.md
36440
+ - FailproofAI Cloud and the \`fp\` CLI: ${SKILLS_RAW}/fp-cloud-cli/SKILL.md
36441
+ - Adding the SDK to an agent, or running an evaluator: ${SKILLS_RAW}/failproofai-sdk/SKILL.md
36442
+ - Planning evaluations: ${SKILLS_RAW}/failproofai-eval-brainstorm/SKILL.md
36443
+ `;
36444
+ });
36445
+
35572
36446
  // lib/fetch-with-timeout.ts
35573
36447
  function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
35574
36448
  const timeoutSignal = AbortSignal.timeout(timeoutMs);
@@ -35585,7 +36459,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 15000;
35585
36459
 
35586
36460
  // src/hooks/cloud-enrollment.ts
35587
36461
  import { randomUUID as randomUUID4 } from "node:crypto";
35588
- import { existsSync as existsSync38, readFileSync as readFileSync31, rmSync as rmSync13 } from "node:fs";
36462
+ import { existsSync as existsSync39, readFileSync as readFileSync32, rmSync as rmSync14 } from "node:fs";
35589
36463
  import { hostname as hostname4 } from "node:os";
35590
36464
  function cloudCredentialPath() {
35591
36465
  return process.env.FAILPROOFAI_CLOUD_CREDENTIALS ?? credentialsFile();
@@ -35624,10 +36498,10 @@ function readCloudCredentials() {
35624
36498
  if (!usingJsonOverride())
35625
36499
  return readCredentials().cloud ?? null;
35626
36500
  const path = cloudCredentialPath();
35627
- if (!existsSync38(path))
36501
+ if (!existsSync39(path))
35628
36502
  return null;
35629
36503
  try {
35630
- const raw = JSON.parse(readFileSync31(path, "utf8"));
36504
+ const raw = JSON.parse(readFileSync32(path, "utf8"));
35631
36505
  if (raw.schemaVersion !== SCHEMA_VERSION2)
35632
36506
  return null;
35633
36507
  if (typeof raw.url !== "string" || typeof raw.machineId !== "string" || typeof raw.token !== "string") {
@@ -35666,10 +36540,10 @@ function resolveMachineLabel(explicit) {
35666
36540
  function clearCloudCredentials() {
35667
36541
  if (usingJsonOverride()) {
35668
36542
  const path = cloudCredentialPath();
35669
- if (!existsSync38(path))
36543
+ if (!existsSync39(path))
35670
36544
  return false;
35671
36545
  try {
35672
- rmSync13(path);
36546
+ rmSync14(path);
35673
36547
  return true;
35674
36548
  } catch {
35675
36549
  return false;
@@ -35771,8 +36645,8 @@ async function introspectKey(origin, token, fetchImpl = fetch, timeoutMs = 1e4)
35771
36645
  identity: {
35772
36646
  keyId: typeof b.key_id === "string" ? b.key_id : undefined,
35773
36647
  orgId: typeof b.org_id === "string" ? b.org_id : undefined,
35774
- orgSlug: typeof b.org_slug === "string" ? b.org_slug : undefined,
35775
- orgName: typeof b.org_name === "string" ? b.org_name : undefined,
36648
+ orgSlug: displayText(b.org_slug),
36649
+ orgName: displayText(b.org_name),
35776
36650
  permissions: b.permissions.filter((p) => typeof p === "string"),
35777
36651
  expiresAt: typeof b.expires_at === "string" ? b.expires_at : null,
35778
36652
  otelIngest: typeof b.otel_ingest === "boolean" ? b.otel_ingest : undefined
@@ -35788,30 +36662,33 @@ function describeOrg(identity) {
35788
36662
  return identity.orgName ?? identity.orgSlug ?? identity.orgId ?? "an unknown organisation";
35789
36663
  }
35790
36664
  var INTROSPECT_PATH = "/v1/auth/introspect", PERMISSION_EVENTS = "events:add", PERMISSION_POLICIES = "policies:pull", PERMISSION_JEV = "jev:evaluate";
36665
+ var init_cloud_introspect = __esm(() => {
36666
+ init_cloud_login();
36667
+ });
35791
36668
 
35792
36669
  // src/hooks/credential-recovery.ts
35793
36670
  import { createHash as createHash11 } from "node:crypto";
35794
- import { existsSync as existsSync39, mkdirSync as mkdirSync21, readdirSync as readdirSync19, renameSync as renameSync13 } from "node:fs";
35795
- import { join as join37 } from "node:path";
36671
+ import { existsSync as existsSync40, mkdirSync as mkdirSync22, readdirSync as readdirSync19, renameSync as renameSync13 } from "node:fs";
36672
+ import { join as join38 } from "node:path";
35796
36673
  function previousOrgDirectory(id) {
35797
36674
  const safe = id && /^[A-Za-z0-9_-]{1,128}$/.test(id) ? id : id ? createHash11("sha256").update(id).digest("hex") : "unknown";
35798
- return join37(stateDir(), "previous-org", safe);
36675
+ return join38(stateDir(), "previous-org", safe);
35799
36676
  }
35800
36677
  function recoverCredentialRefusals(sameOrg, oldOrgId) {
35801
36678
  const dir = failedDir();
35802
- if (!existsSync39(dir))
36679
+ if (!existsSync40(dir))
35803
36680
  return 0;
35804
36681
  let recovered = 0;
35805
36682
  for (const name of readdirSync19(dir)) {
35806
36683
  if (!/\.c(401|403)\.jsonl$/.test(name) || name.startsWith("."))
35807
36684
  continue;
35808
- const targetDir = sameOrg ? dir : join37(previousOrgDirectory(oldOrgId), "failed");
35809
- mkdirSync21(targetDir, { recursive: true, mode: 448 });
36685
+ const targetDir = sameOrg ? dir : join38(previousOrgDirectory(oldOrgId), "failed");
36686
+ mkdirSync22(targetDir, { recursive: true, mode: 448 });
35810
36687
  const targetName = sameOrg ? name.replace(/\.c(401|403)(?=\.jsonl$)/, "") : name;
35811
- const target = join37(targetDir, targetName);
35812
- if (existsSync39(target))
36688
+ const target = join38(targetDir, targetName);
36689
+ if (existsSync40(target))
35813
36690
  continue;
35814
- renameSync13(join37(dir, name), target);
36691
+ renameSync13(join38(dir, name), target);
35815
36692
  recovered++;
35816
36693
  }
35817
36694
  if (sameOrg && recovered > 0)
@@ -35829,16 +36706,16 @@ import {
35829
36706
  chmodSync as chmodSync9,
35830
36707
  closeSync as closeSync5,
35831
36708
  constants as fsConstants5,
35832
- existsSync as existsSync40,
36709
+ existsSync as existsSync41,
35833
36710
  fstatSync as fstatSync3,
35834
36711
  linkSync,
35835
36712
  lstatSync as lstatSync5,
35836
- mkdirSync as mkdirSync22,
36713
+ mkdirSync as mkdirSync23,
35837
36714
  openSync as openSync5,
35838
- readFileSync as readFileSync32,
36715
+ readFileSync as readFileSync33,
35839
36716
  readSync as readSync4,
35840
36717
  renameSync as renameSync14,
35841
- rmSync as rmSync14,
36718
+ rmSync as rmSync15,
35842
36719
  statSync as statSync29,
35843
36720
  unlinkSync as unlinkSync6,
35844
36721
  writeFileSync as writeFileSync18
@@ -35876,7 +36753,7 @@ function repointCloudJevConfig(cloudBase, found) {
35876
36753
  }
35877
36754
  let raw = null;
35878
36755
  try {
35879
- const parsed = JSON.parse(readFileSync32(aside, "utf8"));
36756
+ const parsed = JSON.parse(readFileSync33(aside, "utf8"));
35880
36757
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
35881
36758
  raw = parsed;
35882
36759
  } catch {
@@ -35898,7 +36775,7 @@ function repointCloudJevConfig(cloudBase, found) {
35898
36775
  return found;
35899
36776
  } finally {
35900
36777
  try {
35901
- rmSync14(tmp, { force: true });
36778
+ rmSync15(tmp, { force: true });
35902
36779
  } catch {}
35903
36780
  }
35904
36781
  try {
@@ -35920,15 +36797,15 @@ function writeCloudJevConfigIfAbsent(cloudBase) {
35920
36797
  } catch {
35921
36798
  return { status: "error", path, problem: "the FailproofAI Cloud URL is not a URL" };
35922
36799
  }
35923
- if (existsSync40(path))
36800
+ if (existsSync41(path))
35924
36801
  return kept(path, cloudBase);
35925
36802
  const body = `${JSON.stringify({ provider: JEV_CLOUD_PROVIDER, baseUrl, mode: CLOUD_JEV_INITIAL_MODE }, null, 2)}
35926
36803
  `;
35927
36804
  const dir = dirname22(path);
35928
36805
  const tmp = `${path}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
35929
36806
  try {
35930
- if (!existsSync40(dir))
35931
- mkdirSync22(dir, { recursive: true, mode: 448 });
36807
+ if (!existsSync41(dir))
36808
+ mkdirSync23(dir, { recursive: true, mode: 448 });
35932
36809
  writeFileSync18(tmp, body, { mode: 384, flag: "wx" });
35933
36810
  chmodSync9(tmp, 384);
35934
36811
  try {
@@ -35942,7 +36819,7 @@ function writeCloudJevConfigIfAbsent(cloudBase) {
35942
36819
  return { status: "error", path, problem: `could not write ${path} (${err.code ?? "error"})` };
35943
36820
  } finally {
35944
36821
  try {
35945
- rmSync14(tmp, { force: true });
36822
+ rmSync15(tmp, { force: true });
35946
36823
  } catch {}
35947
36824
  }
35948
36825
  tightenDir(dir);
@@ -35950,7 +36827,7 @@ function writeCloudJevConfigIfAbsent(cloudBase) {
35950
36827
  }
35951
36828
  function existingJevConfig(cloudBase) {
35952
36829
  const path = jevConfigPath();
35953
- return existsSync40(path) ? kept(path, cloudBase) : null;
36830
+ return existsSync41(path) ? kept(path, cloudBase) : null;
35954
36831
  }
35955
36832
  function cloudJevRunningMode() {
35956
36833
  try {
@@ -36049,7 +36926,7 @@ function putBack(aside, path, mode) {
36049
36926
  }
36050
36927
  let created = false;
36051
36928
  try {
36052
- const bytes = readFileSync32(aside);
36929
+ const bytes = readFileSync33(aside);
36053
36930
  const fd = openSync5(path, "wx", mode);
36054
36931
  created = true;
36055
36932
  try {
@@ -36122,7 +36999,7 @@ var init_jev_cloud_connection = __esm(() => {
36122
36999
  });
36123
37000
 
36124
37001
  // src/hooks/cloud-connection.ts
36125
- import { rmSync as rmSync15 } from "node:fs";
37002
+ import { rmSync as rmSync16 } from "node:fs";
36126
37003
  function ingestUrlFor(baseUrl) {
36127
37004
  return `${baseUrl.replace(/\/$/, "")}${INGEST_PATH}`;
36128
37005
  }
@@ -36164,6 +37041,7 @@ async function connectToCloud(input) {
36164
37041
  };
36165
37042
  }
36166
37043
  const known = identity.kind === "ok" ? identity.identity : null;
37044
+ const org = known ? { id: known.orgId, slug: known.orgSlug, name: known.orgName } : input.org;
36167
37045
  const canIngest = known ? hasPermission(known, PERMISSION_EVENTS) : true;
36168
37046
  const canPolicy = known ? hasPermission(known, PERMISSION_POLICIES) : true;
36169
37047
  const missing = (permission) => `that key does not carry \`${permission}\`` + (known ? ` (it is valid for ${describeOrg(known)})` : "");
@@ -36175,7 +37053,7 @@ async function connectToCloud(input) {
36175
37053
  policy: policyResult.ok ? { ok: true, policyCount: policyResult.policyCount, deployment: policyResult.deployment } : { ok: false, reason: policyResult.reason },
36176
37054
  ingest: ingestResult.ok ? { ok: true } : { ok: false, reason: ingestResult.reason },
36177
37055
  anyConfigured: false,
36178
- org: known ? { id: known.orgId, slug: known.orgSlug, name: known.orgName } : undefined
37056
+ org
36179
37057
  };
36180
37058
  outcome.anyConfigured = policyResult.ok || ingestResult.ok;
36181
37059
  if (!outcome.anyConfigured)
@@ -36197,13 +37075,13 @@ async function connectToCloud(input) {
36197
37075
  prev.ingest ? { token: prev.ingest.key, url: prev.ingest.url } : undefined,
36198
37076
  prev.jev ? { token: prev.jev.key, url: prev.jev.url } : undefined
36199
37077
  ].some((slot) => slot && (slot.token !== input.token || !sameOrigin(slot.url)));
36200
- const sameKnownOrg = Boolean(prev.org?.id && known?.orgId && prev.org.id === known.orgId);
36201
- const orgSwitch = hadConnection && !sameKnownOrg && (prev.org?.id && known?.orgId && prev.org.id !== known.orgId || (!prev.org?.id || !known?.orgId) && keyChanged || previousPolicy && !sameOrigin(previousPolicy.url) || prev.ingest && !sameOrigin(prev.ingest.url));
37078
+ const sameKnownOrg = Boolean(prev.org?.id && org?.id && prev.org.id === org.id);
37079
+ const orgSwitch = hadConnection && !sameKnownOrg && (prev.org?.id && org?.id && prev.org.id !== org.id || (!prev.org?.id || !org?.id) && keyChanged || previousPolicy && !sameOrigin(previousPolicy.url) || prev.ingest && !sameOrigin(prev.ingest.url));
36202
37080
  const boundary = orgSwitch ? next.connectedAt : prev.org?.since;
36203
37081
  if (orgSwitch) {
36204
- rmSync15(backfillRequestPath(), { force: true });
37082
+ rmSync16(backfillRequestPath(), { force: true });
36205
37083
  const oldOrg = prev.org?.name ?? prev.org?.slug ?? prev.org?.id ?? "the previous org";
36206
- const newOrg = known?.orgName ?? known?.orgSlug ?? known?.orgId ?? "the new org";
37084
+ const newOrg = org?.name ?? org?.slug ?? org?.id ?? "the new org";
36207
37085
  changes.push(`Switched from ${oldOrg} to ${newOrg}. Nothing recorded before now will be sent to ${newOrg}; ${oldOrg}'s cloud policies stop within ~30 s.`);
36208
37086
  clearActiveCloudManagedPolicies();
36209
37087
  }
@@ -36231,15 +37109,15 @@ async function connectToCloud(input) {
36231
37109
  changes.push(`Removed the previous Jev key ${maskToken(prev.jev.key)}: this key's ${PERMISSION_JEV} permission was not verified.`);
36232
37110
  }
36233
37111
  const since = boundary === undefined ? {} : { since: boundary };
36234
- if (known) {
36235
- next.org = { id: known.orgId, slug: known.orgSlug, name: known.orgName, ...since };
37112
+ if (org) {
37113
+ next.org = { ...org, ...since };
36236
37114
  } else {
36237
37115
  const { since: _drop, ...kept } = orgSwitch ? {} : prev.org ?? {};
36238
37116
  next.org = { ...kept, ...since };
36239
37117
  }
36240
37118
  writeCredentials(next);
36241
37119
  if (ingestResult.ok && prev.ingest && prev.ingest.key !== input.token) {
36242
- recoverCredentialRefusals(Boolean(prev.org?.id && prev.org.id === known?.orgId && sameOrigin(prev.ingest.url)), prev.org?.id);
37120
+ recoverCredentialRefusals(Boolean(prev.org?.id && prev.org.id === org?.id && sameOrigin(prev.ingest.url)), prev.org?.id);
36243
37121
  }
36244
37122
  if (process.env.FAILPROOFAI_CLOUD_CREDENTIALS) {
36245
37123
  if (policyResult.ok)
@@ -36328,47 +37206,23 @@ function jevLines(outcome) {
36328
37206
  }
36329
37207
  const config = jev.config;
36330
37208
  if (!config || config.status === "written" || config.status === "repointed") {
36331
- const plainHttp = config !== undefined && new URL(config.baseUrl).protocol === "http:";
36332
- const httpLine = " Over plain http Jev runs only in observe mode: enforce (set here or by FailproofAI Cloud) turns it off on this machine. For enforce, reconnect with `failproofai config --token <key> --url https://…`.";
36333
37209
  const lead = config?.status === "repointed" ? [` Jev ${config.path} now points here instead of ${config.from}.`] : [];
36334
37210
  const indent = lead.length > 0 ? " " : " Jev ";
36335
37211
  const cloudJev = safeCloudJev();
36336
- if (cloudJev.jevMode !== null) {
36337
- if (cloudJev.jevMode === "off")
36338
- return [...lead, `${indent}off on this machine: FailproofAI Cloud set its Jev mode to off.`];
36339
- if (plainHttp && cloudJev.jevMode === "enforce") {
36340
- return [
36341
- ...lead,
36342
- `${indent}off on this machine: FailproofAI Cloud set enforce, and over plain http Jev runs only in observe mode. For enforce, reconnect with \`failproofai config --token <key> --url https://…\`.`
36343
- ];
36344
- }
36345
- return [
36346
- ...lead,
36347
- `${indent}on through FailproofAI Cloud, mode ${cloudJev.jevMode} (set by FailproofAI Cloud); the Jev checks it deployed here run on FailproofAI Cloud.`
36348
- ];
36349
- }
36350
- const localMode = config?.status === "repointed" ? config.mode : "observe";
36351
- if (localMode === "off") {
36352
- return [...lead, `${indent}switched off in ${config?.path ?? "jev.json"} (mode off). FailproofAI Cloud's Jev mode, if it sets one, arrives with its first deployment. To turn it on: \`failproofai jev setup --mode observe\`.`];
36353
- }
36354
- return [
36355
- ...lead,
36356
- `${indent}configured through FailproofAI Cloud; waiting for FailproofAI Cloud's Jev mode, which arrives with this machine's first deployment (within ~30 s) and decides. Until then, and if Cloud sets none: ${localMode} (${config?.path ?? "jev.json"}). Check with \`failproofai jev status\`.`,
36357
- ...jevChecksDeclared() ? [] : [" No Jev checks are installed on this machine; checks FailproofAI Cloud deploys here run on Cloud. To add local ones: `failproofai policies add FailproofAI/jev-policies`."],
36358
- ...plainHttp ? [httpLine] : []
36359
- ];
37212
+ const state = cloudJev.jevMode === null ? "waiting for its first deployment" : cloudJev.jevMode === "off" ? "off — no Jev policy deployed" : cloudJev.jevMode;
37213
+ return [...lead, `${indent}managed by FailproofAI Cloud · ${state}`];
36360
37214
  }
36361
37215
  if (config.status === "kept") {
36362
37216
  const lines = [` Jev key stored; ${config.path} already exists and was left as configured.`];
36363
37217
  if (config.otherOrigin) {
36364
- lines.push(config.jevOff?.why === "off" ? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here and turn it on: \`failproofai jev setup --provider failproofai --mode observe\`.` : ` It points at ${config.otherOrigin}, so Jev stays off until you run \`failproofai jev setup --provider failproofai\`.`);
37218
+ lines.push(config.jevOff?.why === "off" ? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here: \`failproofai jev setup --provider failproofai\`.` : ` It points at ${config.otherOrigin}, so Jev stays off until you run \`failproofai jev setup --provider failproofai\`.`);
36365
37219
  } else if (config.jevOff?.why === "refused") {
36366
37220
  lines.push(` It was refused (${config.jevOff.problem}), so Jev is off. ${config.jevOff.fix ? `Fix: \`${config.jevOff.fix}\`.` : "Write a valid one: `failproofai jev setup --provider failproofai`."}`);
36367
37221
  } else if (config.jevOff?.why === "off") {
36368
- lines.push(" It has Jev switched off (mode off), so Jev is off. To turn it on: `failproofai jev setup --mode observe`.");
37222
+ lines.push(" It has Jev switched off (mode off) until FailproofAI Cloud's Jev mode arrives with this machine's first deployment; from then on FailproofAI Cloud decides.");
36369
37223
  }
36370
37224
  if (jev.stillOn) {
36371
- lines.push(` Jev is still on through FailproofAI Cloud (${jev.stillOn} mode): it sends each checked tool call and the recent prompt to FailproofAI Cloud. To switch it off: \`failproofai jev setup --mode off\`.`);
37225
+ lines.push(` Jev is still on through FailproofAI Cloud (${jev.stillOn} mode): it sends each checked tool call and the recent prompt to FailproofAI Cloud. FailproofAI Cloud sets its mode: off when no Jev policy is deployed here.`);
36372
37226
  }
36373
37227
  return lines;
36374
37228
  }
@@ -36427,7 +37281,7 @@ var init_cloud_connection = __esm(() => {
36427
37281
  init_cloud_enrollment();
36428
37282
  init_fp_home();
36429
37283
  init_fp_config();
36430
- init_effective_reviewers();
37284
+ init_cloud_introspect();
36431
37285
  init_collector_config();
36432
37286
  init_credential_recovery();
36433
37287
  init_cloud_managed_policies();
@@ -36435,14 +37289,14 @@ var init_cloud_connection = __esm(() => {
36435
37289
  });
36436
37290
 
36437
37291
  // src/hooks/setup-state.ts
36438
- import { existsSync as existsSync41, realpathSync as realpathSync4, statSync as statSync30 } from "node:fs";
36439
- import { dirname as dirname23, resolve as resolve31 } from "node:path";
36440
- import { homedir as homedir30 } from "node:os";
36441
- function globalConfigPath(home = homedir30()) {
37292
+ import { existsSync as existsSync42, realpathSync as realpathSync4, statSync as statSync30 } from "node:fs";
37293
+ import { dirname as dirname23, resolve as resolve32 } from "node:path";
37294
+ import { homedir as homedir31 } from "node:os";
37295
+ function globalConfigPath(home = homedir31()) {
36442
37296
  return globalPolicyConfigFile(home);
36443
37297
  }
36444
37298
  function projectConfigPath(projectRoot) {
36445
- return resolve31(projectRoot, ".failproofai", "policies-config.json");
37299
+ return resolve32(projectRoot, ".failproofai", "policies-config.json");
36446
37300
  }
36447
37301
  function legacyMarkerPath(home) {
36448
37302
  return launcherMarker(home);
@@ -36454,18 +37308,18 @@ function isDir(path) {
36454
37308
  return false;
36455
37309
  }
36456
37310
  }
36457
- function findProjectRoot2(start, home = homedir30()) {
37311
+ function findProjectRoot2(start, home = homedir31()) {
36458
37312
  const resolvedHome = realResolve(home);
36459
37313
  let dir = realResolve(start);
36460
37314
  while (dir !== resolvedHome) {
36461
- if (isDir(resolve31(dir, ".failproofai")))
37315
+ if (isDir(resolve32(dir, ".failproofai")))
36462
37316
  return dir;
36463
37317
  const parent = dirname23(dir);
36464
37318
  if (parent === dir)
36465
37319
  break;
36466
37320
  dir = parent;
36467
37321
  }
36468
- return resolve31(start);
37322
+ return resolve32(start);
36469
37323
  }
36470
37324
  function hasGlobalHooksInstalled() {
36471
37325
  for (const id of INTEGRATION_TYPES) {
@@ -36477,14 +37331,14 @@ function hasGlobalHooksInstalled() {
36477
37331
  return false;
36478
37332
  }
36479
37333
  function realResolve(path) {
36480
- const lexical = resolve31(path);
37334
+ const lexical = resolve32(path);
36481
37335
  try {
36482
37336
  return realpathSync4(lexical);
36483
37337
  } catch {
36484
37338
  return lexical;
36485
37339
  }
36486
37340
  }
36487
- function detectSetupState(cwd = process.cwd(), home = homedir30()) {
37341
+ function detectSetupState(cwd = process.cwd(), home = homedir31()) {
36488
37342
  const resolvedCwd = realResolve(cwd);
36489
37343
  const resolvedHome = realResolve(home);
36490
37344
  const projectRoot = findProjectRoot2(resolvedCwd, resolvedHome);
@@ -36492,10 +37346,10 @@ function detectSetupState(cwd = process.cwd(), home = homedir30()) {
36492
37346
  cwd: resolvedCwd,
36493
37347
  home: resolvedHome,
36494
37348
  projectRoot,
36495
- hasGlobalConfig: existsSync41(globalConfigPath(resolvedHome)),
36496
- hasProjectConfig: projectRoot !== resolvedHome && existsSync41(projectConfigPath(projectRoot)),
37349
+ hasGlobalConfig: existsSync42(globalConfigPath(resolvedHome)),
37350
+ hasProjectConfig: projectRoot !== resolvedHome && existsSync42(projectConfigPath(projectRoot)),
36497
37351
  hasGlobalHooks: hasGlobalHooksInstalled(),
36498
- hasLegacyMarker: existsSync41(legacyMarkerPath(resolvedHome)),
37352
+ hasLegacyMarker: existsSync42(legacyMarkerPath(resolvedHome)),
36499
37353
  inHomeDir: resolvedCwd === resolvedHome
36500
37354
  };
36501
37355
  }
@@ -36509,14 +37363,14 @@ var init_setup_state = __esm(() => {
36509
37363
  });
36510
37364
 
36511
37365
  // src/hooks/onboarding-attempt.ts
36512
- import { existsSync as existsSync42, mkdirSync as mkdirSync23, readFileSync as readFileSync33, rmSync as rmSync16, writeFileSync as writeFileSync19 } from "node:fs";
37366
+ import { existsSync as existsSync43, mkdirSync as mkdirSync24, readFileSync as readFileSync34, rmSync as rmSync17, writeFileSync as writeFileSync19 } from "node:fs";
36513
37367
  import { dirname as dirname24 } from "node:path";
36514
37368
  function readOnboardingAttempt() {
36515
37369
  const path = onboardingAttemptFile();
36516
- if (!existsSync42(path))
37370
+ if (!existsSync43(path))
36517
37371
  return null;
36518
37372
  try {
36519
- const raw = JSON.parse(readFileSync33(path, "utf8"));
37373
+ const raw = JSON.parse(readFileSync34(path, "utf8"));
36520
37374
  if (raw.schemaVersion !== SCHEMA_VERSION3)
36521
37375
  return null;
36522
37376
  if (typeof raw.reason !== "string" || typeof raw.cliVersion !== "string")
@@ -36535,7 +37389,7 @@ function readOnboardingAttempt() {
36535
37389
  function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.now()) {
36536
37390
  try {
36537
37391
  const path = onboardingAttemptFile();
36538
- mkdirSync23(dirname24(path), { recursive: true });
37392
+ mkdirSync24(dirname24(path), { recursive: true });
36539
37393
  const record = {
36540
37394
  schemaVersion: SCHEMA_VERSION3,
36541
37395
  reason,
@@ -36548,7 +37402,7 @@ function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.no
36548
37402
  }
36549
37403
  function clearOnboardingAttempt() {
36550
37404
  try {
36551
- rmSync16(onboardingAttemptFile(), { force: true });
37405
+ rmSync17(onboardingAttemptFile(), { force: true });
36552
37406
  } catch {}
36553
37407
  }
36554
37408
  function blockerCleared(attempt, probe) {
@@ -36587,9 +37441,9 @@ var init_onboarding_attempt = __esm(() => {
36587
37441
  });
36588
37442
 
36589
37443
  // src/hooks/onboarding-lock.ts
36590
- import { mkdirSync as mkdirSync24, readFileSync as readFileSync34, rmSync as rmSync17, writeFileSync as writeFileSync20 } from "node:fs";
36591
- import { homedir as homedir31 } from "node:os";
36592
- function onboardingLockPath(home = homedir31()) {
37444
+ import { mkdirSync as mkdirSync25, readFileSync as readFileSync35, rmSync as rmSync18, writeFileSync as writeFileSync20 } from "node:fs";
37445
+ import { homedir as homedir32 } from "node:os";
37446
+ function onboardingLockPath(home = homedir32()) {
36593
37447
  return onboardingLockFile(home);
36594
37448
  }
36595
37449
  function processAlive(pid) {
@@ -36604,7 +37458,7 @@ function processAlive(pid) {
36604
37458
  }
36605
37459
  function readLock(path) {
36606
37460
  try {
36607
- const parsed = JSON.parse(readFileSync34(path, "utf8"));
37461
+ const parsed = JSON.parse(readFileSync35(path, "utf8"));
36608
37462
  if (typeof parsed.pid !== "number")
36609
37463
  return null;
36610
37464
  return { pid: parsed.pid, startedAt: Number(parsed.startedAt) || 0 };
@@ -36612,13 +37466,13 @@ function readLock(path) {
36612
37466
  return null;
36613
37467
  }
36614
37468
  }
36615
- function acquireOnboardingLock(home = homedir31()) {
37469
+ function acquireOnboardingLock(home = homedir32()) {
36616
37470
  const path = onboardingLockPath(home);
36617
37471
  try {
36618
37472
  const held = readLock(path);
36619
37473
  if (held && held.pid !== process.pid && processAlive(held.pid))
36620
37474
  return null;
36621
- mkdirSync24(stateDir(home), { recursive: true });
37475
+ mkdirSync25(stateDir(home), { recursive: true });
36622
37476
  const body = { pid: process.pid, startedAt: Date.now() };
36623
37477
  writeFileSync20(path, JSON.stringify(body), "utf8");
36624
37478
  let released = false;
@@ -36630,7 +37484,7 @@ function acquireOnboardingLock(home = homedir31()) {
36630
37484
  try {
36631
37485
  const current = readLock(path);
36632
37486
  if (!current || current.pid === process.pid)
36633
- rmSync17(path, { force: true });
37487
+ rmSync18(path, { force: true });
36634
37488
  } catch {}
36635
37489
  }
36636
37490
  };
@@ -36643,8 +37497,8 @@ var init_onboarding_lock = __esm(() => {
36643
37497
  });
36644
37498
 
36645
37499
  // lib/claude-sessions.ts
36646
- import { readdirSync as readdirSync20, statSync as statSync31, existsSync as existsSync43 } from "node:fs";
36647
- import { join as join38, basename as basename9 } from "node:path";
37500
+ import { readdirSync as readdirSync20, statSync as statSync31, existsSync as existsSync44 } from "node:fs";
37501
+ import { join as join39, basename as basename9 } from "node:path";
36648
37502
  function getClaudeProjectsRoot() {
36649
37503
  return getClaudeProjectsPath();
36650
37504
  }
@@ -36659,7 +37513,7 @@ function listClaudeProjects() {
36659
37513
  return entries.filter((e) => e.isDirectory()).map((e) => ({
36660
37514
  name: e.name,
36661
37515
  cwd: decodeFolderName(e.name),
36662
- path: join38(root, e.name)
37516
+ path: join39(root, e.name)
36663
37517
  }));
36664
37518
  }
36665
37519
  function listClaudeTranscripts(project) {
@@ -36675,7 +37529,7 @@ function listClaudeTranscripts(project) {
36675
37529
  const sessionId = entry.name.slice(0, -".jsonl".length);
36676
37530
  if (!UUID_RE2.test(sessionId))
36677
37531
  continue;
36678
- const transcriptPath = join38(project.path, entry.name);
37532
+ const transcriptPath = join39(project.path, entry.name);
36679
37533
  try {
36680
37534
  const s = statSync31(transcriptPath);
36681
37535
  out.push({
@@ -36689,8 +37543,8 @@ function listClaudeTranscripts(project) {
36689
37543
  });
36690
37544
  } catch {}
36691
37545
  } else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
36692
- const subDir = join38(project.path, entry.name, "subagents");
36693
- if (!existsSync43(subDir))
37546
+ const subDir = join39(project.path, entry.name, "subagents");
37547
+ if (!existsSync44(subDir))
36694
37548
  continue;
36695
37549
  let subEntries;
36696
37550
  try {
@@ -36702,7 +37556,7 @@ function listClaudeTranscripts(project) {
36702
37556
  if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
36703
37557
  continue;
36704
37558
  const agentId = sub.name.slice(0, -".jsonl".length);
36705
- const transcriptPath = join38(subDir, sub.name);
37559
+ const transcriptPath = join39(subDir, sub.name);
36706
37560
  try {
36707
37561
  const s = statSync31(transcriptPath);
36708
37562
  out.push({
@@ -38039,8 +38893,8 @@ var init_features = __esm(() => {
38039
38893
 
38040
38894
  // src/audit/cache.ts
38041
38895
  import { createHash as createHash12 } from "node:crypto";
38042
- import { existsSync as existsSync44, mkdirSync as mkdirSync25, readFileSync as readFileSync35, writeFileSync as writeFileSync21, chmodSync as chmodSync10, openSync as openSync6, readSync as readSync5, closeSync as closeSync6 } from "node:fs";
38043
- import { join as join39 } from "node:path";
38896
+ import { existsSync as existsSync45, mkdirSync as mkdirSync26, readFileSync as readFileSync36, writeFileSync as writeFileSync21, chmodSync as chmodSync10, openSync as openSync6, readSync as readSync5, closeSync as closeSync6 } from "node:fs";
38897
+ import { join as join40 } from "node:path";
38044
38898
  function getEngineVersion() {
38045
38899
  if (cachedEngineVersion)
38046
38900
  return cachedEngineVersion;
@@ -38060,7 +38914,7 @@ function getDetectorVersion() {
38060
38914
  function getCachePathFor(transcriptPath) {
38061
38915
  const root = auditCacheDir();
38062
38916
  const key = createHash12("sha1").update(transcriptPath).digest("hex");
38063
- return join39(root, `${key}.json`);
38917
+ return join40(root, `${key}.json`);
38064
38918
  }
38065
38919
  function encodeDetectorState(state) {
38066
38920
  return JSON.stringify(state, (_k, v) => v instanceof Map ? { __map: [...v.entries()] } : v);
@@ -38100,10 +38954,10 @@ function readCachedTranscript(transcriptPath, mtimeMs, sizeBytes) {
38100
38954
  if (sizeBytes === 0)
38101
38955
  return null;
38102
38956
  const cachePath = getCachePathFor(transcriptPath);
38103
- if (!existsSync44(cachePath))
38957
+ if (!existsSync45(cachePath))
38104
38958
  return null;
38105
38959
  try {
38106
- const raw = readFileSync35(cachePath, "utf-8");
38960
+ const raw = readFileSync36(cachePath, "utf-8");
38107
38961
  const entry = JSON.parse(raw);
38108
38962
  if (entry.schemaVersion !== CACHE_SCHEMA_VERSION)
38109
38963
  return null;
@@ -38142,7 +38996,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result,
38142
38996
  return;
38143
38997
  const cachePath = getCachePathFor(transcriptPath);
38144
38998
  try {
38145
- mkdirSync25(auditCacheDir(), { recursive: true });
38999
+ mkdirSync26(auditCacheDir(), { recursive: true });
38146
39000
  const entry = {
38147
39001
  schemaVersion: CACHE_SCHEMA_VERSION,
38148
39002
  cachedAt: Date.now(),
@@ -38627,9 +39481,9 @@ var init_audit = __esm(() => {
38627
39481
  import {
38628
39482
  closeSync as closeSync7,
38629
39483
  linkSync as linkSync2,
38630
- mkdirSync as mkdirSync26,
39484
+ mkdirSync as mkdirSync27,
38631
39485
  openSync as openSync7,
38632
- readFileSync as readFileSync36,
39486
+ readFileSync as readFileSync37,
38633
39487
  unlinkSync as unlinkSync7,
38634
39488
  writeFileSync as writeFileSync22,
38635
39489
  writeSync as writeSync2
@@ -38690,7 +39544,7 @@ function handleFor(path, info) {
38690
39544
  }
38691
39545
  function readLock2(path) {
38692
39546
  try {
38693
- const parsed = JSON.parse(readFileSync36(path, "utf8"));
39547
+ const parsed = JSON.parse(readFileSync37(path, "utf8"));
38694
39548
  if (typeof parsed?.pid !== "number" || typeof parsed?.startedAt !== "number")
38695
39549
  return null;
38696
39550
  return {
@@ -38740,8 +39594,8 @@ function createThenWrite(path, body) {
38740
39594
  }
38741
39595
  }
38742
39596
  function ensureRunDir() {
38743
- mkdirSync26(failproofaiHome(), { recursive: true });
38744
- mkdirSync26(runDir(), { recursive: true, mode: 448 });
39597
+ mkdirSync27(failproofaiHome(), { recursive: true });
39598
+ mkdirSync27(runDir(), { recursive: true, mode: 448 });
38745
39599
  }
38746
39600
  var AUDIT_LOCK_MAX_AGE_MS;
38747
39601
  var init_audit_lock = __esm(() => {
@@ -38750,7 +39604,7 @@ var init_audit_lock = __esm(() => {
38750
39604
  });
38751
39605
 
38752
39606
  // src/audit/dashboard-cache.ts
38753
- import { existsSync as existsSync45, readFileSync as readFileSync37 } from "node:fs";
39607
+ import { existsSync as existsSync46, readFileSync as readFileSync38 } from "node:fs";
38754
39608
  function getCachePath() {
38755
39609
  return auditDashboardFile();
38756
39610
  }
@@ -38770,10 +39624,10 @@ function writeDashboardCache(params, result) {
38770
39624
  }
38771
39625
  function readDashboardCacheMeta() {
38772
39626
  const cachePath = getCachePath();
38773
- if (!existsSync45(cachePath))
39627
+ if (!existsSync46(cachePath))
38774
39628
  return null;
38775
39629
  try {
38776
- const raw = readFileSync37(cachePath, "utf-8");
39630
+ const raw = readFileSync38(cachePath, "utf-8");
38777
39631
  const entry = JSON.parse(raw);
38778
39632
  if (!entry || entry.schemaVersion !== DASHBOARD_CACHE_SCHEMA_VERSION || typeof entry.cachedAt !== "string" || Number.isNaN(new Date(entry.cachedAt).getTime()))
38779
39633
  return null;
@@ -38822,47 +39676,6 @@ function sanitizeErrorMessage(err) {
38822
39676
  var MAX_ERROR_MESSAGE_LEN = 300;
38823
39677
  var init_telemetry_sanitize = () => {};
38824
39678
 
38825
- // src/audit/open-browser.ts
38826
- import { spawn } from "node:child_process";
38827
- function openUrl(url) {
38828
- let cmd;
38829
- let args;
38830
- if (process.platform === "darwin") {
38831
- cmd = "open";
38832
- args = [url];
38833
- } else if (process.platform === "win32") {
38834
- cmd = "cmd";
38835
- args = ["/c", "start", "", url];
38836
- } else {
38837
- cmd = "xdg-open";
38838
- args = [url];
38839
- }
38840
- try {
38841
- const child = spawn(cmd, args, { stdio: "ignore", detached: true });
38842
- child.on("error", () => {});
38843
- child.unref();
38844
- } catch {}
38845
- }
38846
- function openWhenReady(port, path) {
38847
- const base = `http://localhost:${port}`;
38848
- const target = `${base}${path}`;
38849
- (async () => {
38850
- const deadline = Date.now() + POLL_TIMEOUT_MS;
38851
- while (Date.now() < deadline) {
38852
- try {
38853
- await fetch(base, { method: "GET", redirect: "manual" });
38854
- openUrl(target);
38855
- return;
38856
- } catch {
38857
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
38858
- }
38859
- }
38860
- openUrl(target);
38861
- })();
38862
- }
38863
- var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
38864
- var init_open_browser = () => {};
38865
-
38866
39679
  // lib/telemetry.ts
38867
39680
  function trackEvent(name, properties) {
38868
39681
  if (!isTelemetryEnabled())
@@ -38950,7 +39763,7 @@ async function fetchWithTimeout2(url, init) {
38950
39763
  throw err;
38951
39764
  }
38952
39765
  }
38953
- async function postJson2(path, body, init) {
39766
+ async function postJson3(path, body, init) {
38954
39767
  const headers = { "content-type": "application/json" };
38955
39768
  if (init?.accessToken)
38956
39769
  headers["authorization"] = `Bearer ${init.accessToken}`;
@@ -38966,18 +39779,18 @@ async function postJson2(path, body, init) {
38966
39779
  return await res.json();
38967
39780
  }
38968
39781
  async function requestLoginCode(email) {
38969
- return postJson2("/v0/auth/login/request", { email });
39782
+ return postJson3("/v0/auth/login/request", { email });
38970
39783
  }
38971
39784
  async function verifyLoginCode(email, code) {
38972
- return postJson2("/v0/auth/login/verify", { email, code });
39785
+ return postJson3("/v0/auth/login/verify", { email, code });
38973
39786
  }
38974
39787
  async function refreshAccessToken(refreshToken) {
38975
- return postJson2("/v0/auth/token/refresh", {
39788
+ return postJson3("/v0/auth/token/refresh", {
38976
39789
  refresh_token: refreshToken
38977
39790
  });
38978
39791
  }
38979
39792
  async function submitAuditReport(accessToken, body) {
38980
- return postJson2("/v0/audit-reports", body, { accessToken });
39793
+ return postJson3("/v0/audit-reports", body, { accessToken });
38981
39794
  }
38982
39795
  var DEFAULT_API_BASE = "https://api.befailproof.ai", AuthApiError, REQUEST_TIMEOUT_MS = 1e4;
38983
39796
  var init_api_server_client = __esm(() => {
@@ -38997,18 +39810,18 @@ var init_api_server_client = __esm(() => {
38997
39810
  });
38998
39811
 
38999
39812
  // lib/auth/auth-store.ts
39000
- import { existsSync as existsSync46, readFileSync as readFileSync38, readdirSync as readdirSync21, rmSync as rmSync18 } from "node:fs";
39001
- import { join as join40, resolve as resolve32 } from "node:path";
39813
+ import { existsSync as existsSync47, readFileSync as readFileSync39, readdirSync as readdirSync21, rmSync as rmSync19 } from "node:fs";
39814
+ import { join as join41, resolve as resolve33 } from "node:path";
39002
39815
  function getAuthFilePath() {
39003
39816
  const override = process.env.FAILPROOFAI_AUTH_DIR;
39004
- return override ? join40(override, "session.json") : auditSessionFile();
39817
+ return override ? join41(override, "session.json") : auditSessionFile();
39005
39818
  }
39006
39819
  function readAuth() {
39007
39820
  const p = getAuthFilePath();
39008
- if (!existsSync46(p))
39821
+ if (!existsSync47(p))
39009
39822
  return null;
39010
39823
  try {
39011
- const raw = readFileSync38(p, "utf-8");
39824
+ const raw = readFileSync39(p, "utf-8");
39012
39825
  const parsed = JSON.parse(raw);
39013
39826
  if (typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.access_expires_at !== "number" || typeof parsed.user !== "object" || !parsed.user || typeof parsed.user.id !== "string" || typeof parsed.user.email !== "string") {
39014
39827
  return null;
@@ -39032,18 +39845,18 @@ function writeAuth(auth) {
39032
39845
  }
39033
39846
  function deleteAuth() {
39034
39847
  const p = getAuthFilePath();
39035
- if (existsSync46(p))
39036
- rmSync18(p, { force: true });
39848
+ if (existsSync47(p))
39849
+ rmSync19(p, { force: true });
39037
39850
  try {
39038
39851
  const root = migrationsDir();
39039
- if (!existsSync46(root))
39852
+ if (!existsSync47(root))
39040
39853
  return;
39041
39854
  for (const entry of readdirSync21(root)) {
39042
39855
  if (!entry.startsWith("backup-layout"))
39043
39856
  continue;
39044
- const copy = resolve32(root, entry, "auth.json");
39045
- if (existsSync46(copy))
39046
- rmSync18(copy, { force: true });
39857
+ const copy = resolve33(root, entry, "auth.json");
39858
+ if (existsSync47(copy))
39859
+ rmSync19(copy, { force: true });
39047
39860
  }
39048
39861
  } catch {}
39049
39862
  }
@@ -39104,7 +39917,7 @@ var init_auth_store = __esm(() => {
39104
39917
  });
39105
39918
 
39106
39919
  // src/audit/redact-example.ts
39107
- import { homedir as homedir32 } from "node:os";
39920
+ import { homedir as homedir33 } from "node:os";
39108
39921
  function maskTruncatedSecret(input) {
39109
39922
  for (const [pattern, label] of SECRET_PREFIXES) {
39110
39923
  if (pattern.test(input)) {
@@ -39139,7 +39952,7 @@ function maskSecrets(input) {
39139
39952
  }
39140
39953
  return out;
39141
39954
  }
39142
- function shortenPaths(input, home = homedir32()) {
39955
+ function shortenPaths(input, home = homedir33()) {
39143
39956
  const homeRoot = home.replace(/\/+$/, "");
39144
39957
  return input.replace(ABSOLUTE_PATH_RE, (match, offset, whole) => {
39145
39958
  if (PUBLIC_PATH_ROOTS.some((root) => match.startsWith(root)))
@@ -39170,7 +39983,7 @@ function shortenPaths(input, home = homedir32()) {
39170
39983
  return `${root}${middle}/${tail}${trailingSlash ? "/" : ""}`;
39171
39984
  });
39172
39985
  }
39173
- function redactExample(input, home = homedir32()) {
39986
+ function redactExample(input, home = homedir33()) {
39174
39987
  const masked = maskAssignedSecrets(maskTruncatedSecret(maskSecrets(input)));
39175
39988
  const shortened = shortenPaths(masked, home);
39176
39989
  const collapsed = shortened.replace(/\s+/g, " ").trim();
@@ -39293,15 +40106,15 @@ var init_harm_report = __esm(() => {
39293
40106
  });
39294
40107
 
39295
40108
  // src/audit/machine-store.ts
39296
- import { existsSync as existsSync47, readFileSync as readFileSync39, rmSync as rmSync19 } from "node:fs";
40109
+ import { existsSync as existsSync48, readFileSync as readFileSync40, rmSync as rmSync20 } from "node:fs";
39297
40110
  import { hostname as hostname5 } from "node:os";
39298
40111
  import { randomUUID as randomUUID5 } from "node:crypto";
39299
40112
  function readMachineIdentity(home) {
39300
40113
  const path = auditMachineFile(home);
39301
- if (!existsSync47(path))
40114
+ if (!existsSync48(path))
39302
40115
  return null;
39303
40116
  try {
39304
- const parsed = JSON.parse(readFileSync39(path, "utf8"));
40117
+ const parsed = JSON.parse(readFileSync40(path, "utf8"));
39305
40118
  if (typeof parsed.machine_id !== "string" || !parsed.machine_id)
39306
40119
  return null;
39307
40120
  return {
@@ -39631,16 +40444,16 @@ var init_cli = __esm(() => {
39631
40444
  });
39632
40445
 
39633
40446
  // src/hooks/configure-wizard.ts
39634
- import { existsSync as existsSync48, mkdirSync as mkdirSync27, writeFileSync as writeFileSync23, readFileSync as readFileSync40 } from "node:fs";
39635
- import { homedir as homedir33 } from "node:os";
39636
- import { dirname as dirname25, resolve as resolve33, sep as sep5 } from "node:path";
40447
+ import { existsSync as existsSync49, mkdirSync as mkdirSync28, writeFileSync as writeFileSync23, readFileSync as readFileSync41 } from "node:fs";
40448
+ import { homedir as homedir34 } from "node:os";
40449
+ import { dirname as dirname25, resolve as resolve34, sep as sep5 } from "node:path";
39637
40450
  async function emit(event, props) {
39638
40451
  try {
39639
40452
  await trackHookEvent2(getInstanceId2(), event, props);
39640
40453
  } catch {}
39641
40454
  }
39642
40455
  function homeify(p) {
39643
- const home = homedir33();
40456
+ const home = homedir34();
39644
40457
  if (p === home)
39645
40458
  return "~";
39646
40459
  if (p.startsWith(home + sep5))
@@ -39763,7 +40576,7 @@ function removeUserScopeHooks(clis, cwd) {
39763
40576
  }
39764
40577
  }
39765
40578
  for (const settingsPath of settingsPathsFor(integration, "user", cwd)) {
39766
- if (id !== "hermes" && !existsSync48(settingsPath))
40579
+ if (id !== "hermes" && !existsSync49(settingsPath))
39767
40580
  continue;
39768
40581
  try {
39769
40582
  removed += integration.removeHooksFromFile(settingsPath);
@@ -39801,7 +40614,7 @@ function describeCustomPolicies(cwd) {
39801
40614
  const scopes = [];
39802
40615
  let fileCount = 0;
39803
40616
  const dirs = [
39804
- { dir: resolve33(cwd, ".failproofai", "policies"), label: "project" },
40617
+ { dir: resolve34(cwd, ".failproofai", "policies"), label: "project" },
39805
40618
  { dir: customPoliciesDir(), label: "global" }
39806
40619
  ];
39807
40620
  for (const { dir, label } of dirs) {
@@ -39812,7 +40625,7 @@ function describeCustomPolicies(cwd) {
39812
40625
  fileCount += found.length;
39813
40626
  }
39814
40627
  for (const name of findSkippedPolicyFiles(dir)) {
39815
- warnings.push(`! ${homeify(resolve33(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
40628
+ warnings.push(`! ${homeify(resolve34(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
39816
40629
  }
39817
40630
  }
39818
40631
  return { active, warnings, fileCount, scopes };
@@ -39822,7 +40635,7 @@ function firstRunMarkerPath() {
39822
40635
  }
39823
40636
  function markLauncherSeen() {
39824
40637
  try {
39825
- mkdirSync27(dirname25(firstRunMarkerPath()), { recursive: true });
40638
+ mkdirSync28(dirname25(firstRunMarkerPath()), { recursive: true });
39826
40639
  writeFileSync23(firstRunMarkerPath(), "1", "utf8");
39827
40640
  } catch {}
39828
40641
  }
@@ -40057,6 +40870,158 @@ ${process.platform} service target.
40057
40870
  stdin,
40058
40871
  stdout
40059
40872
  });
40873
+ const askKey = () => promptCloudKey({
40874
+ message: "Connect to cloud",
40875
+ meta: source ? `${host} · from ${source}` : host,
40876
+ tabHint: "go back",
40877
+ emptyError: "Paste a key, or press tab to go back.",
40878
+ check: async (key) => {
40879
+ if (!url)
40880
+ return { ok: false, line: badUrl ?? "No usable endpoint." };
40881
+ const r = await checkCloudKey(url, key);
40882
+ if (r.ok) {
40883
+ typedOrg = r.org;
40884
+ return { ok: true, line: r.note };
40885
+ }
40886
+ return { ok: false, line: r.refused ? `That key was refused: ${r.reason}.` : `Couldn't check the key: ${r.reason}.` };
40887
+ },
40888
+ collapsed: (answer) => answer === OPEN_SOURCE ? [] : [...cloudLines(planFor(answer)), ""],
40889
+ stdin,
40890
+ stdout
40891
+ });
40892
+ let loginUnsupported = false;
40893
+ const askLogin = (endpoint) => {
40894
+ let pollSecret = "";
40895
+ return promptBrowserLogin({
40896
+ message: "Connect to cloud",
40897
+ meta: source ? `${host} · from ${source}` : host,
40898
+ machineLabel,
40899
+ start: async () => {
40900
+ const r = await startLogin(endpoint, { label: machineLabel, os: machineOs(), cliVersion: version2 });
40901
+ if (r.kind === "ok") {
40902
+ pollSecret = r.pollSecret;
40903
+ emit("configure_cloud_login", { step: "started" });
40904
+ return { ok: true, url: r.verificationUrl, expiresIn: r.expiresIn, interval: r.interval };
40905
+ }
40906
+ if (r.kind === "unsupported") {
40907
+ emit("configure_cloud_login", { step: "unsupported" });
40908
+ loginUnsupported = true;
40909
+ return { ok: false, retryable: false, line: `${host} doesn't support logging in from here yet.` };
40910
+ }
40911
+ return {
40912
+ ok: false,
40913
+ retryable: true,
40914
+ line: r.kind === "refused" ? `Couldn't start the login: ${r.reason}.` : `Couldn't reach ${host}: ${r.reason}.`
40915
+ };
40916
+ },
40917
+ poll: async () => {
40918
+ const p = await pollLogin(endpoint, pollSecret);
40919
+ switch (p.kind) {
40920
+ case "approved":
40921
+ emit("configure_cloud_login", { step: "approved" });
40922
+ return {
40923
+ state: "approved",
40924
+ value: {
40925
+ kind: "login",
40926
+ pollSecret,
40927
+ org: p.org,
40928
+ approvedBy: p.approvedBy,
40929
+ keyName: p.keyName,
40930
+ permissions: p.permissions
40931
+ }
40932
+ };
40933
+ case "pending":
40934
+ return { state: "pending", interval: p.interval };
40935
+ case "slow_down":
40936
+ return { state: "slow_down" };
40937
+ case "denied":
40938
+ emit("configure_cloud_login", { step: "denied" });
40939
+ return { state: "denied" };
40940
+ case "unreachable":
40941
+ return { state: "unreachable", line: p.reason };
40942
+ default:
40943
+ return { state: "expired" };
40944
+ }
40945
+ },
40946
+ open: (link) => {
40947
+ if (!canOpenBrowser())
40948
+ return false;
40949
+ openUrl(link);
40950
+ return true;
40951
+ },
40952
+ collapsed: (plan) => [...cloudLines(plan), ""],
40953
+ stdin,
40954
+ stdout
40955
+ });
40956
+ };
40957
+ let savedRefused = false;
40958
+ const chooseConnection = async () => {
40959
+ const verdict = saved && !saved.splitKeys ? await transient("Checking your saved key…", () => checkCloudKey(saved.url, saved.token)) : null;
40960
+ const savedBroken = verdict !== null && !verdict.ok && verdict.refused;
40961
+ savedRefused = savedBroken;
40962
+ const keptPlan = verdict && verdict.ok ? { kind: "kept", how: "saved", verified: true } : verdict && !verdict.refused ? { kind: "kept", how: "saved", verified: false } : { kind: "kept", how: "skip", verified: false };
40963
+ const keepHint = !saved ? "" : saved.splitKeys ? "leave both keys as they are" : verdict?.ok ? `${verdict.org ?? saved.org ?? hostOf(saved.url)} · key ${maskToken(saved.token)}` : savedBroken ? "its key no longer works" : "its key couldn't be checked just now";
40964
+ const login = {
40965
+ label: saved ? "Log in again" : "Log in to FailproofAI Cloud",
40966
+ value: "login",
40967
+ hint: savedBroken ? "recommended · your saved key no longer works" : saved ? "opens your browser and makes a new key" : "recommended · opens your browser"
40968
+ };
40969
+ const key = { label: "Paste an API key", value: "key", hint: "one you already have" };
40970
+ const oss = { label: "Use open source", value: "oss", hint: "skip the cloud" };
40971
+ const keep = { label: "Keep current connection", value: "keep", hint: keepHint };
40972
+ const choices = !saved ? [login, key, oss] : savedBroken ? [login, key, keep] : [keep, login, key];
40973
+ const leftLogin = (result) => {
40974
+ if (result === null || result === BACK) {
40975
+ if (result === BACK && loginUnsupported) {
40976
+ write([kit.caution(`${host} doesn't support logging in from here yet. Paste an API key instead.`), ""]);
40977
+ }
40978
+ return result;
40979
+ }
40980
+ return result.value;
40981
+ };
40982
+ if (url && (!saved || savedBroken)) {
40983
+ if (savedBroken && verdict && !verdict.ok) {
40984
+ write([kit.caution(`Your saved key no longer works: ${verdict.reason}. Logging in again.`)]);
40985
+ }
40986
+ emit("configure_cloud_choice", { choice: "login", connected: Boolean(saved), auto: true });
40987
+ const first = leftLogin(await askLogin(url));
40988
+ if (first !== BACK)
40989
+ return first;
40990
+ }
40991
+ for (;; ) {
40992
+ const pick = await selectOne({
40993
+ message: "Connect to cloud",
40994
+ meta: source ? `${host} · from ${source}` : host,
40995
+ choices: loginUnsupported ? choices.filter((c) => c.value !== "login") : choices,
40996
+ collapsed: (p) => p === "keep" ? [...cloudLines(keptPlan), ""] : p === "oss" ? [...cloudLines({ kind: "oss" }), ""] : [],
40997
+ stdin,
40998
+ stdout
40999
+ });
41000
+ if (pick === null)
41001
+ return null;
41002
+ emit("configure_cloud_choice", { choice: pick, connected: Boolean(saved) });
41003
+ if (pick === "keep")
41004
+ return keptPlan;
41005
+ if (pick === "oss")
41006
+ return { kind: "oss" };
41007
+ if (pick === "key") {
41008
+ const answer = await askKey();
41009
+ if (answer === null)
41010
+ return null;
41011
+ if (answer === OPEN_SOURCE)
41012
+ continue;
41013
+ return planFor(answer);
41014
+ }
41015
+ if (!url) {
41016
+ write([kit.fail(badUrl ?? "No usable endpoint."), ""]);
41017
+ continue;
41018
+ }
41019
+ const result = leftLogin(await askLogin(url));
41020
+ if (result === BACK)
41021
+ continue;
41022
+ return result;
41023
+ }
41024
+ };
40060
41025
  let cloud;
40061
41026
  if (answers.oss) {
40062
41027
  cloud = saved ? { kind: "kept", how: "skip", verified: false } : { kind: "oss" };
@@ -40088,16 +41053,17 @@ ${process.platform} service target.
40088
41053
  } else {
40089
41054
  if (badUrl)
40090
41055
  write([kit.fail(badUrl)]);
40091
- const answer = await askCloud();
40092
- if (answer === null)
41056
+ const chosen = await chooseConnection();
41057
+ if (chosen === null)
40093
41058
  return cancel();
40094
- cloud = planFor(answer);
41059
+ cloud = chosen;
40095
41060
  }
40096
41061
  const fp = readConfig();
40097
41062
  const hookedNow = INTEGRATION_TYPES.filter((id) => hookedAtUserScope(id, cwd));
40098
41063
  const plan = planAgents({ detected: detectInstalledClis(), saved: fp.agents, hooked: hookedNow });
40099
- const collectingAfter = cloud.kind === "connect" || readIngestCredential() !== null;
40100
- const sessionsOn = cloud.kind === "connect" ? answers.noTranscripts !== true : fp.collector.sessions === true;
41064
+ const connecting = cloud.kind === "connect" || cloud.kind === "login";
41065
+ const collectingAfter = connecting || readIngestCredential() !== null;
41066
+ const sessionsOn = connecting ? answers.noTranscripts !== true : fp.collector.sessions === true;
40101
41067
  let selected;
40102
41068
  if (answers.agents && answers.agents.length > 0) {
40103
41069
  const named = new Set(answers.agents);
@@ -40129,6 +41095,27 @@ ${process.platform} service target.
40129
41095
  const { added, removed } = agentChanges(plan, selected);
40130
41096
  const done = [];
40131
41097
  let daemonInstalled = daemonAlreadyRunning;
41098
+ if ((daemonWanted || daemonUnitStale) && !unattended) {
41099
+ let stillRoot = false;
41100
+ try {
41101
+ stillRoot = canElevate();
41102
+ } catch {
41103
+ stillRoot = false;
41104
+ }
41105
+ if (!stillRoot && daemonWanted) {
41106
+ if (!elevate("Your sudo session expired while you were away. Installing failproofaid needs root again.")) {
41107
+ write([
41108
+ kit.fail("Couldn't get root, so setup stopped before changing anything."),
41109
+ ` Re-run once you can use sudo: ${kit.cmd("failproofai config")}`
41110
+ ]);
41111
+ emit("configure_aborted", { reason: "needs_root" });
41112
+ return { applied: false, abort: "needs_root" };
41113
+ }
41114
+ } else if (!stillRoot && !elevate("Your sudo session expired while you were away. Refreshing failproofaid's service definition needs root again.")) {
41115
+ done.push(kit.caution("No root, so its service definition stays as it is. Scheduled audits stay off until it is refreshed."));
41116
+ daemonUnitStale = false;
41117
+ }
41118
+ }
40132
41119
  if (daemonWanted) {
40133
41120
  if (daemonBroken) {
40134
41121
  await transient("Removing the failproofaid service that can't start…", async () => {
@@ -40195,39 +41182,47 @@ ${process.platform} service target.
40195
41182
  setDaemonConfigured(true, version2);
40196
41183
  pruneOldDaemonBinaries();
40197
41184
  }
40198
- const connectedAfterPlan = cloud.kind === "connect" || cloud.kind === "kept";
40199
- const applied = emit("configure_applied", {
40200
- target: "user",
40201
- scopes: ["user"],
40202
- cli: selected,
40203
- cli_count: selected.length,
40204
- policy_count: (readScopedHooksConfig("user", cwd).enabledPolicies ?? []).length,
40205
- connected: cloud.kind === "connect",
40206
- mode: connectedAfterPlan ? "cloud" : "oss",
40207
- agents_selected: selected.length,
40208
- agents_detected: plan.detected.size,
40209
- agents_changed: added.length + removed.length > 0
40210
- });
40211
41185
  const policies = readScopedHooksConfig("user", cwd).enabledPolicies ?? [];
40212
41186
  if (selected.length > 0)
40213
41187
  await transient("Installing hooks…", () => installHooks(policies, "user", cwd, false, "configure-wizard", undefined, false, selected, { replace: true, quiet: true }));
40214
41188
  removeUserScopeHooks(INTEGRATION_TYPES.filter((id) => !selected.includes(id) && (plan.baseline.has(id) || hookedNow.includes(id))), cwd);
41189
+ installAgentSkill(INTEGRATION_TYPES.filter((id) => plan.detected.has(id) || selected.includes(id)));
40215
41190
  requestFreshStart(plan.mode === "rerun" ? added : [], collectingAfter && sessionsOn);
40216
41191
  updateConfig({ agents: { selected, seen: plan.seen } });
40217
41192
  let connected = false;
40218
- if (cloud.kind === "connect" && url) {
40219
- const token = cloud.token;
41193
+ let token = cloud.kind === "connect" ? cloud.token : null;
41194
+ let orgLabel = cloud.kind === "connect" ? cloud.org : undefined;
41195
+ let mintedName;
41196
+ let claimedOrg;
41197
+ if (cloud.kind === "login" && url) {
41198
+ const claimed = await transient("Creating this machine's key…", () => claimLogin(url, cloud.pollSecret));
41199
+ emit("configure_cloud_login", { step: "claimed", ok: claimed.kind === "ok", reason: claimed.kind });
41200
+ if (claimed.kind === "ok") {
41201
+ token = claimed.key;
41202
+ orgLabel = loginOrgLabel(claimed.org);
41203
+ mintedName = claimed.keyName;
41204
+ claimedOrg = claimed.org;
41205
+ } else {
41206
+ hookLogWarn(`cloud login could not be completed: ${claimed.kind}`);
41207
+ done.push(kit.caution(claimed.kind === "expired" ? "The login took too long to finish, so no key was created. Everything else was applied." : claimed.kind === "revoked" ? "Whoever approved the login can no longer create keys in that org, so no key was created." : claimed.kind === "unreachable" ? `Couldn't reach ${host} to create the key (${claimed.reason}); everything else was applied.` : `The key couldn't be created: ${claimed.reason}. Everything else was applied.`, "failproofai config"));
41208
+ }
41209
+ }
41210
+ if (token && url) {
40220
41211
  try {
40221
41212
  const outcome = await transient("Connecting…", () => connectToCloud({
40222
41213
  url,
40223
41214
  token,
40224
41215
  machineId,
40225
41216
  machineLabel,
40226
- sessions: answers.noTranscripts !== true
41217
+ sessions: answers.noTranscripts !== true,
41218
+ org: claimedOrg
40227
41219
  }));
40228
41220
  connected = outcome.anyConfigured;
40229
41221
  done.push(...outcome.changes ?? []);
40230
- done.push(...connectDoneLines(kit, outcome, host, cloud.org));
41222
+ done.push(...connectDoneLines(kit, outcome, host, orgLabel));
41223
+ if (cloud.kind === "login" && saved && connected) {
41224
+ done.push(` Your previous key for this machine still works until you revoke it on ${host}'s keys page.`);
41225
+ }
40231
41226
  if (outcome.ingest.ok && answers.noTranscripts === true) {
40232
41227
  done.push(" Decisions only: session transcripts are not sent (--no-transcripts).");
40233
41228
  }
@@ -40236,10 +41231,29 @@ ${process.platform} service target.
40236
41231
  hookLogWarn(`cloud connection was not written: ${err instanceof Error ? err.message : String(err)}`);
40237
41232
  done.push(kit.caution("Couldn't connect; everything else was applied.", "failproofai config"));
40238
41233
  }
41234
+ if (cloud.kind === "login" && !connected) {
41235
+ done.push(` The key ${mintedName ? `"${mintedName}" ` : ""}was created but isn't in use here; revoke it on ${host}'s keys page.`);
41236
+ }
40239
41237
  } else if (cloud.kind === "kept" && cloud.how === "saved" && fp.mode !== "cloud") {
40240
41238
  updateConfig({ mode: "cloud" });
40241
41239
  }
40242
- await applied;
41240
+ const keptOld = cloud.kind === "login" && !connected && Boolean(saved);
41241
+ if (keptOld) {
41242
+ done.push(savedRefused ? " This machine keeps its previous connection, but its key no longer works. Run failproofai config again to log in." : " This machine keeps its previous connection.");
41243
+ }
41244
+ const cloudAfter = connected || cloud.kind === "kept" || keptOld;
41245
+ await emit("configure_applied", {
41246
+ target: "user",
41247
+ scopes: ["user"],
41248
+ cli: selected,
41249
+ cli_count: selected.length,
41250
+ policy_count: policies.length,
41251
+ connected,
41252
+ mode: cloudAfter ? "cloud" : "oss",
41253
+ agents_selected: selected.length,
41254
+ agents_detected: plan.detected.size,
41255
+ agents_changed: added.length + removed.length > 0
41256
+ });
40243
41257
  markLauncherSeen();
40244
41258
  clearOnboardingAttempt();
40245
41259
  const codexTrust = selected.includes("codex") ? codexHookTrust() : undefined;
@@ -40248,7 +41262,7 @@ ${process.platform} service target.
40248
41262
  done,
40249
41263
  cwd,
40250
41264
  agents: selected.length,
40251
- connected: connected || cloud.kind === "kept",
41265
+ connected: cloudAfter,
40252
41266
  codexTrust
40253
41267
  }));
40254
41268
  return {
@@ -40259,10 +41273,15 @@ ${process.platform} service target.
40259
41273
  policies,
40260
41274
  daemonInstalled,
40261
41275
  connected,
40262
- mode: connected || cloud.kind === "kept" ? "cloud" : "oss",
41276
+ mode: cloudAfter ? "cloud" : "oss",
40263
41277
  agents: { selected, added, removed }
40264
41278
  };
40265
41279
  }
41280
+ function loginOrgLabel(org) {
41281
+ if (org.name && org.slug)
41282
+ return `${org.name} (${org.slug})`;
41283
+ return org.name ?? org.slug ?? "your org";
41284
+ }
40266
41285
  function savedConnection() {
40267
41286
  const cloud = readCloudCredentials();
40268
41287
  const ingest = readIngestCredential();
@@ -40289,8 +41308,12 @@ function resolveCloudUrl(flag, existing) {
40289
41308
  const env = process.env.FAILPROOFAI_CLOUD_URL?.trim();
40290
41309
  const fromFlag = flag?.trim();
40291
41310
  const override = fromFlag || env;
40292
- if (!override)
40293
- return { ok: true, url: existing ?? cloudBaseFor2(DEFAULT_INGEST_URL) };
41311
+ if (!override) {
41312
+ if (!existing)
41313
+ return { ok: true, url: cloudBaseFor2(DEFAULT_INGEST_URL) };
41314
+ const saved = validateCloudUrl(existing);
41315
+ return saved.ok ? { ok: true, url: existing } : { ok: false, message: `the saved cloud address "${existing}" cannot be used: ${saved.reason}` };
41316
+ }
40294
41317
  const source = fromFlag && fromFlag !== env ? "--url" : "FAILPROOFAI_CLOUD_URL";
40295
41318
  const validated = validateCloudUrl(cloudBaseFor2(override));
40296
41319
  if (!validated.ok) {
@@ -40309,6 +41332,9 @@ function hostOf(url) {
40309
41332
  }
40310
41333
  }
40311
41334
  async function checkCloudKey(url, key) {
41335
+ const target = validateCloudUrl(url);
41336
+ if (!target.ok)
41337
+ return { ok: false, refused: false, reason: `the address ${url} can't be used: ${target.reason}` };
40312
41338
  const identity = await introspectKey(url, key);
40313
41339
  if (identity.kind === "rejected")
40314
41340
  return { ok: false, refused: true, reason: "the server did not accept it" };
@@ -40345,6 +41371,11 @@ function cloudCollapsedLines(kit, plan, at) {
40345
41371
  kit.ok(plan.org ? `Key accepted for ${plan.org} on ${at.host}` : `Key accepted by ${at.host}`),
40346
41372
  ...plan.note ? [kit.caution(plan.note)] : []
40347
41373
  ];
41374
+ case "login":
41375
+ return [
41376
+ head,
41377
+ kit.ok(`Logged in${plan.approvedBy ? ` as ${plan.approvedBy}` : ""} · ${loginOrgLabel(plan.org)} on ${at.host}`)
41378
+ ];
40348
41379
  }
40349
41380
  }
40350
41381
  function connectDoneLines(kit, outcome, host, org) {
@@ -40401,10 +41432,13 @@ function doneLines(kit, state) {
40401
41432
  var init_configure_wizard = __esm(() => {
40402
41433
  init_pack_store();
40403
41434
  init_tui();
41435
+ init_cloud_login();
41436
+ init_open_browser();
40404
41437
  init_collector_config();
40405
41438
  init_integrations();
40406
41439
  init_types();
40407
41440
  init_manager();
41441
+ init_agent_skill();
40408
41442
  init_codex_system_hooks();
40409
41443
  init_pack_manifest();
40410
41444
  init_hooks_config();
@@ -40415,6 +41449,7 @@ var init_configure_wizard = __esm(() => {
40415
41449
  init_hook_logger();
40416
41450
  init_cloud_enrollment();
40417
41451
  init_cloud_connection();
41452
+ init_cloud_introspect();
40418
41453
  init_fp_config();
40419
41454
  init_backfill_request();
40420
41455
  init_agent_selection();
@@ -40427,18 +41462,18 @@ var init_configure_wizard = __esm(() => {
40427
41462
  });
40428
41463
 
40429
41464
  // src/hooks/flush-cli.ts
40430
- import { existsSync as existsSync49, mkdirSync as mkdirSync28, writeFileSync as writeFileSync24, readdirSync as readdirSync22, statSync as statSync36 } from "node:fs";
40431
- import { dirname as dirname26, join as join41 } from "node:path";
41465
+ import { existsSync as existsSync50, mkdirSync as mkdirSync29, writeFileSync as writeFileSync24, readdirSync as readdirSync22, statSync as statSync36 } from "node:fs";
41466
+ import { dirname as dirname26, join as join42 } from "node:path";
40432
41467
  function flushRequestPath2(home) {
40433
- return join41(failproofaiHome(home), "state", "flush-request.json");
41468
+ return join42(failproofaiHome(home), "state", "flush-request.json");
40434
41469
  }
40435
41470
  function spoolDirs2(home) {
40436
41471
  const root = spoolDir(home);
40437
41472
  const dirs = [root, customAgentsEventsDir(home)];
40438
- if (!existsSync49(root))
41473
+ if (!existsSync50(root))
40439
41474
  return dirs;
40440
41475
  try {
40441
- return dirs.concat(readdirSync22(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join41(root, e.name)));
41476
+ return dirs.concat(readdirSync22(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join42(root, e.name)));
40442
41477
  } catch {
40443
41478
  return dirs;
40444
41479
  }
@@ -40469,7 +41504,7 @@ function spoolBacklog2(home, now = Date.now()) {
40469
41504
  count += names.length;
40470
41505
  for (const name of names) {
40471
41506
  try {
40472
- const mtime = statSync36(join41(dir, name)).mtimeMs;
41507
+ const mtime = statSync36(join42(dir, name)).mtimeMs;
40473
41508
  if (oldest === undefined || mtime < oldest)
40474
41509
  oldest = mtime;
40475
41510
  } catch {}
@@ -40531,7 +41566,7 @@ async function runFlushCommand2(opts = {}) {
40531
41566
  return { exitCode: 0, pending: 0, lines: nothing };
40532
41567
  }
40533
41568
  const path = flushRequestPath2(home);
40534
- mkdirSync28(dirname26(path), { recursive: true });
41569
+ mkdirSync29(dirname26(path), { recursive: true });
40535
41570
  writeFileSync24(path, `${JSON.stringify({ requestedAtMs: Date.now() }, null, 2)}
40536
41571
  `, {
40537
41572
  mode: 384
@@ -40565,7 +41600,7 @@ var init_flush_cli2 = __esm(() => {
40565
41600
  });
40566
41601
 
40567
41602
  // src/hooks/backfill-cli.ts
40568
- import { existsSync as existsSync50 } from "node:fs";
41603
+ import { existsSync as existsSync51 } from "node:fs";
40569
41604
  async function adapterSessionCount(cli, sinceMs) {
40570
41605
  try {
40571
41606
  return (await getAdapter(cli).listTranscripts({ sinceMs })).length;
@@ -40583,7 +41618,7 @@ async function runBackfillCommand(opts = {}) {
40583
41618
  const sinceLabel = new Date(sinceMs).toISOString().slice(0, 10);
40584
41619
  const home = failproofaiHome();
40585
41620
  const fail = (text, ...fix) => ({ exitCode: 1, lines: [kit.fail(text), ...fix] });
40586
- if (!existsSync50(home)) {
41621
+ if (!existsSync51(home)) {
40587
41622
  return fail(`There is no failproofai home at ${home}.`, ` Set this machine up first: ${kit.cmd("failproofai config")}`);
40588
41623
  }
40589
41624
  if (!readIngestCredential()) {
@@ -43058,20 +44093,20 @@ var init_main = __esm(() => {
43058
44093
  });
43059
44094
 
43060
44095
  // src/hooks/otel-writers.ts
43061
- import { homedir as homedir34 } from "node:os";
43062
- import { resolve as resolve34 } from "node:path";
44096
+ import { homedir as homedir35 } from "node:os";
44097
+ import { resolve as resolve35 } from "node:path";
43063
44098
  import { accessSync as accessSync2, constants, readdirSync as readdirSync23, statSync as statSync37 } from "node:fs";
43064
- function vscodeSettingsPath(home = homedir34(), platform = process.platform) {
44099
+ function vscodeSettingsPath(home = homedir35(), platform = process.platform) {
43065
44100
  if (platform === "darwin")
43066
- return resolve34(home, "Library/Application Support/Code/User/settings.json");
44101
+ return resolve35(home, "Library/Application Support/Code/User/settings.json");
43067
44102
  if (platform === "win32")
43068
- return resolve34(process.env.APPDATA ?? resolve34(home, "AppData/Roaming"), "Code/User/settings.json");
43069
- return resolve34(process.env.XDG_CONFIG_HOME ?? resolve34(home, ".config"), "Code/User/settings.json");
44103
+ return resolve35(process.env.APPDATA ?? resolve35(home, "AppData/Roaming"), "Code/User/settings.json");
44104
+ return resolve35(process.env.XDG_CONFIG_HOME ?? resolve35(home, ".config"), "Code/User/settings.json");
43070
44105
  }
43071
- function agentConfigPath(agent, home = homedir34()) {
43072
- return agent === "copilot" ? vscodeSettingsPath(home) : resolve34(home, `.${agent}`, agent === "codex" ? "config.toml" : "settings.json");
44106
+ function agentConfigPath(agent, home = homedir35()) {
44107
+ return agent === "copilot" ? vscodeSettingsPath(home) : resolve35(home, `.${agent}`, agent === "codex" ? "config.toml" : "settings.json");
43073
44108
  }
43074
- function vscodeInstalled(home = homedir34(), platform = process.platform) {
44109
+ function vscodeInstalled(home = homedir35(), platform = process.platform) {
43075
44110
  const executable = (path) => {
43076
44111
  try {
43077
44112
  accessSync2(path, platform === "win32" ? constants.F_OK : constants.X_OK);
@@ -43081,13 +44116,13 @@ function vscodeInstalled(home = homedir34(), platform = process.platform) {
43081
44116
  }
43082
44117
  };
43083
44118
  const names = platform === "win32" ? ["code.cmd", "code-insiders.cmd", "Code.exe", "Code - Insiders.exe"] : ["code", "code-insiders"];
43084
- if ((process.env.PATH ?? "").split(platform === "win32" ? ";" : ":").some((dir) => names.some((name) => executable(resolve34(dir, name)))))
44119
+ if ((process.env.PATH ?? "").split(platform === "win32" ? ";" : ":").some((dir) => names.some((name) => executable(resolve35(dir, name)))))
43085
44120
  return true;
43086
44121
  if (vscodeProbe.appPaths(home, platform).some(executable))
43087
44122
  return true;
43088
44123
  return [".vscode", ".vscode-insiders"].some((folder) => {
43089
44124
  try {
43090
- return readdirSync23(resolve34(home, folder, "extensions"), { withFileTypes: true }).some((entry) => entry.isDirectory() && /^github\.copilot(?:-chat)?-\d/i.test(entry.name));
44125
+ return readdirSync23(resolve35(home, folder, "extensions"), { withFileTypes: true }).some((entry) => entry.isDirectory() && /^github\.copilot(?:-chat)?-\d/i.test(entry.name));
43091
44126
  } catch {
43092
44127
  return false;
43093
44128
  }
@@ -43295,10 +44330,10 @@ var init_otel_writers = __esm(() => {
43295
44330
  return platform === "darwin" ? [
43296
44331
  "/Applications/Visual Studio Code.app/Contents/MacOS/Electron",
43297
44332
  "/Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Electron",
43298
- resolve34(home, "Applications/Visual Studio Code.app/Contents/MacOS/Electron")
44333
+ resolve35(home, "Applications/Visual Studio Code.app/Contents/MacOS/Electron")
43299
44334
  ] : platform === "win32" ? [
43300
- resolve34(process.env.LOCALAPPDATA ?? resolve34(home, "AppData/Local"), "Programs/Microsoft VS Code/Code.exe"),
43301
- resolve34(process.env.ProgramFiles ?? "C:/Program Files", "Microsoft VS Code/Code.exe")
44335
+ resolve35(process.env.LOCALAPPDATA ?? resolve35(home, "AppData/Local"), "Programs/Microsoft VS Code/Code.exe"),
44336
+ resolve35(process.env.ProgramFiles ?? "C:/Program Files", "Microsoft VS Code/Code.exe")
43302
44337
  ] : ["/usr/share/code/code", "/opt/visual-studio-code/code"];
43303
44338
  }
43304
44339
  };
@@ -43307,16 +44342,16 @@ var init_otel_writers = __esm(() => {
43307
44342
  // src/hooks/otel-cli.ts
43308
44343
  import {
43309
44344
  chmodSync as chmodSync11,
43310
- existsSync as existsSync51,
43311
- mkdirSync as mkdirSync29,
43312
- readFileSync as readFileSync41,
44345
+ existsSync as existsSync52,
44346
+ mkdirSync as mkdirSync30,
44347
+ readFileSync as readFileSync42,
43313
44348
  statSync as statSync38,
43314
44349
  unlinkSync as unlinkSync8,
43315
44350
  writeFileSync as writeFileSync25
43316
44351
  } from "node:fs";
43317
44352
  import { dirname as dirname27 } from "node:path";
43318
44353
  import { createHash as createHash13, randomUUID as randomUUID6 } from "node:crypto";
43319
- import { homedir as homedir35 } from "node:os";
44354
+ import { homedir as homedir36 } from "node:os";
43320
44355
  import { connect } from "node:net";
43321
44356
  function rawConfig() {
43322
44357
  return readJsonFile(configFile());
@@ -43346,7 +44381,7 @@ async function waitForCollectorConfig(generation, timeoutMs) {
43346
44381
  const deadline = Date.now() + timeoutMs;
43347
44382
  do {
43348
44383
  try {
43349
- const health = JSON.parse(readFileSync41(collectorHealthFile(), "utf8"));
44384
+ const health = JSON.parse(readFileSync42(collectorHealthFile(), "utf8"));
43350
44385
  if (health.config_generation === generation && typeof health.ts === "number" && Date.now() - health.ts * 1000 < 35000)
43351
44386
  return true;
43352
44387
  } catch {}
@@ -43355,7 +44390,7 @@ async function waitForCollectorConfig(generation, timeoutMs) {
43355
44390
  return false;
43356
44391
  }
43357
44392
  function privateState(agent) {
43358
- return JSON.parse(readFileSync41(otelAgentStateFile(agent), "utf8"));
44393
+ return JSON.parse(readFileSync42(otelAgentStateFile(agent), "utf8"));
43359
44394
  }
43360
44395
  function credential() {
43361
44396
  const ingest = readCredentials().ingest;
@@ -43372,15 +44407,15 @@ function credential() {
43372
44407
  return { key, origin: url.origin };
43373
44408
  }
43374
44409
  function backupOnce(path, original) {
43375
- if (existsSync51(`${path}.failproofai-bak`))
44410
+ if (existsSync52(`${path}.failproofai-bak`))
43376
44411
  return;
43377
- mkdirSync29(dirname27(path), { recursive: true });
44412
+ mkdirSync30(dirname27(path), { recursive: true });
43378
44413
  writeFileSync25(`${path}.failproofai-bak`, original ?? "", { flag: "wx", mode: 384 });
43379
44414
  chmodSync11(`${path}.failproofai-bak`, 384);
43380
44415
  }
43381
44416
  function savePrivate(agent, state) {
43382
44417
  const path = otelAgentStateFile(agent);
43383
- mkdirSync29(dirname27(path), { recursive: true, mode: 448 });
44418
+ mkdirSync30(dirname27(path), { recursive: true, mode: 448 });
43384
44419
  writeConfigFileAtomic(path, JSON.stringify(state), { backup: false, mode: 384 });
43385
44420
  chmodSync11(path, 384);
43386
44421
  }
@@ -43419,8 +44454,8 @@ function restoreAgent(agent, raw) {
43419
44454
  if (!record)
43420
44455
  return;
43421
44456
  const state = privateState(agent);
43422
- if (existsSync51(record.file)) {
43423
- const current = readFileSync41(record.file, "utf8");
44457
+ if (existsSync52(record.file)) {
44458
+ const current = readFileSync42(record.file, "utf8");
43424
44459
  if (digest(current) === state.writtenHash) {
43425
44460
  if (state.original === null)
43426
44461
  unlinkSync8(record.file);
@@ -43459,8 +44494,8 @@ async function enableAgent(agent, yes, localFlag, content, options) {
43459
44494
  const { key, origin } = credential();
43460
44495
  const raw = rawConfig();
43461
44496
  const previousConfig = structuredClone(raw);
43462
- const file = agentConfigPath(agent, options.home ?? homedir35());
43463
- if (agent === "copilot" && !vscodeInstalled(options.home ?? homedir35())) {
44497
+ const file = agentConfigPath(agent, options.home ?? homedir36());
44498
+ if (agent === "copilot" && !vscodeInstalled(options.home ?? homedir36())) {
43464
44499
  return { lines: ["VS Code not found — skipping copilot"], exitCode: 0 };
43465
44500
  }
43466
44501
  const existing = records(raw)[agent];
@@ -43475,7 +44510,7 @@ async function enableAgent(agent, yes, localFlag, content, options) {
43475
44510
  if (!Number.isInteger(port) || port < 1 || port > 65535)
43476
44511
  throw new Error("collector.otlp.port must be between 1 and 65535.");
43477
44512
  const destination = otlpDestination(origin, local, port);
43478
- const original = existsSync51(file) ? readFileSync41(file, "utf8") : null;
44513
+ const original = existsSync52(file) ? readFileSync42(file, "utf8") : null;
43479
44514
  const parsed = parseAgentConfig(original ?? "", agent);
43480
44515
  const oldState = existing ? privateState(agent) : null;
43481
44516
  const privacyConfig = oldState && original !== null && digest(original) === oldState.writtenHash ? parseAgentConfig(oldState.original ?? "", agent) : parsed;
@@ -43537,7 +44572,7 @@ async function enableAgent(agent, yes, localFlag, content, options) {
43537
44572
  } catch (err) {
43538
44573
  if (original !== null)
43539
44574
  writeConfigFileAtomic(file, original, { backup: false, mode: state.originalMode });
43540
- else if (existsSync51(file))
44575
+ else if (existsSync52(file))
43541
44576
  unlinkSync8(file);
43542
44577
  saveConfig(previousConfig);
43543
44578
  throw err;
@@ -43580,9 +44615,9 @@ async function status(options) {
43580
44615
  let parsed = {};
43581
44616
  let unreadable = false;
43582
44617
  try {
43583
- const file = record?.file ?? agentConfigPath(agent, options.home ?? homedir35());
43584
- if (existsSync51(file))
43585
- parsed = parseAgentConfig(readFileSync41(file, "utf8"), agent);
44618
+ const file = record?.file ?? agentConfigPath(agent, options.home ?? homedir36());
44619
+ if (existsSync52(file))
44620
+ parsed = parseAgentConfig(readFileSync42(file, "utf8"), agent);
43586
44621
  } catch {
43587
44622
  unreadable = true;
43588
44623
  }
@@ -43604,7 +44639,7 @@ async function status(options) {
43604
44639
  }
43605
44640
  let health = {};
43606
44641
  try {
43607
- health = JSON.parse(readFileSync41(otlpHealthFile(), "utf8"));
44642
+ health = JSON.parse(readFileSync42(otlpHealthFile(), "utf8"));
43608
44643
  } catch {}
43609
44644
  const requested = readConfig().collector.otlp?.enabled === true;
43610
44645
  const live = requested && health.relay === "listening" && await relayListening(readConfig().collector.otlp?.port ?? 4318);
@@ -43691,6 +44726,7 @@ var init_otel_cli = __esm(() => {
43691
44726
  init_fp_home();
43692
44727
  init_safe_config_write();
43693
44728
  init_collector_config();
44729
+ init_cloud_introspect();
43694
44730
  init_cloud_enrollment();
43695
44731
  init_integrations();
43696
44732
  init_tui();
@@ -43704,6 +44740,7 @@ var init_otel_cli2 = __esm(() => {
43704
44740
  init_fp_home();
43705
44741
  init_safe_config_write();
43706
44742
  init_collector_config();
44743
+ init_cloud_introspect();
43707
44744
  init_cloud_enrollment();
43708
44745
  init_integrations();
43709
44746
  init_tui();
@@ -43741,8 +44778,8 @@ var init_otel_output = __esm(() => {
43741
44778
  });
43742
44779
 
43743
44780
  // src/hooks/harness-cli.ts
43744
- import { homedir as homedir36 } from "node:os";
43745
- import { resolve as resolve35 } from "node:path";
44781
+ import { homedir as homedir37 } from "node:os";
44782
+ import { resolve as resolve36 } from "node:path";
43746
44783
  function unknownHarness(name) {
43747
44784
  const kit = kitFor();
43748
44785
  return fail([kit.fail(`Unknown harness: ${name}.`), ` Known: ${HARNESS_KEYS.join(", ")}`]);
@@ -43789,8 +44826,8 @@ function comparableLabel(raw) {
43789
44826
  return raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
43790
44827
  }
43791
44828
  function comparablePath(raw) {
43792
- const home = homedir36();
43793
- const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve35(home, raw.slice(2)) : raw;
44829
+ const home = homedir37();
44830
+ const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve36(home, raw.slice(2)) : raw;
43794
44831
  return expanded.replace(/\/+$/, "");
43795
44832
  }
43796
44833
  function effectiveLabel(entry) {
@@ -43948,7 +44985,7 @@ var init_harness_cli = __esm(() => {
43948
44985
  });
43949
44986
 
43950
44987
  // src/hooks/policy-reviewability.ts
43951
- import { resolve as resolve36 } from "node:path";
44988
+ import { resolve as resolve37 } from "node:path";
43952
44989
  function countReviewable(policies, knownReviewers) {
43953
44990
  let enabled = 0;
43954
44991
  let reviewable = 0;
@@ -43986,7 +45023,7 @@ function surveyReviewableCoverage(cwd) {
43986
45023
  }
43987
45024
  try {
43988
45025
  const mode = readCloudJevMode();
43989
- const assignments = readActiveCloudManagedPolicies();
45026
+ const assignments = listActiveCloudManagedPolicies();
43990
45027
  const cloudReviewers = cloudReviewerNames(assignments, mode);
43991
45028
  if (cloudReviewers.length > 0)
43992
45029
  reviewers = new Set([...reviewers ?? [], ...cloudReviewers]);
@@ -43996,9 +45033,9 @@ function surveyReviewableCoverage(cwd) {
43996
45033
  let customFiles = 0;
43997
45034
  try {
43998
45035
  const projectRoot = findProjectConfigDir(cwd ?? process.cwd());
43999
- const files = new Set(configuredCustomPolicyPaths(config).map((p) => resolve36(projectRoot, p)));
45036
+ const files = new Set(configuredCustomPolicyPaths(config).map((p) => resolve37(projectRoot, p)));
44000
45037
  if (config.customPoliciesEnabled !== false) {
44001
- const projectDir = resolve36(projectRoot, ".failproofai", "policies");
45038
+ const projectDir = resolve37(projectRoot, ".failproofai", "policies");
44002
45039
  for (const dir of [projectDir, customPoliciesDir()])
44003
45040
  for (const f of discoverPolicyFiles(dir))
44004
45041
  files.add(f);
@@ -44161,7 +45198,7 @@ var init_jev_stats = __esm(() => {
44161
45198
  });
44162
45199
 
44163
45200
  // src/hooks/jev-cli.ts
44164
- import { chmodSync as chmodSync12, existsSync as existsSync52, statSync as statSync39, unlinkSync as unlinkSync9 } from "node:fs";
45201
+ import { chmodSync as chmodSync12, existsSync as existsSync53, statSync as statSync39, unlinkSync as unlinkSync9 } from "node:fs";
44165
45202
  import { dirname as dirname28 } from "node:path";
44166
45203
  function jevHeader(name, meta, opts) {
44167
45204
  return [screenKit(opts).header(meta ? `${name} · ${meta}` : name)];
@@ -44264,7 +45301,7 @@ function cloudKeyRow() {
44264
45301
  function octal(mode) {
44265
45302
  return mode === null ? null : mode.toString(8).padStart(4, "0");
44266
45303
  }
44267
- function permissions(mode) {
45304
+ function permissions2(mode) {
44268
45305
  if (mode === null)
44269
45306
  return "unknown";
44270
45307
  return (mode & 63) === 0 ? `${octal(mode)} (owner-only)` : `${octal(mode)} (too open)`;
@@ -44476,6 +45513,8 @@ async function setupRun(argv, deps, opts) {
44476
45513
  if (parsed.positionals.length > 0)
44477
45514
  return fail2([STRAY_ARGUMENT, "", ...JEV_USAGE]);
44478
45515
  const { values, bools } = parsed;
45516
+ if (values.has("--mode") && cloudManagesJevMode())
45517
+ return fail2(CLOUD_MANAGED_MODE_LINES);
44479
45518
  if (bools.has("--key-stdin") && bools.has("--key-from-env"))
44480
45519
  return fail2(["Use one of --key-stdin and --key-from-env, not both."]);
44481
45520
  if (values.has("--token") && (bools.has("--key-stdin") || bools.has("--key-from-env"))) {
@@ -44765,7 +45804,7 @@ async function setupRun(argv, deps, opts) {
44765
45804
  ["timeout", `${cfg.timeoutMs} ms`],
44766
45805
  ["key", keyNote],
44767
45806
  ["config", path],
44768
- ["permissions", permissions(fileMode)]
45807
+ ["permissions", permissions2(fileMode)]
44769
45808
  ], opts), offBase ? jevWarn([
44770
45809
  `Saved as given, but ${provider}'s API is at ${offBase.api}, and this URL sends requests to ${offBase.endpoint} instead.`,
44771
45810
  `Unless something of yours answers Jev requests at that path, every evaluation will fail there and hooks will fall back to regex — \`failproofai jev test\` says which it is in one request. To send requests back to ${provider}'s own API: failproofai jev setup --base-url default`
@@ -44890,7 +45929,7 @@ async function cloudSetup(values, bools, opts) {
44890
45929
  ["timeout", `${cfg.timeoutMs} ms`],
44891
45930
  ["key", keySource ? CLOUD_KEY_SOURCE : cloudKeyRow()],
44892
45931
  ["config", path],
44893
- ["permissions", permissions(fileMode)]
45932
+ ["permissions", permissions2(fileMode)]
44894
45933
  ], opts), jevNote("Hooks read this file on every tool call — no restart. Calls are charged to your FailproofAI Cloud org's plan.", opts), shownMode === "off" ? null : keySource ? jevNext("failproofai jev test", "Check it with one live request:", opts) : jevNext("failproofai config --token <key>", `Jev stays off until this machine is connected with a key that carries jev:evaluate (the "machine" preset on the dashboard's Keys page):`, opts)));
44895
45934
  }
44896
45935
  function routeForRouting(routing) {
@@ -44954,12 +45993,6 @@ function cloudProblemAdvice(problem) {
44954
45993
  "To use FailproofAI Cloud's Jev checks, reconnect without --no-transcripts (failproofai config --token <key>)."
44955
45994
  ];
44956
45995
  }
44957
- if (/plain http/.test(problem)) {
44958
- return [
44959
- "Jev does not run here: FailproofAI Cloud set enforce, and this machine is connected over plain http, where Jev runs only in observe mode.",
44960
- "Reconnect to an https FailproofAI Cloud URL (failproofai config --token <key> --url https://…), or set the machine's Jev mode to observe from FailproofAI Cloud."
44961
- ];
44962
- }
44963
45996
  return [
44964
45997
  `Jev cannot run here: ${problem}.`,
44965
45998
  "Connect with a key that carries jev:evaluate (failproofai config --token <key>). A local jev.json is not used while FailproofAI Cloud sets the mode."
@@ -45158,7 +46191,7 @@ The daemon does not see this shell's environment.`) : [];
45158
46191
  ...route ? [["endpoint", displayEndpoint(route.endpoint)]] : [],
45159
46192
  ["mode", modeLine(inspection.routing.mode ?? DEFAULT_JEV_MODE)],
45160
46193
  ["key", `from ${JEV_API_KEY_ENV}, not set in this shell; the daemon never sees a shell's environment`],
45161
- ["config", `${inspection.path}${kit.sep}${permissions(inspection.mode)}`]
46194
+ ["config", `${inspection.path}${kit.sep}${permissions2(inspection.mode)}`]
45162
46195
  ], [legacyNote], [step(`Set ${JEV_API_KEY_ENV} for this shell, or store the key in the file:`, "failproofai jev setup --key-stdin < key-file")]));
45163
46196
  }
45164
46197
  if (inspection.status === "off") {
@@ -45225,7 +46258,7 @@ The daemon does not see this shell's environment.`) : [];
45225
46258
  "key",
45226
46259
  inspection.keySource === "cloud" ? CLOUD_KEY_SOURCE : inspection.keySource === "file" ? "set in the config file" : `from ${JEV_API_KEY_ENV} (this shell only; the daemon does not see it)`
45227
46260
  ],
45228
- ["config", `${inspection.path}${kit.sep}${permissions(inspection.mode)}`],
46261
+ ["config", `${inspection.path}${kit.sep}${permissions2(inspection.mode)}`],
45229
46262
  ...coverage ? [["reviewable", reviewableSummary(coverage)]] : []
45230
46263
  ], [coverageProblem ? kit.notice("caution", coverageProblem) : [], legacyNote], []));
45231
46264
  }
@@ -45434,7 +46467,7 @@ function remove(argv, opts) {
45434
46467
  if (parsed.positionals.length > 0)
45435
46468
  return fail2([STRAY_ARGUMENT, "", ...JEV_USAGE]);
45436
46469
  const path = jevConfigPath();
45437
- if (!existsSync52(path)) {
46470
+ if (!existsSync53(path)) {
45438
46471
  return ok2(stack(jevHeader("Jev remove", "nothing to do", opts), jevNote(`There is no ${path}; Jev is already off.`, opts)));
45439
46472
  }
45440
46473
  try {
@@ -45470,7 +46503,7 @@ async function runJevCommand(argv, deps = {}) {
45470
46503
  }
45471
46504
  }
45472
46505
  }
45473
- var liveModelListReader = (url, apiKey) => readJevModelList(url, apiKey, AbortSignal.timeout(JEV_MODEL_LIST_TIMEOUT_MS)), ok2 = (lines, json) => ({ lines, exitCode: 0, ...json !== undefined ? { json } : {} }), fail2 = (lines, json) => ({ lines, exitCode: 1, ...json !== undefined ? { json } : {} }), JEV_USAGE, JEV_TEST_QUESTION_ID = "jev_test", DEFAULT_TEST_TIMEOUT_MS = 15000, ENV_KEY_STAND_IN = "env-key-stand-in", STRAY_ARGUMENT = "Unexpected argument (not repeated here, in case it is a key). The key goes on stdin: --key-stdin.", SUBCOMMAND_SHAPE, VALUE_FLAGS2, BOOL_FLAGS, OPTION_SHAPE, CLOUD_KEY_SOURCE = "FailproofAI Cloud connection", KEY_LACKS_JEV_TITLE = "off — no Jev key is stored for this machine's FailproofAI Cloud connection", MAX_SHOWN_MODELS = 20, TOKEN_HISTORY_WARNING, CLOUD_CREDENTIAL_REFUSED = "the FailproofAI Cloud credential was refused", MODE_SET_BY_CLOUD = "mode set by FailproofAI Cloud";
46506
+ var liveModelListReader = (url, apiKey) => readJevModelList(url, apiKey, AbortSignal.timeout(JEV_MODEL_LIST_TIMEOUT_MS)), ok2 = (lines, json) => ({ lines, exitCode: 0, ...json !== undefined ? { json } : {} }), fail2 = (lines, json) => ({ lines, exitCode: 1, ...json !== undefined ? { json } : {} }), JEV_USAGE, JEV_TEST_QUESTION_ID = "jev_test", DEFAULT_TEST_TIMEOUT_MS = 15000, ENV_KEY_STAND_IN = "env-key-stand-in", STRAY_ARGUMENT = "Unexpected argument (not repeated here, in case it is a key). The key goes on stdin: --key-stdin.", SUBCOMMAND_SHAPE, VALUE_FLAGS2, BOOL_FLAGS, OPTION_SHAPE, CLOUD_KEY_SOURCE = "FailproofAI Cloud connection", CLOUD_MANAGED_MODE_LINES, KEY_LACKS_JEV_TITLE = "off — no Jev key is stored for this machine's FailproofAI Cloud connection", MAX_SHOWN_MODELS = 20, TOKEN_HISTORY_WARNING, CLOUD_CREDENTIAL_REFUSED = "the FailproofAI Cloud credential was refused", MODE_SET_BY_CLOUD = "mode set by FailproofAI Cloud";
45474
46507
  var init_jev_cli = __esm(() => {
45475
46508
  init_atomic_write();
45476
46509
  init_jev_config();
@@ -45494,6 +46527,7 @@ var init_jev_cli = __esm(() => {
45494
46527
  VALUE_FLAGS2 = new Set(["--provider", "--url", "--token", "--base-url", "--account-id", "--model", "--timeout-ms", "--mode"]);
45495
46528
  BOOL_FLAGS = new Set(["--key-stdin", "--key-from-env", "--json"]);
45496
46529
  OPTION_SHAPE = /^--?[a-z][a-z0-9-]{0,30}$/;
46530
+ CLOUD_MANAGED_MODE_LINES = [CLOUD_MANAGED_JEV_MODE, "Deploy or remove Jev policies in FailproofAI Cloud to change it. Nothing was written."];
45497
46531
  TOKEN_HISTORY_WARNING = [
45498
46532
  TOKEN_ON_ARGV,
45499
46533
  "Prefer piping the key in — `failproofai jev --url <url> --key-stdin < key-file` — and rotate this one if it matters."
@@ -45505,11 +46539,11 @@ import {
45505
46539
  chmodSync as chmodSync13,
45506
46540
  closeSync as closeSync8,
45507
46541
  constants as fsConstants6,
45508
- existsSync as existsSync53,
46542
+ existsSync as existsSync54,
45509
46543
  fstatSync as fstatSync4,
45510
- mkdirSync as mkdirSync30,
46544
+ mkdirSync as mkdirSync31,
45511
46545
  openSync as openSync8,
45512
- readFileSync as readFileSync42,
46546
+ readFileSync as readFileSync43,
45513
46547
  readSync as readSync6,
45514
46548
  statSync as statSync40,
45515
46549
  writeFileSync as writeFileSync26
@@ -45521,17 +46555,17 @@ function detectLayout2() {
45521
46555
  return { kind: "current", version: raw };
45522
46556
  return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
45523
46557
  }
45524
- if (existsSync53(configFile())) {
46558
+ if (existsSync54(configFile())) {
45525
46559
  const layoutThreePositions = [
45526
46560
  legacy.authJson(),
45527
46561
  legacy.nextAudit(),
45528
46562
  legacy.auditSchedule()
45529
46563
  ];
45530
- if (layoutThreePositions.some((p) => existsSync53(p)))
46564
+ if (layoutThreePositions.some((p) => existsSync54(p)))
45531
46565
  return { kind: "stale", found: 3 };
45532
46566
  return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
45533
46567
  }
45534
- if (existsSync53(legacy.configToml()))
46568
+ if (existsSync54(legacy.configToml()))
45535
46569
  return { kind: "stale", found: 2 };
45536
46570
  const layoutOneMarkers = [
45537
46571
  legacy.policyConfig(),
@@ -45542,7 +46576,7 @@ function detectLayout2() {
45542
46576
  legacy.ingestCredentials(),
45543
46577
  legacy.auditDashboard()
45544
46578
  ];
45545
- return layoutOneMarkers.some((p) => existsSync53(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
46579
+ return layoutOneMarkers.some((p) => existsSync54(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
45546
46580
  }
45547
46581
  function readLegacyTomlVersion2(text) {
45548
46582
  const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
@@ -45554,7 +46588,7 @@ function readLegacyTomlVersion2(text) {
45554
46588
  function readVersionFile2() {
45555
46589
  let text;
45556
46590
  try {
45557
- text = readFileSync42(versionFile(), "utf8");
46591
+ text = readFileSync43(versionFile(), "utf8");
45558
46592
  } catch {
45559
46593
  return null;
45560
46594
  }
@@ -45588,14 +46622,14 @@ var init_fp_home2 = () => {};
45588
46622
  import {
45589
46623
  chmodSync as chmodSync14,
45590
46624
  copyFileSync as copyFileSync4,
45591
- existsSync as existsSync54,
45592
- mkdirSync as mkdirSync31,
45593
- readFileSync as readFileSync43,
46625
+ existsSync as existsSync55,
46626
+ mkdirSync as mkdirSync32,
46627
+ readFileSync as readFileSync44,
45594
46628
  renameSync as renameSync15,
45595
- rmSync as rmSync20,
46629
+ rmSync as rmSync21,
45596
46630
  writeFileSync as writeFileSync27
45597
46631
  } from "node:fs";
45598
- import { basename as basename10, dirname as dirname29, join as join42, resolve as resolve37 } from "node:path";
46632
+ import { basename as basename10, dirname as dirname29, join as join43, resolve as resolve38 } from "node:path";
45599
46633
  function migrateToLayout42() {
45600
46634
  const moves = [
45601
46635
  { from: legacy.authJson(), to: auditSessionFile() },
@@ -45604,20 +46638,20 @@ function migrateToLayout42() {
45604
46638
  ];
45605
46639
  const authDirOverride = process.env.FAILPROOFAI_AUTH_DIR;
45606
46640
  if (authDirOverride) {
45607
- moves.push({ from: join42(authDirOverride, "auth.json"), to: join42(authDirOverride, "session.json") }, {
45608
- from: join42(authDirOverride, "next-audit.json"),
45609
- to: join42(authDirOverride, "reminder.json")
46641
+ moves.push({ from: join43(authDirOverride, "auth.json"), to: join43(authDirOverride, "session.json") }, {
46642
+ from: join43(authDirOverride, "next-audit.json"),
46643
+ to: join43(authDirOverride, "reminder.json")
45610
46644
  });
45611
46645
  }
45612
46646
  const migrated = [];
45613
46647
  for (const { from, to } of moves) {
45614
- if (!existsSync54(from))
46648
+ if (!existsSync55(from))
45615
46649
  continue;
45616
- if (existsSync54(to)) {
45617
- rmSync20(from, { force: true });
46650
+ if (existsSync55(to)) {
46651
+ rmSync21(from, { force: true });
45618
46652
  continue;
45619
46653
  }
45620
- mkdirSync31(dirname29(to), { recursive: true });
46654
+ mkdirSync32(dirname29(to), { recursive: true });
45621
46655
  try {
45622
46656
  renameSync15(from, to);
45623
46657
  } catch {
@@ -45625,16 +46659,16 @@ function migrateToLayout42() {
45625
46659
  copyFileSync4(from, to);
45626
46660
  } catch (err) {
45627
46661
  try {
45628
- rmSync20(to, { force: true });
46662
+ rmSync21(to, { force: true });
45629
46663
  } catch {}
45630
46664
  throw err;
45631
46665
  }
45632
- rmSync20(from, { force: true });
46666
+ rmSync21(from, { force: true });
45633
46667
  }
45634
46668
  migrated.push(`${basename10(from)} → audit/${basename10(to)}`);
45635
46669
  }
45636
46670
  for (const secret of [auditSessionFile()]) {
45637
- if (!existsSync54(secret))
46671
+ if (!existsSync55(secret))
45638
46672
  continue;
45639
46673
  try {
45640
46674
  chmodSync14(secret, 384);
@@ -45660,7 +46694,7 @@ function planMigration2(from, registry = MIGRATIONS2) {
45660
46694
  }
45661
46695
  function readLedger2() {
45662
46696
  try {
45663
- const parsed = JSON.parse(readFileSync43(migrationLedgerFile(), "utf8"));
46697
+ const parsed = JSON.parse(readFileSync44(migrationLedgerFile(), "utf8"));
45664
46698
  return Array.isArray(parsed) ? parsed : [];
45665
46699
  } catch {
45666
46700
  return [];
@@ -45668,7 +46702,7 @@ function readLedger2() {
45668
46702
  }
45669
46703
  function appendLedger2(entry) {
45670
46704
  try {
45671
- mkdirSync31(migrationsDir(), { recursive: true });
46705
+ mkdirSync32(migrationsDir(), { recursive: true });
45672
46706
  const all = [...readLedger2(), entry];
45673
46707
  writeFileSync27(migrationLedgerFile(), `${JSON.stringify(all, null, 2)}
45674
46708
  `, "utf8");
@@ -45682,12 +46716,12 @@ function pruneMigratedCredentials2(from) {
45682
46716
  for (let layout = from;layout < LAYOUT_VERSION; layout++) {
45683
46717
  for (const { as, landsAt } of PRUNED_AFTER_SUCCESS2) {
45684
46718
  try {
45685
- if (!existsSync54(landsAt()))
46719
+ if (!existsSync55(landsAt()))
45686
46720
  continue;
45687
- const copy = resolve37(migrationBackupDir(layout), as);
45688
- if (!existsSync54(copy))
46721
+ const copy = resolve38(migrationBackupDir(layout), as);
46722
+ if (!existsSync55(copy))
45689
46723
  continue;
45690
- rmSync20(copy, { force: true });
46724
+ rmSync21(copy, { force: true });
45691
46725
  pruned.push(as);
45692
46726
  } catch {}
45693
46727
  }
@@ -45698,7 +46732,7 @@ function filesToBackUp2() {
45698
46732
  const seen = new Map;
45699
46733
  for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
45700
46734
  const path = f.at();
45701
- if (existsSync54(path))
46735
+ if (existsSync55(path))
45702
46736
  seen.set(path, backupNameOf2(f));
45703
46737
  }
45704
46738
  return [...seen].map(([path, as]) => ({ path, as }));
@@ -45707,14 +46741,14 @@ function backupBeforeMigrating2(from) {
45707
46741
  const dir = migrationBackupDir(from);
45708
46742
  const saved = [];
45709
46743
  try {
45710
- rmSync20(dir, { recursive: true, force: true });
45711
- mkdirSync31(dir, { recursive: true });
46744
+ rmSync21(dir, { recursive: true, force: true });
46745
+ mkdirSync32(dir, { recursive: true });
45712
46746
  } catch {
45713
46747
  return [];
45714
46748
  }
45715
46749
  for (const { path: source, as } of filesToBackUp2()) {
45716
46750
  try {
45717
- copyFileSync4(source, resolve37(dir, as));
46751
+ copyFileSync4(source, resolve38(dir, as));
45718
46752
  saved.push(as);
45719
46753
  } catch {}
45720
46754
  }
@@ -45852,16 +46886,16 @@ var init_migrations2 = __esm(() => {
45852
46886
 
45853
46887
  // src/hooks/daemon-service.ts
45854
46888
  import {
45855
- existsSync as existsSync55,
45856
- mkdirSync as mkdirSync32,
46889
+ existsSync as existsSync56,
46890
+ mkdirSync as mkdirSync33,
45857
46891
  mkdtempSync as mkdtempSync2,
45858
- readFileSync as readFileSync44,
46892
+ readFileSync as readFileSync45,
45859
46893
  writeFileSync as writeFileSync28,
45860
46894
  unlinkSync as unlinkSync10,
45861
- rmSync as rmSync21
46895
+ rmSync as rmSync22
45862
46896
  } from "node:fs";
45863
- import { homedir as homedir37, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
45864
- import { resolve as resolve38 } from "node:path";
46897
+ import { homedir as homedir38, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
46898
+ import { resolve as resolve39 } from "node:path";
45865
46899
  import { execFileSync as execFileSync3 } from "node:child_process";
45866
46900
  function isDaemonSupportedPlatform2() {
45867
46901
  return process.platform === "linux" || process.platform === "darwin";
@@ -45877,13 +46911,13 @@ function resolveFailproofaidBinaryPath2() {
45877
46911
  if (process.env.FAILPROOFAI_DAEMON_BINARY)
45878
46912
  return process.env.FAILPROOFAI_DAEMON_BINARY;
45879
46913
  const downloaded = installedBinaryPath();
45880
- if (existsSync55(downloaded))
46914
+ if (existsSync56(downloaded))
45881
46915
  return downloaded;
45882
46916
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
45883
46917
  if (packageRoot) {
45884
46918
  for (const profile of ["release", "debug"]) {
45885
- const candidate = resolve38(packageRoot, "target", profile, "failproofaid");
45886
- if (existsSync55(candidate))
46919
+ const candidate = resolve39(packageRoot, "target", profile, "failproofaid");
46920
+ if (existsSync56(candidate))
45887
46921
  return candidate;
45888
46922
  }
45889
46923
  }
@@ -45913,8 +46947,8 @@ function resolveWorkerCommand2() {
45913
46947
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
45914
46948
  if (!packageRoot)
45915
46949
  return null;
45916
- const workerScript = resolve38(packageRoot, "dist", "worker.mjs");
45917
- if (!existsSync55(workerScript))
46950
+ const workerScript = resolve39(packageRoot, "dist", "worker.mjs");
46951
+ if (!existsSync56(workerScript))
45918
46952
  return null;
45919
46953
  return `${shellQuote2(process.execPath)} ${shellQuote2(workerScript)}`;
45920
46954
  }
@@ -45924,8 +46958,8 @@ function resolveCliCommand2() {
45924
46958
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
45925
46959
  if (!packageRoot)
45926
46960
  return null;
45927
- const cliScript = resolve38(packageRoot, "dist", "cli.mjs");
45928
- if (!existsSync55(cliScript))
46961
+ const cliScript = resolve39(packageRoot, "dist", "cli.mjs");
46962
+ if (!existsSync56(cliScript))
45929
46963
  return null;
45930
46964
  return `${shellQuote2(process.execPath)} ${shellQuote2(cliScript)}`;
45931
46965
  }
@@ -45938,8 +46972,8 @@ function workerScriptPath2() {
45938
46972
  const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
45939
46973
  if (!packageRoot)
45940
46974
  return null;
45941
- const workerScript = resolve38(packageRoot, "dist", "worker.mjs");
45942
- return existsSync55(workerScript) ? workerScript : null;
46975
+ const workerScript = resolve39(packageRoot, "dist", "worker.mjs");
46976
+ return existsSync56(workerScript) ? workerScript : null;
45943
46977
  }
45944
46978
  function assertUnitSafe2(value, field) {
45945
46979
  if (UNIT_UNSAFE_RE2.test(value)) {
@@ -45965,10 +46999,10 @@ function systemdUnitDir2() {
45965
46999
  return process.env.FAILPROOFAI_SYSTEMD_DIR || "/etc/systemd/system";
45966
47000
  }
45967
47001
  function systemdUnitPath2(user = serviceUser2()) {
45968
- return resolve38(systemdUnitDir2(), systemdUnitName2(user));
47002
+ return resolve39(systemdUnitDir2(), systemdUnitName2(user));
45969
47003
  }
45970
47004
  function legacySystemdUserUnitPath2() {
45971
- return resolve38(homedir37(), ".config", "systemd", "user", "failproofaid.service");
47005
+ return resolve39(homedir38(), ".config", "systemd", "user", "failproofaid.service");
45972
47006
  }
45973
47007
  function launchdLabel2(user = serviceUser2()) {
45974
47008
  return `ai.failproof.failproofaid.${user}`;
@@ -45977,7 +47011,7 @@ function launchdPlistPath2(user = serviceUser2()) {
45977
47011
  return `/Library/LaunchDaemons/${launchdLabel2(user)}.plist`;
45978
47012
  }
45979
47013
  function legacyLaunchAgentPlistPath2() {
45980
- return resolve38(homedir37(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
47014
+ return resolve39(homedir38(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
45981
47015
  }
45982
47016
  function legacySharedLaunchDaemonPath2() {
45983
47017
  return "/Library/LaunchDaemons/ai.failproof.failproofaid.plist";
@@ -46015,20 +47049,20 @@ function runPrivileged2(command, args) {
46015
47049
  execFileSync3(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS2 });
46016
47050
  }
46017
47051
  function writePrivilegedFile2(destination, contents, mode = "0644") {
46018
- const stagingDir = mkdtempSync2(resolve38(tmpdir3(), "failproofaid-stage-"));
46019
- const staging = resolve38(stagingDir, "service-definition");
47052
+ const stagingDir = mkdtempSync2(resolve39(tmpdir3(), "failproofaid-stage-"));
47053
+ const staging = resolve39(stagingDir, "service-definition");
46020
47054
  try {
46021
47055
  writeFileSync28(staging, contents, "utf8");
46022
47056
  runPrivileged2("install", ["-m", mode, staging, destination]);
46023
47057
  } finally {
46024
- rmSync21(stagingDir, { recursive: true, force: true });
47058
+ rmSync22(stagingDir, { recursive: true, force: true });
46025
47059
  }
46026
47060
  }
46027
47061
  function removeLegacyUserService2() {
46028
47062
  try {
46029
47063
  if (process.platform === "linux") {
46030
47064
  const legacy = legacySystemdUserUnitPath2();
46031
- if (!existsSync55(legacy))
47065
+ if (!existsSync56(legacy))
46032
47066
  return;
46033
47067
  try {
46034
47068
  execFileSync3("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
@@ -46045,7 +47079,7 @@ function removeLegacyUserService2() {
46045
47079
  } catch {}
46046
47080
  } else {
46047
47081
  const legacyAgent = legacyLaunchAgentPlistPath2();
46048
- if (existsSync55(legacyAgent)) {
47082
+ if (existsSync56(legacyAgent)) {
46049
47083
  try {
46050
47084
  execFileSync3("launchctl", ["unload", "-w", legacyAgent], {
46051
47085
  stdio: "ignore",
@@ -46055,7 +47089,7 @@ function removeLegacyUserService2() {
46055
47089
  unlinkSync10(legacyAgent);
46056
47090
  }
46057
47091
  const legacyDaemon = legacySharedLaunchDaemonPath2();
46058
- if (existsSync55(legacyDaemon)) {
47092
+ if (existsSync56(legacyDaemon)) {
46059
47093
  try {
46060
47094
  runPrivileged2("launchctl", ["unload", "-w", legacyDaemon]);
46061
47095
  } catch {}
@@ -46071,8 +47105,8 @@ function systemdUnitContents2(binaryPath, workerCmd, cliCmd = null) {
46071
47105
  `).join("");
46072
47106
  const user = assertUnitSafe2(serviceUser2(), "User");
46073
47107
  assertUnitSafe2(binaryPath, "ExecStart");
46074
- assertUnitSafe2(homedir37(), "HOME");
46075
- const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync55(p));
47108
+ assertUnitSafe2(homedir38(), "HOME");
47109
+ const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync56(p));
46076
47110
  const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe2(p, "ConditionPathExists")}
46077
47111
  `).join("");
46078
47112
  return `[Unit]
@@ -46085,7 +47119,7 @@ User=${user}
46085
47119
  # Set explicitly rather than relying on systemd deriving it from User=:
46086
47120
  # failproofaid stores per-user state and refuses to start without HOME, so this
46087
47121
  # required variable is not left to a version-dependent default.
46088
- Environment="HOME=${homedir37()}"
47122
+ Environment="HOME=${homedir38()}"
46089
47123
  ${envLines}ExecStart=${binaryPath}
46090
47124
  Restart=on-failure
46091
47125
  RestartSec=2
@@ -46130,9 +47164,9 @@ ${env.map(([key, value]) => ` <key>${key}</key>
46130
47164
  <key>UserName</key>
46131
47165
  <string>${escapeXml2(serviceUser2())}</string>
46132
47166
  ${envBlock} <key>StandardOutPath</key>
46133
- <string>${escapeXml2(resolve38(logDir, "failproofaid.log"))}</string>
47167
+ <string>${escapeXml2(resolve39(logDir, "failproofaid.log"))}</string>
46134
47168
  <key>StandardErrorPath</key>
46135
- <string>${escapeXml2(resolve38(logDir, "failproofaid.err.log"))}</string>
47169
+ <string>${escapeXml2(resolve39(logDir, "failproofaid.err.log"))}</string>
46136
47170
  </dict>
46137
47171
  </plist>
46138
47172
  `;
@@ -46178,7 +47212,7 @@ async function installDaemonService2() {
46178
47212
  } else {
46179
47213
  const plistPath = launchdPlistPath2();
46180
47214
  const logDir = logsDir();
46181
- mkdirSync32(logDir, { recursive: true });
47215
+ mkdirSync33(logDir, { recursive: true });
46182
47216
  try {
46183
47217
  runPrivileged2("launchctl", ["unload", plistPath]);
46184
47218
  } catch {}
@@ -46294,7 +47328,7 @@ async function refreshDaemonToCliVersion(deps = {}) {
46294
47328
  return { ok: true, lines: ["No failproofaid service on this machine; nothing to update."] };
46295
47329
  }
46296
47330
  const recorded = readVersionFile()?.daemon;
46297
- if (current === "running" && recorded === version2 && existsSync55(installedBinaryPath(version2))) {
47331
+ if (current === "running" && recorded === version2 && existsSync56(installedBinaryPath(version2))) {
46298
47332
  return { ok: true, lines: [`failproofaid ${version2} is already installed and running; nothing to update.`] };
46299
47333
  }
46300
47334
  if (interactive())
@@ -46333,7 +47367,7 @@ function daemonServiceStatus2() {
46333
47367
  if (!isDaemonSupportedPlatform2())
46334
47368
  return "unsupported-platform";
46335
47369
  if (process.platform === "linux") {
46336
- if (!existsSync55(systemdUnitPath2()))
47370
+ if (!existsSync56(systemdUnitPath2()))
46337
47371
  return "not-installed";
46338
47372
  try {
46339
47373
  const out = execFileSync3("systemctl", ["is-active", systemdUnitName2()], {
@@ -46346,7 +47380,7 @@ function daemonServiceStatus2() {
46346
47380
  }
46347
47381
  }
46348
47382
  const plistPath = launchdPlistPath2();
46349
- if (!existsSync55(plistPath))
47383
+ if (!existsSync56(plistPath))
46350
47384
  return "not-installed";
46351
47385
  try {
46352
47386
  const root = typeof process.getuid === "function" && process.getuid() === 0;
@@ -46414,7 +47448,7 @@ var init_hermes_update = __esm(() => {
46414
47448
  });
46415
47449
 
46416
47450
  // src/hooks/uninstall-cli.ts
46417
- import { existsSync as existsSync56, rmSync as rmSync22 } from "node:fs";
47451
+ import { existsSync as existsSync57, rmSync as rmSync23 } from "node:fs";
46418
47452
  function survey(cwd) {
46419
47453
  const clis = [];
46420
47454
  for (const id of listInstallableIds()) {
@@ -46434,11 +47468,12 @@ function survey(cwd) {
46434
47468
  const servicePath = isDaemonSupportedPlatform() ? daemonServiceFilePath() : null;
46435
47469
  return {
46436
47470
  clis,
47471
+ skillDirs: installedAgentSkillDirs(),
46437
47472
  codexSystemPath: codexSystemHooksPresent() ? codexSystemConfigPath() : null,
46438
47473
  servicePath,
46439
- serviceInstalled: !!servicePath && existsSync56(servicePath),
47474
+ serviceInstalled: !!servicePath && existsSync57(servicePath),
46440
47475
  daemonConfigured,
46441
- homeExists: existsSync56(failproofaiHome())
47476
+ homeExists: existsSync57(failproofaiHome())
46442
47477
  };
46443
47478
  }
46444
47479
  async function runUninstallCommand(opts = {}) {
@@ -46448,7 +47483,7 @@ async function runUninstallCommand(opts = {}) {
46448
47483
  const many = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
46449
47484
  const errText = (err) => err instanceof Error ? err.message : String(err);
46450
47485
  const lines = [];
46451
- const nothingToDo = found.clis.length === 0 && !found.serviceInstalled && !found.daemonConfigured && !(opts.purge && found.homeExists);
47486
+ const nothingToDo = found.clis.length === 0 && found.skillDirs.length === 0 && !found.serviceInstalled && !found.daemonConfigured && !(opts.purge && found.homeExists);
46452
47487
  if (nothingToDo) {
46453
47488
  return {
46454
47489
  exitCode: 0,
@@ -46462,6 +47497,8 @@ ${home} still holds settings and history. Delete it with \`failproofai uninstal
46462
47497
  const removals = [];
46463
47498
  if (found.clis.length > 0)
46464
47499
  removals.push(`failproofai's hooks from ${many(found.clis.length, "agent")}`);
47500
+ if (found.skillDirs.length > 0)
47501
+ removals.push(`the failproofai skill from ${many(found.skillDirs.length, "agent folder")}`);
46465
47502
  if (found.serviceInstalled)
46466
47503
  removals.push("the failproofaid service (needs sudo)");
46467
47504
  if (purging)
@@ -46548,6 +47585,15 @@ ${home} still holds settings and history. Delete it with \`failproofai uninstal
46548
47585
  ` + "Finish with `failproofai policies --uninstall --scope all`");
46549
47586
  }
46550
47587
  }
47588
+ if (found.skillDirs.length > 0) {
47589
+ const left = found.skillDirs.length - removeAgentSkill().length;
47590
+ if (left === 0)
47591
+ lines.push(kit.ok(`Removed the failproofai skill from ${many(found.skillDirs.length, "agent folder")}.`));
47592
+ else
47593
+ failures.push(`Could not remove the failproofai skill from ${many(left, "agent folder")}:
47594
+ ${installedAgentSkillDirs().join(`
47595
+ `)}`);
47596
+ }
46551
47597
  if (found.serviceInstalled) {
46552
47598
  elevateOnce();
46553
47599
  const byHand = (servicePath) => `
@@ -46557,18 +47603,18 @@ Remove it as root:
46557
47603
  Check it with \`${daemonStatusCommand()}\`` : "");
46558
47604
  try {
46559
47605
  await uninstallDaemonService();
46560
- if (found.servicePath && existsSync56(found.servicePath)) {
47606
+ if (found.servicePath && existsSync57(found.servicePath)) {
46561
47607
  failures.push("The failproofaid service is still there, most often because sudo was not available." + byHand(found.servicePath));
46562
47608
  } else {
46563
47609
  lines.push(kit.ok("Stopped and removed the failproofaid service."));
46564
47610
  }
46565
47611
  } catch (err) {
46566
- failures.push(`Could not remove the failproofaid service: ${errText(err)}` + (found.servicePath && existsSync56(found.servicePath) ? byHand(found.servicePath) : ""));
47612
+ failures.push(`Could not remove the failproofaid service: ${errText(err)}` + (found.servicePath && existsSync57(found.servicePath) ? byHand(found.servicePath) : ""));
46567
47613
  }
46568
47614
  }
46569
47615
  if (purging) {
46570
47616
  try {
46571
- rmSync22(home, { recursive: true, force: true });
47617
+ rmSync23(home, { recursive: true, force: true });
46572
47618
  purged = true;
46573
47619
  lines.push(kit.ok(`Deleted ${home}.`));
46574
47620
  } catch (err) {
@@ -46603,16 +47649,17 @@ var init_uninstall_cli = __esm(() => {
46603
47649
  init_daemon_service();
46604
47650
  init_integrations();
46605
47651
  init_manager();
47652
+ init_agent_skill();
46606
47653
  init_codex_system_hooks();
46607
47654
  init_tui();
46608
47655
  });
46609
47656
 
46610
47657
  // src/hooks/pack-cli.ts
46611
47658
  import { execFileSync as execFileSync4 } from "node:child_process";
46612
- import { homedir as homedir38, tmpdir as tmpdir4 } from "node:os";
47659
+ import { homedir as homedir39, tmpdir as tmpdir4 } from "node:os";
46613
47660
  import { createHash as createHash14 } from "node:crypto";
46614
- import { copyFileSync as copyFileSync5, existsSync as existsSync57, mkdirSync as mkdirSync33, mkdtempSync as mkdtempSync3, readFileSync as readFileSync45, readdirSync as readdirSync24, realpathSync as realpathSync5, rmSync as rmSync23, statSync as statSync41, writeFileSync as writeFileSync29 } from "node:fs";
46615
- import { basename as basename11, dirname as dirname30, isAbsolute as isAbsolute6, join as join43, relative as relative5, resolve as resolve39, sep as sep6 } from "node:path";
47661
+ import { copyFileSync as copyFileSync5, existsSync as existsSync58, mkdirSync as mkdirSync34, mkdtempSync as mkdtempSync3, readFileSync as readFileSync46, readdirSync as readdirSync24, realpathSync as realpathSync5, rmSync as rmSync24, statSync as statSync41, writeFileSync as writeFileSync29 } from "node:fs";
47662
+ import { basename as basename11, dirname as dirname30, isAbsolute as isAbsolute6, join as join44, relative as relative5, resolve as resolve40, sep as sep6 } from "node:path";
46616
47663
  function parseList(rest, ...flags) {
46617
47664
  let found = false;
46618
47665
  const out = [];
@@ -46727,7 +47774,7 @@ async function build(rest) {
46727
47774
  const commit = flag("commit");
46728
47775
  const effect = flag("effect") ?? "enforce";
46729
47776
  const minCliVersion = flag("min-cli-version");
46730
- const outDir = resolve39(flag("out") ?? "dist-pack");
47777
+ const outDir = resolve40(flag("out") ?? "dist-pack");
46731
47778
  const repo = flag("repo");
46732
47779
  if (!entry || !id || !version) {
46733
47780
  return fail3([
@@ -46747,10 +47794,10 @@ async function build(rest) {
46747
47794
  } catch (err) {
46748
47795
  return fail3([err instanceof Error ? err.message : String(err)]);
46749
47796
  }
46750
- const entryPath = resolve39(entry);
46751
- if (!existsSync57(entryPath))
47797
+ const entryPath = resolve40(entry);
47798
+ if (!existsSync58(entryPath))
46752
47799
  return fail3([`No such file: ${entryPath}`]);
46753
- const source = readFileSync45(entryPath, "utf8");
47800
+ const source = readFileSync46(entryPath, "utf8");
46754
47801
  const localImport = firstRelativeSpecifier(source);
46755
47802
  if (localImport) {
46756
47803
  return fail3([
@@ -46869,14 +47916,14 @@ async function build(rest) {
46869
47916
  ...semantic.length > 0 ? { semantic } : {}
46870
47917
  }, null, 2) + `
46871
47918
  `;
46872
- mkdirSync33(outDir, { recursive: true });
46873
- const manifestPath = resolve39(outDir, PACK_MANIFEST_ASSET);
46874
- const entryOut = resolve39(outDir, PACK_ENTRY_ASSET);
47919
+ mkdirSync34(outDir, { recursive: true });
47920
+ const manifestPath = resolve40(outDir, PACK_MANIFEST_ASSET);
47921
+ const entryOut = resolve40(outDir, PACK_ENTRY_ASSET);
46875
47922
  writeFileSync29(manifestPath, manifest, "utf8");
46876
47923
  copyFileSync5(entryPath, entryOut);
46877
47924
  const sha = (bytes) => createHash14("sha256").update(bytes).digest("hex");
46878
- writeFileSync29(resolve39(outDir, PACK_CHECKSUMS_ASSET), `${sha(manifest)} ${PACK_MANIFEST_ASSET}
46879
- ${sha(readFileSync45(entryOut))} ${PACK_ENTRY_ASSET}
47925
+ writeFileSync29(resolve40(outDir, PACK_CHECKSUMS_ASSET), `${sha(manifest)} ${PACK_MANIFEST_ASSET}
47926
+ ${sha(readFileSync46(entryOut))} ${PACK_ENTRY_ASSET}
46880
47927
  `, "utf8");
46881
47928
  const on = policies.filter((p) => p.defaultEnabled).length;
46882
47929
  const semanticOnly = policies.length === 0 && semantic.length > 0;
@@ -47017,11 +48064,11 @@ async function scaffold(target) {
47017
48064
  return ok3(["Nothing written."]);
47018
48065
  chosen = answer.trim();
47019
48066
  }
47020
- const path = resolve39(withPolicyExtension(chosen ?? "my-policies"));
47021
- if (existsSync57(path)) {
48067
+ const path = resolve40(withPolicyExtension(chosen ?? "my-policies"));
48068
+ if (existsSync58(path)) {
47022
48069
  return fail3([`${path} already exists — name a different file, or edit that one.`]);
47023
48070
  }
47024
- mkdirSync33(resolve39(path, ".."), { recursive: true });
48071
+ mkdirSync34(resolve40(path, ".."), { recursive: true });
47025
48072
  writeFileSync29(path, `import { customPolicies, allow, deny } from "failproofai";
47026
48073
 
47027
48074
  // One file, no relative imports: only this entry is digest-pinned, so a pack
@@ -47059,7 +48106,7 @@ customPolicies.add({
47059
48106
  function inferRepo(entryPath) {
47060
48107
  try {
47061
48108
  const url = execFileSync4("git", ["remote", "get-url", "origin"], {
47062
- cwd: resolve39(entryPath, ".."),
48109
+ cwd: resolve40(entryPath, ".."),
47063
48110
  encoding: "utf8",
47064
48111
  stdio: ["ignore", "pipe", "ignore"],
47065
48112
  timeout: 5000
@@ -47071,7 +48118,7 @@ function inferRepo(entryPath) {
47071
48118
  }
47072
48119
  }
47073
48120
  function inferCommit(entryPath, outDir, sources) {
47074
- const cwd = resolve39(entryPath, "..");
48121
+ const cwd = resolve40(entryPath, "..");
47075
48122
  const raw = (args) => {
47076
48123
  try {
47077
48124
  return execFileSync4("git", args, {
@@ -47126,9 +48173,9 @@ function realOrSelf(path) {
47126
48173
  }
47127
48174
  }
47128
48175
  function sourcesInHead(git, raw, sources) {
47129
- const paths = sources.map((f) => realOrSelf(resolve39(f)));
48176
+ const paths = sources.map((f) => realOrSelf(resolve40(f)));
47130
48177
  const shown = git(["rev-parse", "--show-toplevel"]);
47131
- const top = shown === null ? null : realOrSelf(resolve39(shown));
48178
+ const top = shown === null ? null : realOrSelf(resolve40(shown));
47132
48179
  const missing = [];
47133
48180
  for (const path of paths) {
47134
48181
  const rel = top ? relative5(top, path) : null;
@@ -47152,7 +48199,7 @@ function sourcesInHead(git, raw, sources) {
47152
48199
  ]);
47153
48200
  if (status === null)
47154
48201
  return { ok: false, offending: paths, why: "modified" };
47155
- const dirty = porcelainEntries(status).flatMap((entry) => [entry.path, entry.from].filter(Boolean).map((path) => resolve39(top ?? ".", path)));
48202
+ const dirty = porcelainEntries(status).flatMap((entry) => [entry.path, entry.from].filter(Boolean).map((path) => resolve40(top ?? ".", path)));
47156
48203
  if (dirty.length > 0)
47157
48204
  return { ok: false, offending: dirty, why: "modified" };
47158
48205
  return { ok: true };
@@ -47160,19 +48207,19 @@ function sourcesInHead(git, raw, sources) {
47160
48207
  function skipOutDir(git, entryPath, outDir) {
47161
48208
  if (!outDir)
47162
48209
  return [];
47163
- const out = resolve39(outDir);
47164
- if (resolve39(entryPath).startsWith(out + sep6))
48210
+ const out = resolve40(outDir);
48211
+ if (resolve40(entryPath).startsWith(out + sep6))
47165
48212
  return [];
47166
48213
  const top = git(["rev-parse", "--show-toplevel"]);
47167
48214
  if (!top)
47168
48215
  return [];
47169
- const rel = relative5(resolve39(top), out);
48216
+ const rel = relative5(resolve40(top), out);
47170
48217
  if (!rel || rel.startsWith("..") || isAbsolute6(rel))
47171
48218
  return [];
47172
48219
  return ["--", ":(top)", `:(exclude,literal,top)${rel.split(sep6).join("/")}`];
47173
48220
  }
47174
48221
  function inferTaggedVersion(entryPath, sources) {
47175
- const cwd = resolve39(entryPath, "..");
48222
+ const cwd = resolve40(entryPath, "..");
47176
48223
  const raw = (args) => {
47177
48224
  try {
47178
48225
  return execFileSync4("git", args, {
@@ -47210,11 +48257,11 @@ function findEntry(dir) {
47210
48257
  for (const name of names) {
47211
48258
  if (!/\.(mjs|js|ts)$/.test(name))
47212
48259
  continue;
47213
- const full = resolve39(dir, name);
48260
+ const full = resolve40(dir, name);
47214
48261
  try {
47215
48262
  if (statSync41(full).size > 512 * 1024)
47216
48263
  continue;
47217
- const text = readFileSync45(full, "utf8");
48264
+ const text = readFileSync46(full, "utf8");
47218
48265
  if (/from\s+["']failproofai["']/.test(text) && /(?:customPolicies|semanticPolicies)\s*\.\s*add\s*\(/.test(text)) {
47219
48266
  found.push(full);
47220
48267
  }
@@ -47225,7 +48272,7 @@ function findEntry(dir) {
47225
48272
  return found.sort();
47226
48273
  }
47227
48274
  function withLocalImports(sources) {
47228
- const seen = new Set(sources.map((f) => resolve39(f)));
48275
+ const seen = new Set(sources.map((f) => resolve40(f)));
47229
48276
  const queue = [...seen];
47230
48277
  while (queue.length > 0 && seen.size < 500) {
47231
48278
  const file = queue.shift();
@@ -47233,13 +48280,13 @@ function withLocalImports(sources) {
47233
48280
  try {
47234
48281
  if (statSync41(file).size > 512 * 1024)
47235
48282
  continue;
47236
- text = readFileSync45(file, "utf8");
48283
+ text = readFileSync46(file, "utf8");
47237
48284
  } catch {
47238
48285
  continue;
47239
48286
  }
47240
48287
  for (const re of LOCAL_IMPORT_RES) {
47241
48288
  for (const match of text.matchAll(re)) {
47242
- const base = resolve39(dirname30(file), match[1]);
48289
+ const base = resolve40(dirname30(file), match[1]);
47243
48290
  for (const ext of LOCAL_IMPORT_EXTENSIONS) {
47244
48291
  const candidate = base + ext;
47245
48292
  if (seen.has(candidate))
@@ -47260,7 +48307,7 @@ function withLocalImports(sources) {
47260
48307
  return [...seen];
47261
48308
  }
47262
48309
  function settleGitState(entryPath, provenance, packFiles, id, outDir) {
47263
- const cwd = resolve39(entryPath, "..");
48310
+ const cwd = resolve40(entryPath, "..");
47264
48311
  const raw = (args) => {
47265
48312
  try {
47266
48313
  return execFileSync4("git", args, {
@@ -47335,11 +48382,11 @@ function settleGitState(entryPath, provenance, packFiles, id, outDir) {
47335
48382
  if (root === null || status === null)
47336
48383
  return null;
47337
48384
  const entries = porcelainEntries(status);
47338
- const dirty = entries.map((entry) => resolve39(root, entry.path));
47339
- const renamedFrom = entries.filter((entry) => entry.from).map((entry) => resolve39(root, entry.from));
48385
+ const dirty = entries.map((entry) => resolve40(root, entry.path));
48386
+ const renamedFrom = entries.filter((entry) => entry.from).map((entry) => resolve40(root, entry.from));
47340
48387
  if (dirty.length === 0)
47341
48388
  return null;
47342
- const mine = new Set(packFiles.map((f) => resolve39(f)));
48389
+ const mine = new Set(packFiles.map((f) => resolve40(f)));
47343
48390
  const foreign = dirty.filter((path) => !mine.has(path));
47344
48391
  const toStage = [...dirty, ...renamedFrom];
47345
48392
  if (foreign.length > 0) {
@@ -47470,7 +48517,7 @@ function pushExistingHistory(dir, owner, name, token) {
47470
48517
  const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
47471
48518
  if (!branch || branch === "HEAD" || !git(["rev-parse", "--verify", "HEAD"]))
47472
48519
  return false;
47473
- const askpass = join43(mkdtempSync3(join43(tmpdir4(), "fpai-askpass-")), "askpass.sh");
48520
+ const askpass = join44(mkdtempSync3(join44(tmpdir4(), "fpai-askpass-")), "askpass.sh");
47474
48521
  try {
47475
48522
  writeFileSync29(askpass, `#!/bin/sh
47476
48523
  case "$1" in
@@ -47492,7 +48539,7 @@ esac
47492
48539
  return true;
47493
48540
  } finally {
47494
48541
  try {
47495
- rmSync23(dirname30(askpass), { recursive: true, force: true });
48542
+ rmSync24(dirname30(askpass), { recursive: true, force: true });
47496
48543
  } catch {}
47497
48544
  }
47498
48545
  }
@@ -47516,8 +48563,8 @@ failproofai policies add ${owner}/${name}
47516
48563
  function findBun() {
47517
48564
  const candidates = [
47518
48565
  "bun",
47519
- process.env.BUN_INSTALL ? resolve39(process.env.BUN_INSTALL, "bin", "bun") : null,
47520
- resolve39(homedir38(), ".bun", "bin", "bun"),
48566
+ process.env.BUN_INSTALL ? resolve40(process.env.BUN_INSTALL, "bin", "bun") : null,
48567
+ resolve40(homedir39(), ".bun", "bin", "bun"),
47521
48568
  "/opt/homebrew/bin/bun",
47522
48569
  "/usr/local/bin/bun"
47523
48570
  ].filter((c) => c !== null);
@@ -47543,12 +48590,12 @@ function bundleEntry(sources, outDir) {
47543
48590
  ]
47544
48591
  };
47545
48592
  }
47546
- mkdirSync33(outDir, { recursive: true });
47547
- const entry = resolve39(outDir, ".entry.generated.mjs");
47548
- writeFileSync29(entry, sources.map((f) => `import ${JSON.stringify(f.startsWith("/") ? f : resolve39(f))};`).join(`
48593
+ mkdirSync34(outDir, { recursive: true });
48594
+ const entry = resolve40(outDir, ".entry.generated.mjs");
48595
+ writeFileSync29(entry, sources.map((f) => `import ${JSON.stringify(f.startsWith("/") ? f : resolve40(f))};`).join(`
47549
48596
  `) + `
47550
48597
  `, "utf8");
47551
- const bundled = resolve39(outDir, ".bundled.mjs");
48598
+ const bundled = resolve40(outDir, ".bundled.mjs");
47552
48599
  try {
47553
48600
  execFileSync4(bun, [
47554
48601
  "build",
@@ -47680,7 +48727,7 @@ async function publish(rest) {
47680
48727
  `);
47681
48728
  const token = githubToken();
47682
48729
  const login = token ? await credentialLogin(token) : null;
47683
- const folder = basename11(resolve39(entry, ".."));
48730
+ const folder = basename11(resolve40(entry, ".."));
47684
48731
  const answered = await askWhereToPublish(`${login ?? "your-account"}/${folder}`, { stdin: process.stdin, stdout: process.stdout });
47685
48732
  if (answered === null)
47686
48733
  return ok3(["Nothing was published."]);
@@ -47690,7 +48737,7 @@ async function publish(rest) {
47690
48737
  let version = flag("version") ?? (entry ? inferTaggedVersion(entry, bundledSources) : null) ?? undefined;
47691
48738
  const dryRun = rest.includes("--dry-run") || !repo;
47692
48739
  const allowPrivate = rest.includes("--allow-private");
47693
- const id = flag("id") ?? repo ?? (dryRun && entry ? `local/${basename11(resolve39(entry, ".."))}` : undefined);
48740
+ const id = flag("id") ?? repo ?? (dryRun && entry ? `local/${basename11(resolve40(entry, ".."))}` : undefined);
47694
48741
  const [owner, name] = (repo ?? "/").split("/");
47695
48742
  if (repo && (!owner || !name || repo.split("/").length !== 2)) {
47696
48743
  return fail3([`--repo must be <owner>/<repo>, got ${JSON.stringify(repo)}`]);
@@ -47709,7 +48756,7 @@ async function publish(rest) {
47709
48756
  "With no --repo it writes the three release assets and stops."
47710
48757
  ]);
47711
48758
  }
47712
- const outDirEarly = resolve39(flag("out") ?? "dist-pack");
48759
+ const outDirEarly = resolve40(flag("out") ?? "dist-pack");
47713
48760
  let provenance = entry ? inferCommit(entry, outDirEarly, bundledSources) : null;
47714
48761
  const gitLines = [];
47715
48762
  if (!version && entry && interactive) {
@@ -47748,7 +48795,7 @@ async function publish(rest) {
47748
48795
  "It needs write access to releases on that repository, and nothing else."
47749
48796
  ]);
47750
48797
  }
47751
- const ensured = await ensureRepo(owner, name, id, token, resolve39(entry, ".."));
48798
+ const ensured = await ensureRepo(owner, name, id, token, resolve40(entry, ".."));
47752
48799
  if ("error" in ensured)
47753
48800
  return fail3(ensured.error);
47754
48801
  created = ensured.created;
@@ -47761,7 +48808,7 @@ async function publish(rest) {
47761
48808
  }
47762
48809
  const tag = flag("tag") ?? version;
47763
48810
  let entryToBuild = entry;
47764
- const needsBundle = discovered.length > 1 || firstRelativeSpecifier(readFileSync45(entry, "utf8")) !== null;
48811
+ const needsBundle = discovered.length > 1 || firstRelativeSpecifier(readFileSync46(entry, "utf8")) !== null;
47765
48812
  if (needsBundle) {
47766
48813
  const sources = discovered.length > 1 ? discovered : [entry];
47767
48814
  const bundled = bundleEntry(sources, outDirEarly);
@@ -47842,7 +48889,7 @@ async function publish(rest) {
47842
48889
  if (prior) {
47843
48890
  await gh(`${GITHUB_API2}/repos/${owner}/${name}/releases/assets/${prior.id}`, auth, { method: "DELETE" });
47844
48891
  }
47845
- const bytes = readFileSync45(resolve39(outDir, asset));
48892
+ const bytes = readFileSync46(resolve40(outDir, asset));
47846
48893
  const upload = await gh(`${GITHUB_UPLOADS2}/repos/${owner}/${name}/releases/${releaseId}/assets?name=${encodeURIComponent(asset)}`, auth, {
47847
48894
  method: "POST",
47848
48895
  contentType: asset.endsWith(".json") ? "application/json" : "application/octet-stream",
@@ -48569,8 +49616,8 @@ var init_install_prompt = __esm(() => {
48569
49616
 
48570
49617
  // src/hooks/manager.ts
48571
49618
  import { execSync as execSync8 } from "node:child_process";
48572
- import { existsSync as existsSync58 } from "node:fs";
48573
- import { resolve as resolve40, basename as basename12 } from "node:path";
49619
+ import { existsSync as existsSync59 } from "node:fs";
49620
+ import { resolve as resolve41, basename as basename12 } from "node:path";
48574
49621
  import { platform as platform4, arch as arch4, release as release3, hostname as hostname6 } from "node:os";
48575
49622
  function getSettingsPath2(scope, cwd) {
48576
49623
  return claudeCode.getSettingsPath(scope, cwd);
@@ -48713,6 +49760,34 @@ function integrationsInstalledAt2(scope, cwd) {
48713
49760
  }
48714
49761
  });
48715
49762
  }
49763
+ function cloudPolicyStatus2() {
49764
+ let count = 0;
49765
+ let observing = false;
49766
+ const jsIds = new Set;
49767
+ try {
49768
+ for (const artifact of listActiveCloudManagedPolicies()) {
49769
+ jsIds.add(artifact.id);
49770
+ if (artifact.effect === "observe")
49771
+ observing = true;
49772
+ else
49773
+ count++;
49774
+ }
49775
+ } catch {}
49776
+ try {
49777
+ const { jevMode } = readCloudJevState();
49778
+ if (jevMode === "enforce" || jevMode === "observe") {
49779
+ for (const assignment of readCloudJevAssignments() ?? []) {
49780
+ if (jsIds.has(assignment.id))
49781
+ continue;
49782
+ if (jevMode === "observe" || assignment.effect === "observe")
49783
+ observing = true;
49784
+ else
49785
+ count++;
49786
+ }
49787
+ }
49788
+ } catch {}
49789
+ return { count, observing };
49790
+ }
48716
49791
  function notEnforcingReason2(cwd) {
48717
49792
  const wired = deduplicateScopes2(HOOK_SCOPES, cwd).some((scope) => hooksInstalledInSettings2(scope, cwd));
48718
49793
  if (!wired)
@@ -48735,17 +49810,13 @@ function notEnforcingReason2(cwd) {
48735
49810
  return null;
48736
49811
  }
48737
49812
  } catch {}
48738
- try {
48739
- for (const artifact of listActiveCloudManagedPolicies()) {
48740
- if (artifact.effect === "observe")
48741
- observing = true;
48742
- else
48743
- return null;
48744
- }
48745
- } catch {}
49813
+ const cloud = cloudPolicyStatus2();
49814
+ if (cloud.count > 0)
49815
+ return null;
49816
+ observing ||= cloud.observing;
48746
49817
  if (configuredCustomPolicyPaths(config).length > 0)
48747
49818
  return null;
48748
- const projectDir = resolve40(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
49819
+ const projectDir = resolve41(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
48749
49820
  if (discoverPolicyFiles(projectDir).length > 0 || discoverPolicyFiles(customPoliciesDir()).length > 0) {
48750
49821
  return null;
48751
49822
  }
@@ -48841,9 +49912,9 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
48841
49912
  delete configToWrite.customPoliciesPath;
48842
49913
  delete configToWrite.customPoliciesPaths;
48843
49914
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
48844
- const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path) => resolve40(path));
48845
- const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path) => resolve40(path)).filter((path) => {
48846
- if (existsSync58(path))
49915
+ const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path) => resolve41(path));
49916
+ const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path) => resolve41(path)).filter((path) => {
49917
+ if (existsSync59(path))
48847
49918
  return true;
48848
49919
  console.log(`Dropping custom policies path (file no longer exists): ${path}`);
48849
49920
  return false;
@@ -48928,7 +49999,7 @@ Validated ${validatedHooks.length} custom hook(s) from ${path}: ${validatedHooks
48928
49999
  const placed = installCodexSystemHooks(codexSystemHookEntries(binaryPath));
48929
50000
  if (placed.ok) {
48930
50001
  const userPath = integration.getSettingsPath("user", cwd);
48931
- if (existsSync58(userPath))
50002
+ if (existsSync59(userPath))
48932
50003
  integration.removeHooksFromFile(userPath);
48933
50004
  writtenSettingsPaths.push({ cli: cliId, path: placed.path });
48934
50005
  continue;
@@ -49125,7 +50196,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
49125
50196
  }
49126
50197
  }
49127
50198
  const settingsPaths = settingsPathsFor(integration, s, cwd);
49128
- const existing = cliId === "hermes" ? settingsPaths : settingsPaths.filter((p) => existsSync58(p));
50199
+ const existing = cliId === "hermes" ? settingsPaths : settingsPaths.filter((p) => existsSync59(p));
49129
50200
  if (existing.length === 0) {
49130
50201
  totalRemoved += removedFromSystem;
49131
50202
  if (removedFromSystem > 0)
@@ -49282,7 +50353,9 @@ function renderPoliciesScreen(screen, opts = {}) {
49282
50353
  out.push(`${INDENT}Jev mode: off, set by FailproofAI Cloud (overrides jev.json).`);
49283
50354
  } else if (jevMode) {
49284
50355
  out.push(`${INDENT}Jev mode: ${jevMode}, set by FailproofAI Cloud. Jev checks run on FailproofAI Cloud; nothing of them is installed here.`);
49285
- out.push(`${INDENT}Jev-only Cloud policies are not listed here; see them with ${kit.cmd("fp fleet show")} or on the dashboard.`);
50356
+ if (!screen.cloud.jevListed) {
50357
+ out.push(`${INDENT}Jev-only Cloud policies are not listed here; see them with ${kit.cmd("fp fleet show")} or on the dashboard.`);
50358
+ }
49286
50359
  }
49287
50360
  for (const problem of problems ?? [])
49288
50361
  out.push(kit.caution(problem));
@@ -49332,8 +50405,8 @@ async function listHooks(cwd, { all = false } = {}) {
49332
50405
  const custom = [];
49333
50406
  let customProblem = null;
49334
50407
  for (const path of configuredCustomPolicyPaths(config)) {
49335
- const absPath = resolve40(findProjectConfigDir(cwd ?? process.cwd()), path);
49336
- if (!existsSync58(absPath)) {
50408
+ const absPath = resolve41(findProjectConfigDir(cwd ?? process.cwd()), path);
50409
+ if (!existsSync59(absPath)) {
49337
50410
  custom.push({ state: "failed", name: absPath, description: "not found" });
49338
50411
  customProblem ??= "A custom policy file was not found, so its policies are not running.";
49339
50412
  continue;
@@ -49353,7 +50426,7 @@ async function listHooks(cwd, { all = false } = {}) {
49353
50426
  }
49354
50427
  }
49355
50428
  const base = findProjectConfigDir(cwd ?? process.cwd());
49356
- const projectDir = resolve40(base, ".failproofai", "policies");
50429
+ const projectDir = resolve41(base, ".failproofai", "policies");
49357
50430
  const userDir = customPoliciesDir();
49358
50431
  const sameDir = userDir === projectDir;
49359
50432
  const conventionDirs = [
@@ -49441,19 +50514,27 @@ async function listHooks(cwd, { all = false } = {}) {
49441
50514
  loadErrors.set(e.id, e.message);
49442
50515
  }
49443
50516
  } catch {}
49444
- let loadChecked = null;
50517
+ let jevAssignments = null;
49445
50518
  try {
49446
- const record = readCloudLoadChecked();
49447
- loadChecked = new Set(record && record.deployment === active[0]?.deployment ? record.checked : []);
49448
- } catch {
49449
- loadChecked = new Set;
49450
- }
49451
- const labels = active.some((a) => a.agentTargets?.some((tg) => tg.instanceId)) ? readRosterLabels() : new Map;
50519
+ jevAssignments = readCloudJevAssignments();
50520
+ } catch {}
50521
+ const jsIds = new Set(active.map((a) => a.id));
50522
+ const jevOnly = (jevAssignments ?? []).filter((a) => !jsIds.has(a.id));
50523
+ const labels = [...active, ...jevOnly].some((a) => a.agentTargets?.some((tg) => tg.instanceId)) ? readRosterLabels() : new Map;
49452
50524
  const targetText = (targets) => `for ${targets.map((tg) => tg.instanceId ? `${tg.integration} / ${labels.get(tg.instanceId) ?? tg.instanceId}` : tg.integration).join(", ")}`;
49453
- if (active.length > 0 || jev.jevMode !== null) {
50525
+ if (active.length > 0 || jevOnly.length > 0 || jev.jevMode !== null) {
50526
+ const jevRows = jevOnly.map((a) => {
50527
+ const tags = [
50528
+ ...a.effect === "observe" ? ["observe"] : [],
50529
+ "jev",
50530
+ ...a.agentTargets?.length ? [targetText(a.agentTargets)] : []
50531
+ ];
50532
+ return { state: "on", name: a.id, description: `v${a.version}`, tag: tags.join(" · ") };
50533
+ });
49454
50534
  cloud = {
49455
50535
  deployment: active[0]?.deployment ?? jev.deployment ?? null,
49456
- rows: active.map((artifact) => {
50536
+ jevListed: jevAssignments !== null,
50537
+ rows: [...active.map((artifact) => {
49457
50538
  const both = artifact.authority === "reviewable" && (artifact.reviewedBy?.length ?? 0) > 0;
49458
50539
  const checks = both ? (artifact.reviewedBy ?? []).join(", ") : "";
49459
50540
  const tags = [
@@ -49471,21 +50552,13 @@ async function listHooks(cwd, { all = false } = {}) {
49471
50552
  tag: [...tags, `not loaded: ${failed}`].join(" · ")
49472
50553
  };
49473
50554
  }
49474
- if (!loadChecked?.has(`${artifact.id}@${artifact.version}`)) {
49475
- return {
49476
- state: "off",
49477
- name: artifact.id,
49478
- description: `v${artifact.version}`,
49479
- tag: [...tags, "not loaded yet: it loads at the next agent tool call"].join(" · ")
49480
- };
49481
- }
49482
50555
  return {
49483
50556
  state: "on",
49484
50557
  name: artifact.id,
49485
50558
  description: `v${artifact.version}`,
49486
50559
  ...tags.length > 0 ? { tag: tags.join(" · ") } : {}
49487
50560
  };
49488
- }),
50561
+ }), ...jevRows],
49489
50562
  jevMode: jev.jevMode,
49490
50563
  problems: [
49491
50564
  ...jsProblem !== null ? [`The Cloud JavaScript policies could not be read, so none of them is enforcing: ${jsProblem}`] : [],
@@ -49558,11 +50631,11 @@ var init_manager2 = __esm(() => {
49558
50631
  });
49559
50632
 
49560
50633
  // src/audit/audit-schedule.ts
49561
- import { readFileSync as readFileSync46 } from "node:fs";
50634
+ import { readFileSync as readFileSync47 } from "node:fs";
49562
50635
  function readAuditSchedule(home) {
49563
50636
  let raw;
49564
50637
  try {
49565
- raw = readFileSync46(auditScheduleFile(home), "utf8");
50638
+ raw = readFileSync47(auditScheduleFile(home), "utf8");
49566
50639
  } catch {
49567
50640
  return null;
49568
50641
  }
@@ -49914,14 +50987,14 @@ var init_schedule_cli = __esm(() => {
49914
50987
  });
49915
50988
 
49916
50989
  // scripts/parse-script-args.ts
49917
- import { resolve as resolve41 } from "path";
50990
+ import { resolve as resolve42 } from "path";
49918
50991
  function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
49919
50992
  const raw = inlineValue ?? args[index + 1];
49920
50993
  if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
49921
50994
  console.error(`Error: ${flagName} requires ${errorLabel}`);
49922
50995
  process.exit(1);
49923
50996
  }
49924
- const value = options?.resolve ? resolve41(raw) : raw;
50997
+ const value = options?.resolve ? resolve42(raw) : raw;
49925
50998
  return { value, spliceCount: inlineValue !== null ? 1 : 2 };
49926
50999
  }
49927
51000
  function parseScriptArgs(argv) {
@@ -49977,21 +51050,21 @@ function parseScriptArgs(argv) {
49977
51050
  var init_parse_script_args = () => {};
49978
51051
 
49979
51052
  // scripts/install-diagnosis.mjs
49980
- import { existsSync as existsSync59, readFileSync as readFileSync47, realpathSync as realpathSync6 } from "node:fs";
49981
- import { dirname as dirname31, resolve as resolve42 } from "node:path";
49982
- import { homedir as homedir39, platform as platform5 } from "node:os";
51053
+ import { existsSync as existsSync60, readFileSync as readFileSync48, realpathSync as realpathSync6 } from "node:fs";
51054
+ import { dirname as dirname31, resolve as resolve43 } from "node:path";
51055
+ import { homedir as homedir40, platform as platform5 } from "node:os";
49983
51056
  import { spawnSync } from "node:child_process";
49984
51057
  function findPackageRoot(start) {
49985
51058
  try {
49986
51059
  let dir = realpathSync6(start);
49987
- if (existsSync59(dir) && !existsSync59(resolve42(dir, "package.json"))) {
51060
+ if (existsSync60(dir) && !existsSync60(resolve43(dir, "package.json"))) {
49988
51061
  dir = dirname31(dir);
49989
51062
  }
49990
51063
  while (true) {
49991
- const pkgPath = resolve42(dir, "package.json");
49992
- if (existsSync59(pkgPath)) {
51064
+ const pkgPath = resolve43(dir, "package.json");
51065
+ if (existsSync60(pkgPath)) {
49993
51066
  try {
49994
- const pkg = JSON.parse(readFileSync47(pkgPath, "utf8"));
51067
+ const pkg = JSON.parse(readFileSync48(pkgPath, "utf8"));
49995
51068
  if (pkg.name === PKG_NAME)
49996
51069
  return dir;
49997
51070
  } catch {}
@@ -50009,7 +51082,7 @@ function readPackageVersion(packageRoot) {
50009
51082
  if (!packageRoot)
50010
51083
  return null;
50011
51084
  try {
50012
- const pkg = JSON.parse(readFileSync47(resolve42(packageRoot, "package.json"), "utf8"));
51085
+ const pkg = JSON.parse(readFileSync48(resolve43(packageRoot, "package.json"), "utf8"));
50013
51086
  return typeof pkg.version === "string" ? pkg.version : null;
50014
51087
  } catch {
50015
51088
  return null;
@@ -50035,16 +51108,16 @@ function locateNpmGlobal() {
50035
51108
  const root = (res.stdout || "").trim();
50036
51109
  if (!root)
50037
51110
  return null;
50038
- const candidate = resolve42(root, PKG_NAME);
50039
- return existsSync59(resolve42(candidate, "package.json")) ? candidate : null;
51111
+ const candidate = resolve43(root, PKG_NAME);
51112
+ return existsSync60(resolve43(candidate, "package.json")) ? candidate : null;
50040
51113
  } catch {
50041
51114
  return null;
50042
51115
  }
50043
51116
  }
50044
51117
  function locateBunGlobal() {
50045
51118
  try {
50046
- const candidate = resolve42(homedir39(), ".bun", "install", "global", "node_modules", PKG_NAME);
50047
- return existsSync59(resolve42(candidate, "package.json")) ? candidate : null;
51119
+ const candidate = resolve43(homedir40(), ".bun", "install", "global", "node_modules", PKG_NAME);
51120
+ return existsSync60(resolve43(candidate, "package.json")) ? candidate : null;
50048
51121
  } catch {
50049
51122
  return null;
50050
51123
  }
@@ -50052,8 +51125,8 @@ function locateBunGlobal() {
50052
51125
  function buildRecommendation(pathFirstBin) {
50053
51126
  if (!pathFirstBin)
50054
51127
  return null;
50055
- const bunBinPrefix = resolve42(homedir39(), ".bun", "bin") + "/";
50056
- const bunGlobalPrefix = resolve42(homedir39(), ".bun", "install", "global") + "/";
51128
+ const bunBinPrefix = resolve43(homedir40(), ".bun", "bin") + "/";
51129
+ const bunGlobalPrefix = resolve43(homedir40(), ".bun", "install", "global") + "/";
50057
51130
  const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
50058
51131
  if (isBun) {
50059
51132
  return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
@@ -50526,8 +51599,8 @@ var init_launch_ready = () => {};
50526
51599
 
50527
51600
  // scripts/launch.ts
50528
51601
  import { spawn as spawn2 } from "child_process";
50529
- import { realpathSync as realpathSync7, existsSync as existsSync60 } from "node:fs";
50530
- import { resolve as resolve43, dirname as dirname32 } from "node:path";
51602
+ import { realpathSync as realpathSync7, existsSync as existsSync61 } from "node:fs";
51603
+ import { resolve as resolve44, dirname as dirname32 } from "node:path";
50531
51604
  import { fileURLToPath as fileURLToPath2 } from "node:url";
50532
51605
  function launch(mode, options = {}) {
50533
51606
  const { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs } = parseScriptArgs(process.argv.slice(2));
@@ -50563,9 +51636,9 @@ ${k.caution(exposedBindWarning(exposed.host))}
50563
51636
  process.env.PORT = port;
50564
51637
  process.env.HOSTNAME = bindHost;
50565
51638
  cmd = "node";
50566
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve43(dirname32(realpathSync7(fileURLToPath2(import.meta.url))), "..");
50567
- const serverJsPath = resolve43(packageRoot, ".next/standalone/server.js");
50568
- if (!existsSync60(serverJsPath)) {
51639
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve44(dirname32(realpathSync7(fileURLToPath2(import.meta.url))), "..");
51640
+ const serverJsPath = resolve44(packageRoot, ".next/standalone/server.js");
51641
+ if (!existsSync61(serverJsPath)) {
50569
51642
  let shadowMessage = null;
50570
51643
  try {
50571
51644
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -51272,12 +52345,12 @@ var init_session_pause_cli = __esm(() => {
51272
52345
  });
51273
52346
 
51274
52347
  // src/hooks/config-status.ts
51275
- import { readFileSync as readFileSync48 } from "node:fs";
51276
- import { resolve as resolve44 } from "node:path";
52348
+ import { readFileSync as readFileSync49 } from "node:fs";
52349
+ import { resolve as resolve45 } from "node:path";
51277
52350
  function readCollectorHealth(file = collectorHealthFile()) {
51278
52351
  let parsed;
51279
52352
  try {
51280
- parsed = JSON.parse(readFileSync48(file, "utf8"));
52353
+ parsed = JSON.parse(readFileSync49(file, "utf8"));
51281
52354
  } catch {
51282
52355
  return null;
51283
52356
  }
@@ -51347,14 +52420,14 @@ function summarizePolicySources(cwd) {
51347
52420
  summary.morePacks = Math.max(0, enforcing - 1);
51348
52421
  } catch {}
51349
52422
  try {
51350
- for (const artifact of readActiveCloudManagedPolicies()) {
52423
+ for (const artifact of listActiveCloudManagedPolicies()) {
51351
52424
  if (artifact.effect === "observe")
51352
52425
  summary.observing++;
51353
52426
  else
51354
52427
  summary.cloudOn++;
51355
52428
  }
51356
52429
  } catch {}
51357
- const projectDir = resolve44(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
52430
+ const projectDir = resolve45(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies");
51358
52431
  const conventionFiles = new Set([...discoverPolicyFiles(projectDir), ...discoverPolicyFiles(customPoliciesDir())]);
51359
52432
  summary.customFiles = configuredCustomPolicyPaths(config).length + conventionFiles.size;
51360
52433
  if (config.enabledPolicies.length > 0 && !hasInstalledRegexPacks())
@@ -51555,7 +52628,8 @@ function renderStatusScreen(facts, opts = {}) {
51555
52628
  }
51556
52629
  if (!health.delivering)
51557
52630
  return `${kit.off} Not delivering, failproofaid has no key`;
51558
- const backlog = queued === 0 ? "nothing queued" : `${many(queued, "batch", "batches")} queued`;
52631
+ const pending = queued + (refused.resending ?? 0);
52632
+ const backlog = pending === 0 ? "nothing queued" : `${many(pending, "batch", "batches")} queued`;
51559
52633
  const stuck = queued > 0 && (oldestQueuedMs ?? 0) > STATUS_QUEUE_STUCK_MS && (health.lastOkAt === undefined || now - health.lastOkAt > STATUS_QUEUE_STUCK_MS);
51560
52634
  if (stuck) {
51561
52635
  const last = health.lastOkAt !== undefined ? `last delivered ${ago(health.lastOkAt)}` : "nothing delivered since failproofaid started";
@@ -51790,8 +52864,8 @@ var init_session_pause_cli2 = __esm(() => {
51790
52864
 
51791
52865
  // scripts/launch.ts
51792
52866
  import { spawn as spawn3 } from "child_process";
51793
- import { realpathSync as realpathSync8, existsSync as existsSync61 } from "node:fs";
51794
- import { resolve as resolve45, dirname as dirname33 } from "node:path";
52867
+ import { realpathSync as realpathSync8, existsSync as existsSync62 } from "node:fs";
52868
+ import { resolve as resolve46, dirname as dirname33 } from "node:path";
51795
52869
  import { fileURLToPath as fileURLToPath3 } from "node:url";
51796
52870
  function launch2(mode, options = {}) {
51797
52871
  const { loggingLevel, disableTelemetry, allowedDevOrigins, host, remainingArgs } = parseScriptArgs(process.argv.slice(2));
@@ -51827,9 +52901,9 @@ ${k.caution(exposedBindWarning(exposed.host))}
51827
52901
  process.env.PORT = port;
51828
52902
  process.env.HOSTNAME = bindHost;
51829
52903
  cmd = "node";
51830
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve45(dirname33(realpathSync8(fileURLToPath3(import.meta.url))), "..");
51831
- const serverJsPath = resolve45(packageRoot, ".next/standalone/server.js");
51832
- if (!existsSync61(serverJsPath)) {
52904
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve46(dirname33(realpathSync8(fileURLToPath3(import.meta.url))), "..");
52905
+ const serverJsPath = resolve46(packageRoot, ".next/standalone/server.js");
52906
+ if (!existsSync62(serverJsPath)) {
51833
52907
  let shadowMessage = null;
51834
52908
  try {
51835
52909
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -51949,17 +53023,17 @@ var init_cli_error2 = __esm(() => {
51949
53023
 
51950
53024
  // bin/failproofai.mjs
51951
53025
  import { realpathSync as realpathSync9 } from "fs";
51952
- import { dirname as dirname34, resolve as resolve46 } from "path";
53026
+ import { dirname as dirname34, resolve as resolve47 } from "path";
51953
53027
  import { fileURLToPath as fileURLToPath4 } from "url";
51954
53028
  // package.json
51955
- var version = "1.0.12-beta.1";
53029
+ var version = "1.0.12-beta.2";
51956
53030
 
51957
53031
  // bin/failproofai.mjs
51958
53032
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
51959
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve46(dirname34(realpathSync9(fileURLToPath4(import.meta.url))), "..");
53033
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve47(dirname34(realpathSync9(fileURLToPath4(import.meta.url))), "..");
51960
53034
  }
51961
53035
  if (!process.env.FAILPROOFAI_DIST_PATH) {
51962
- process.env.FAILPROOFAI_DIST_PATH = resolve46(dirname34(realpathSync9(fileURLToPath4(import.meta.url))), "..", "dist");
53036
+ process.env.FAILPROOFAI_DIST_PATH = resolve47(dirname34(realpathSync9(fileURLToPath4(import.meta.url))), "..", "dist");
51963
53037
  }
51964
53038
  var args = process.argv.slice(2);
51965
53039
  if (args[0] === "p" || args[0] === "policy")