failproofai 1.0.6-beta.1 → 1.0.6

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 (113) 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 +4 -4
  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 +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  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 +14 -14
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  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 +14 -14
  34. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  35. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  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.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  44. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  47. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  48. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  51. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  54. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  57. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
  58. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
  59. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  60. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +2 -2
  61. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  62. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0576iuy._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1b2dbet._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  70. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1x3j9mj._.js → [root-of-the-server]__1svlq8e._.js} +2 -2
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/{_1_ddpm3._.js → _0do6d26._.js} +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/{_0qlygw6._.js → _17o744m._.js} +2 -2
  76. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  77. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +4 -4
  79. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  83. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  84. package/.next/standalone/.next/server/pages/404.html +1 -1
  85. package/.next/standalone/.next/server/pages/500.html +1 -1
  86. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  87. package/.next/standalone/.next/server/server-reference-manifest.json +19 -19
  88. package/.next/standalone/.next/static/chunks/0bepqeawy72n6.js +1 -0
  89. package/.next/standalone/.next/static/chunks/{0cbvytf8epn34.js → 0h-gz8e00478q.js} +1 -1
  90. package/.next/standalone/.next/static/chunks/{06fjx8-0_qufs.js → 25m8mg7bosgip.js} +1 -1
  91. package/.next/standalone/.next/static/chunks/{27_bwbg8sv6vj.js → 2y4ycwcwfjv4k.js} +1 -1
  92. package/.next/standalone/.next/static/chunks/3-6zhytp6adkk.js +1 -0
  93. package/.next/standalone/.next/static/chunks/{1p4xauf-zu6zk.js → 319c_91le7-h_.js} +1 -1
  94. package/.next/standalone/.next/static/chunks/35m53kix7uaii.js +6 -0
  95. package/.next/standalone/.next/static/chunks/{1qf2uiipatr80.js → 35pw7zxhh76n7.js} +1 -1
  96. package/.next/standalone/.next/static/chunks/3xacd7qmuqlvs.js +1 -0
  97. package/.next/standalone/.next/static/chunks/{02vwr5js1x28j.js → 40-wncjnguy4e.js} +1 -1
  98. package/.next/standalone/package.json +9 -9
  99. package/.next/standalone/server.js +1 -1
  100. package/dist/cli.mjs +123 -62
  101. package/dist/worker.mjs +1 -1
  102. package/package.json +9 -9
  103. package/src/hooks/daemon-client.ts +128 -50
  104. package/src/hooks/daemon-service.ts +30 -0
  105. package/src/hooks/integrations.ts +3 -2
  106. package/src/hooks/manager.ts +4 -4
  107. package/.next/standalone/.next/static/chunks/0y227_ohdqwvn.js +0 -1
  108. package/.next/standalone/.next/static/chunks/1uc_s_dftgxiq.js +0 -6
  109. package/.next/standalone/.next/static/chunks/2x776s0x3n_tu.js +0 -1
  110. package/.next/standalone/.next/static/chunks/3ht0xsco46pul.js +0 -1
  111. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → LzR_etPlaZV9JZH09lEp3}/_buildManifest.js +0 -0
  112. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → LzR_etPlaZV9JZH09lEp3}/_clientMiddlewareManifest.js +0 -0
  113. /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → LzR_etPlaZV9JZH09lEp3}/_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.6-beta.1", package_default;
32
+ var version2 = "1.0.6", package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "failproofai",
@@ -155,16 +155,16 @@ var init_package = __esm(() => {
155
155
  browserslist: "4.28.8"
156
156
  },
157
157
  optionalDependencies: {
158
- "@failproofai/failproofaid-linux-x64": "1.0.6-beta.1",
159
- "@failproofai/failproofaid-linux-arm64": "1.0.6-beta.1",
160
- "@failproofai/failproofaid-darwin-x64": "1.0.6-beta.1",
161
- "@failproofai/failproofaid-darwin-arm64": "1.0.6-beta.1"
158
+ "@failproofai/failproofaid-linux-x64": "1.0.6",
159
+ "@failproofai/failproofaid-linux-arm64": "1.0.6",
160
+ "@failproofai/failproofaid-darwin-x64": "1.0.6",
161
+ "@failproofai/failproofaid-darwin-arm64": "1.0.6"
162
162
  },
163
163
  failproofaidBinaries: {
164
- "linux-x64": "b6f877cd407c3322ff8ad2f98a096b6ea15b0bd1d63de6276c689368345c1cd2",
165
- "linux-arm64": "0da90a816b9dc707efe9b82176923cbf0f00026179c816e34eaad362ce2c0fa4",
166
- "darwin-x64": "bcbec488353fa04a1953a87cad15054e4796117cac4fd60b17fb1c87d62b7b1e",
167
- "darwin-arm64": "6830989a91a27642cdcc63fcfad529c69c0deb3f3abcf5ad40fd3516289b451d"
164
+ "linux-x64": "e075f24a3a0950b631979d9370b665da2c3e2920decfb917ff0831d1f17c9740",
165
+ "linux-arm64": "b90ce8c642f2685719f4915695615219860daf3155a9c07344cf1e10395028c3",
166
+ "darwin-x64": "51dd95e9b85226e4f1888c7edaa56ec4464e91eb645d39c5cb7cede16d2da2f1",
167
+ "darwin-arm64": "f6a1b9d63363e8805e0d72b4fa168fde661bdbbd4125b54047f7de8948ea0f39"
168
168
  }
169
169
  };
170
170
  });
@@ -809,7 +809,7 @@ function encodeFrame(value) {
809
809
  header.writeUInt32BE(body.length, 0);
810
810
  return Buffer.concat([header, body]);
811
811
  }
812
- async function attemptDaemonHook(req, opts) {
812
+ async function attemptDaemonRequest(request, opts) {
813
813
  if (process.platform === "win32")
814
814
  return { ok: false, failure: "unreachable" };
815
815
  const responseBudget = opts?.responseTimeoutMs ?? DAEMON_RESPONSE_TIMEOUT_MS;
@@ -837,14 +837,7 @@ async function attemptDaemonHook(req, opts) {
837
837
  let declaredLen = null;
838
838
  socket.on("connect", () => {
839
839
  arm(responseBudget);
840
- socket.write(encodeFrame({
841
- type: "hook",
842
- protocolVersion: PROTOCOL_VERSION,
843
- hookEvent: req.hookEvent,
844
- cli: req.cli,
845
- stdin: req.stdin,
846
- cwd: req.cwd
847
- }));
840
+ socket.write(encodeFrame(request));
848
841
  });
849
842
  socket.on("data", (chunk) => {
850
843
  recvBuf = Buffer.concat([recvBuf, chunk]);
@@ -861,34 +854,52 @@ async function attemptDaemonHook(req, opts) {
861
854
  if (recvBuf.length < declaredLen)
862
855
  return;
863
856
  const body = recvBuf.subarray(0, declaredLen);
864
- let message;
857
+ let parsed;
865
858
  try {
866
- message = JSON.parse(body.toString("utf8"));
859
+ parsed = JSON.parse(body.toString("utf8"));
867
860
  } catch {
868
861
  fail("unreachable");
869
862
  return;
870
863
  }
871
- if (message.protocolVersion !== PROTOCOL_VERSION) {
872
- fail("protocol-mismatch");
864
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
865
+ fail("unreachable");
873
866
  return;
874
867
  }
875
- if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
876
- finish({
877
- ok: true,
878
- response: {
879
- exitCode: message.exitCode,
880
- stdout: message.stdout,
881
- stderr: message.stderr
882
- }
883
- });
868
+ const message = parsed;
869
+ if (message.protocolVersion !== PROTOCOL_VERSION) {
870
+ fail("protocol-mismatch");
884
871
  return;
885
872
  }
886
- fail("unreachable");
873
+ finish({ ok: true, message });
887
874
  });
888
875
  socket.on("error", () => fail("unreachable"));
889
876
  socket.on("close", () => fail("unreachable"));
890
877
  });
891
878
  }
879
+ async function attemptDaemonHook(req, opts) {
880
+ const attempt = await attemptDaemonRequest({
881
+ type: "hook",
882
+ protocolVersion: PROTOCOL_VERSION,
883
+ hookEvent: req.hookEvent,
884
+ cli: req.cli,
885
+ stdin: req.stdin,
886
+ cwd: req.cwd
887
+ }, opts);
888
+ if (!attempt.ok)
889
+ return attempt;
890
+ const message = attempt.message;
891
+ if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
892
+ return {
893
+ ok: true,
894
+ response: {
895
+ exitCode: message.exitCode,
896
+ stdout: message.stdout,
897
+ stderr: message.stderr
898
+ }
899
+ };
900
+ }
901
+ return { ok: false, failure: "unreachable" };
902
+ }
892
903
  var PROTOCOL_VERSION = 1, DAEMON_CONNECT_TIMEOUT_MS = 150, DAEMON_RESPONSE_TIMEOUT_MS = 30000, MAX_FRAME_LEN;
893
904
  var init_daemon_client = __esm(() => {
894
905
  init_fp_home();
@@ -21996,7 +22007,7 @@ function encodeFrame2(value) {
21996
22007
  header.writeUInt32BE(body.length, 0);
21997
22008
  return Buffer.concat([header, body]);
21998
22009
  }
21999
- async function attemptDaemonHook2(req, opts) {
22010
+ async function attemptDaemonRequest2(request, opts) {
22000
22011
  if (process.platform === "win32")
22001
22012
  return { ok: false, failure: "unreachable" };
22002
22013
  const responseBudget = opts?.responseTimeoutMs ?? DAEMON_RESPONSE_TIMEOUT_MS2;
@@ -22024,14 +22035,7 @@ async function attemptDaemonHook2(req, opts) {
22024
22035
  let declaredLen = null;
22025
22036
  socket.on("connect", () => {
22026
22037
  arm(responseBudget);
22027
- socket.write(encodeFrame2({
22028
- type: "hook",
22029
- protocolVersion: PROTOCOL_VERSION2,
22030
- hookEvent: req.hookEvent,
22031
- cli: req.cli,
22032
- stdin: req.stdin,
22033
- cwd: req.cwd
22034
- }));
22038
+ socket.write(encodeFrame2(request));
22035
22039
  });
22036
22040
  socket.on("data", (chunk) => {
22037
22041
  recvBuf = Buffer.concat([recvBuf, chunk]);
@@ -22048,34 +22052,73 @@ async function attemptDaemonHook2(req, opts) {
22048
22052
  if (recvBuf.length < declaredLen)
22049
22053
  return;
22050
22054
  const body = recvBuf.subarray(0, declaredLen);
22051
- let message;
22055
+ let parsed;
22052
22056
  try {
22053
- message = JSON.parse(body.toString("utf8"));
22057
+ parsed = JSON.parse(body.toString("utf8"));
22054
22058
  } catch {
22055
22059
  fail("unreachable");
22056
22060
  return;
22057
22061
  }
22058
- if (message.protocolVersion !== PROTOCOL_VERSION2) {
22059
- fail("protocol-mismatch");
22062
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
22063
+ fail("unreachable");
22060
22064
  return;
22061
22065
  }
22062
- if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
22063
- finish({
22064
- ok: true,
22065
- response: {
22066
- exitCode: message.exitCode,
22067
- stdout: message.stdout,
22068
- stderr: message.stderr
22069
- }
22070
- });
22066
+ const message = parsed;
22067
+ if (message.protocolVersion !== PROTOCOL_VERSION2) {
22068
+ fail("protocol-mismatch");
22071
22069
  return;
22072
22070
  }
22073
- fail("unreachable");
22071
+ finish({ ok: true, message });
22074
22072
  });
22075
22073
  socket.on("error", () => fail("unreachable"));
22076
22074
  socket.on("close", () => fail("unreachable"));
22077
22075
  });
22078
22076
  }
22077
+ async function attemptDaemonHook2(req, opts) {
22078
+ const attempt = await attemptDaemonRequest2({
22079
+ type: "hook",
22080
+ protocolVersion: PROTOCOL_VERSION2,
22081
+ hookEvent: req.hookEvent,
22082
+ cli: req.cli,
22083
+ stdin: req.stdin,
22084
+ cwd: req.cwd
22085
+ }, opts);
22086
+ if (!attempt.ok)
22087
+ return attempt;
22088
+ const message = attempt.message;
22089
+ if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
22090
+ return {
22091
+ ok: true,
22092
+ response: {
22093
+ exitCode: message.exitCode,
22094
+ stdout: message.stdout,
22095
+ stderr: message.stderr
22096
+ }
22097
+ };
22098
+ }
22099
+ return { ok: false, failure: "unreachable" };
22100
+ }
22101
+ function isStringArray(value) {
22102
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
22103
+ }
22104
+ async function attemptDaemonPolicyEvaluation(req, opts) {
22105
+ const attempt = await attemptDaemonRequest2({
22106
+ type: "policyEvaluation",
22107
+ protocolVersion: PROTOCOL_VERSION2,
22108
+ integration: req.integration,
22109
+ event: req.event,
22110
+ payload: req.payload,
22111
+ cwd: req.cwd
22112
+ }, opts);
22113
+ if (!attempt.ok)
22114
+ return attempt;
22115
+ const message = attempt.message;
22116
+ const validOptionalString = (value) => value === null || value === undefined || typeof value === "string";
22117
+ if (message.type === "policyResult" && (message.decision === "allow" || message.decision === "deny" || message.decision === "instruct") && isStringArray(message.policyNames) && validOptionalString(message.reason) && isStringArray(message.matchedPolicies) && typeof message.durationMs === "number" && Number.isInteger(message.durationMs) && message.durationMs >= 0 && validOptionalString(message.toolName)) {
22118
+ return { ok: true };
22119
+ }
22120
+ return { ok: false, failure: "unreachable" };
22121
+ }
22079
22122
  var PROTOCOL_VERSION2 = 1, DAEMON_CONNECT_TIMEOUT_MS2 = 150, DAEMON_RESPONSE_TIMEOUT_MS2 = 30000, MAX_FRAME_LEN2;
22080
22123
  var init_daemon_client2 = __esm(() => {
22081
22124
  init_fp_home();
@@ -22488,6 +22531,24 @@ async function daemonAcceptsConnectionsQuietly() {
22488
22531
  async function probeDaemonEndToEnd() {
22489
22532
  return (await probeDaemon()).ok;
22490
22533
  }
22534
+ async function probeDaemonPolicyEvaluation() {
22535
+ if (!await probeDaemonEndToEnd())
22536
+ return false;
22537
+ try {
22538
+ await Promise.resolve().then(() => init_daemon_client2());
22539
+ const attempt = await attemptDaemonPolicyEvaluation({
22540
+ integration: "hermes",
22541
+ event: "on_session_start",
22542
+ payload: {
22543
+ hook_event_name: "on_session_start",
22544
+ source: "failproofai-native-policy-health-probe"
22545
+ }
22546
+ }, { responseTimeoutMs: DAEMON_PROBE_TIMEOUT_MS });
22547
+ return attempt.ok;
22548
+ } catch {
22549
+ return false;
22550
+ }
22551
+ }
22491
22552
  async function waitForDaemonRunning(deps = {}) {
22492
22553
  const status = deps.status ?? daemonServiceStatus;
22493
22554
  const accepts = deps.accepts ?? daemonAcceptsConnectionsQuietly;
@@ -25156,11 +25217,11 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
25156
25217
  }
25157
25218
  }
25158
25219
  const daemonRequiredBy = selectedIntegrations.map(({ integration }) => integration).filter((integration) => integration.requiresHealthyDaemon);
25159
- if (daemonRequiredBy.length > 0 && !await probeDaemonEndToEnd()) {
25220
+ if (daemonRequiredBy.length > 0 && !await probeDaemonPolicyEvaluation()) {
25160
25221
  const names = daemonRequiredBy.map((integration) => integration.displayName).join(", ");
25161
25222
  const verb = daemonRequiredBy.length === 1 ? "requires" : "require";
25162
- throw new CliError(`${names} ${verb} a healthy failproofaid daemon before FailproofAI enforcement can be enabled.
25163
- ` + "Run `failproofai config` to install and configure the daemon, then retry.");
25223
+ throw new CliError(`${names} ${verb} a compatible failproofaid daemon with native policy evaluation before FailproofAI enforcement can be enabled.
25224
+ ` + "Run `failproofai config` to install or update the daemon, then retry.");
25164
25225
  }
25165
25226
  const binaryPath = resolveFailproofaiBinary();
25166
25227
  const previousConfig = readScopedHooksConfig(scope, cwd);
@@ -33148,11 +33209,11 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
33148
33209
  }
33149
33210
  }
33150
33211
  const daemonRequiredBy = selectedIntegrations.map(({ integration }) => integration).filter((integration) => integration.requiresHealthyDaemon);
33151
- if (daemonRequiredBy.length > 0 && !await probeDaemonEndToEnd()) {
33212
+ if (daemonRequiredBy.length > 0 && !await probeDaemonPolicyEvaluation()) {
33152
33213
  const names = daemonRequiredBy.map((integration) => integration.displayName).join(", ");
33153
33214
  const verb = daemonRequiredBy.length === 1 ? "requires" : "require";
33154
- throw new CliError(`${names} ${verb} a healthy failproofaid daemon before FailproofAI enforcement can be enabled.
33155
- ` + "Run `failproofai config` to install and configure the daemon, then retry.");
33215
+ throw new CliError(`${names} ${verb} a compatible failproofaid daemon with native policy evaluation before FailproofAI enforcement can be enabled.
33216
+ ` + "Run `failproofai config` to install or update the daemon, then retry.");
33156
33217
  }
33157
33218
  const binaryPath = resolveFailproofaiBinary2();
33158
33219
  const previousConfig = readScopedHooksConfig(scope, cwd);
@@ -35290,7 +35351,7 @@ import { realpathSync as realpathSync7 } from "fs";
35290
35351
  import { dirname as dirname26, resolve as resolve36 } from "path";
35291
35352
  import { fileURLToPath as fileURLToPath4 } from "url";
35292
35353
  // package.json
35293
- var version = "1.0.6-beta.1";
35354
+ var version = "1.0.6";
35294
35355
 
35295
35356
  // bin/failproofai.mjs
35296
35357
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
package/dist/worker.mjs CHANGED
@@ -3483,7 +3483,7 @@ function parsePackPolicyName(canonicalName) {
3483
3483
  }
3484
3484
 
3485
3485
  // package.json
3486
- var version = "1.0.6-beta.1";
3486
+ var version = "1.0.6";
3487
3487
  var init_package = () => {};
3488
3488
 
3489
3489
  // src/posthog-key.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.6-beta.1",
3
+ "version": "1.0.6",
4
4
  "description": "Observability and enforcement for AI agent harnesses. 39 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
5
5
  "bin": {
6
6
  "failproofai": "./dist/cli.mjs",
@@ -122,15 +122,15 @@
122
122
  "browserslist": "4.28.8"
123
123
  },
124
124
  "optionalDependencies": {
125
- "@failproofai/failproofaid-linux-x64": "1.0.6-beta.1",
126
- "@failproofai/failproofaid-linux-arm64": "1.0.6-beta.1",
127
- "@failproofai/failproofaid-darwin-x64": "1.0.6-beta.1",
128
- "@failproofai/failproofaid-darwin-arm64": "1.0.6-beta.1"
125
+ "@failproofai/failproofaid-linux-x64": "1.0.6",
126
+ "@failproofai/failproofaid-linux-arm64": "1.0.6",
127
+ "@failproofai/failproofaid-darwin-x64": "1.0.6",
128
+ "@failproofai/failproofaid-darwin-arm64": "1.0.6"
129
129
  },
130
130
  "failproofaidBinaries": {
131
- "linux-x64": "b6f877cd407c3322ff8ad2f98a096b6ea15b0bd1d63de6276c689368345c1cd2",
132
- "linux-arm64": "0da90a816b9dc707efe9b82176923cbf0f00026179c816e34eaad362ce2c0fa4",
133
- "darwin-x64": "bcbec488353fa04a1953a87cad15054e4796117cac4fd60b17fb1c87d62b7b1e",
134
- "darwin-arm64": "6830989a91a27642cdcc63fcfad529c69c0deb3f3abcf5ad40fd3516289b451d"
131
+ "linux-x64": "e075f24a3a0950b631979d9370b665da2c3e2920decfb917ff0831d1f17c9740",
132
+ "linux-arm64": "b90ce8c642f2685719f4915695615219860daf3155a9c07344cf1e10395028c3",
133
+ "darwin-x64": "51dd95e9b85226e4f1888c7edaa56ec4464e91eb645d39c5cb7cede16d2da2f1",
134
+ "darwin-arm64": "f6a1b9d63363e8805e0d72b4fa168fde661bdbbd4125b54047f7de8948ea0f39"
135
135
  }
136
136
  }
@@ -93,6 +93,26 @@ export type DaemonAttempt =
93
93
  | { ok: true; response: DaemonHookResponse }
94
94
  | { ok: false; failure: DaemonFailure };
95
95
 
96
+ export interface DaemonPolicyEvaluationRequest {
97
+ integration: IntegrationType;
98
+ event: string;
99
+ payload: Record<string, unknown>;
100
+ cwd?: string;
101
+ }
102
+
103
+ export type DaemonPolicyEvaluationAttempt =
104
+ | { ok: true }
105
+ | { ok: false; failure: DaemonFailure };
106
+
107
+ interface DaemonRequestOptions {
108
+ /** Override the response budget; the connect budget is never relaxed. */
109
+ responseTimeoutMs?: number;
110
+ }
111
+
112
+ type DaemonWireAttempt =
113
+ | { ok: true; message: Record<string, unknown> }
114
+ | { ok: false; failure: DaemonFailure };
115
+
96
116
  /**
97
117
  * Whether a daemon is listening, regardless of how it was started.
98
118
  *
@@ -176,29 +196,22 @@ function encodeFrame(value: unknown): Buffer {
176
196
  return Buffer.concat([header, body]);
177
197
  }
178
198
 
179
- /** Attempts a daemon evaluation while preserving the failure category. */
180
- export async function attemptDaemonHook(
181
- req: DaemonHookRequest,
182
- opts?: {
183
- /**
184
- * Override the RESPONSE budget only — the connect budget is never
185
- * relaxed. Used by the health probe (`probeDaemonEndToEnd`), which runs
186
- * from an interactive command rather than a hook and must not make a
187
- * person wait out the full 30s hook budget to be told their daemon is
188
- * broken. Never set this on the hook path: 30s is matched to the
189
- * daemon's own read timeout so this side never gives up on a request the
190
- * daemon is still honestly working on.
191
- */
192
- responseTimeoutMs?: number;
193
- },
194
- ): Promise<DaemonAttempt> {
199
+ /**
200
+ * Sends one framed daemon request while preserving the failure category.
201
+ * Shape validation belongs to the typed caller: both shell hooks and native
202
+ * integrations share the transport, but require different response variants.
203
+ */
204
+ async function attemptDaemonRequest(
205
+ request: Record<string, unknown>,
206
+ opts?: DaemonRequestOptions,
207
+ ): Promise<DaemonWireAttempt> {
195
208
  // Windows never has a daemon in this phase (see the plan's platform
196
209
  // scope) — skip the attempt outright rather than depending on however
197
210
  // Node happens to behave when handed a POSIX socket path on Windows.
198
211
  if (process.platform === "win32") return { ok: false, failure: "unreachable" };
199
212
  const responseBudget = opts?.responseTimeoutMs ?? DAEMON_RESPONSE_TIMEOUT_MS;
200
213
 
201
- return new Promise<DaemonAttempt>((resolvePromise) => {
214
+ return new Promise<DaemonWireAttempt>((resolvePromise) => {
202
215
  let settled = false;
203
216
  let timer: ReturnType<typeof setTimeout>;
204
217
  const arm = (ms: number) => {
@@ -210,7 +223,7 @@ export async function attemptDaemonHook(
210
223
  // process lingers if something upstream forgets to await us.
211
224
  timer.unref?.();
212
225
  };
213
- const finish = (result: DaemonAttempt) => {
226
+ const finish = (result: DaemonWireAttempt) => {
214
227
  if (settled) return;
215
228
  settled = true;
216
229
  clearTimeout(timer);
@@ -230,16 +243,7 @@ export async function attemptDaemonHook(
230
243
  // Connected: the daemon is demonstrably reachable, so the question is
231
244
  // no longer "is it there" but "how long does this evaluation take".
232
245
  arm(responseBudget);
233
- socket.write(
234
- encodeFrame({
235
- type: "hook",
236
- protocolVersion: PROTOCOL_VERSION,
237
- hookEvent: req.hookEvent,
238
- cli: req.cli,
239
- stdin: req.stdin,
240
- cwd: req.cwd,
241
- }),
242
- );
246
+ socket.write(encodeFrame(request));
243
247
  });
244
248
 
245
249
  socket.on("data", (chunk: Buffer) => {
@@ -258,13 +262,18 @@ export async function attemptDaemonHook(
258
262
  if (recvBuf.length < declaredLen) return;
259
263
 
260
264
  const body = recvBuf.subarray(0, declaredLen);
261
- let message: Record<string, unknown>;
265
+ let parsed: unknown;
262
266
  try {
263
- message = JSON.parse(body.toString("utf8")) as Record<string, unknown>;
267
+ parsed = JSON.parse(body.toString("utf8"));
264
268
  } catch {
265
269
  fail("unreachable");
266
270
  return;
267
271
  }
272
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
273
+ fail("unreachable");
274
+ return;
275
+ }
276
+ const message = parsed as Record<string, unknown>;
268
277
 
269
278
  if (message.protocolVersion !== PROTOCOL_VERSION) {
270
279
  // Catches BOTH directions. A newer CLI sends v2 and the daemon replies
@@ -274,29 +283,98 @@ export async function attemptDaemonHook(
274
283
  fail("protocol-mismatch");
275
284
  return;
276
285
  }
277
- if (
278
- message.type === "hookResult" &&
279
- typeof message.exitCode === "number" &&
280
- typeof message.stdout === "string" &&
281
- typeof message.stderr === "string"
282
- ) {
283
- finish({
284
- ok: true,
285
- response: {
286
- exitCode: message.exitCode,
287
- stdout: message.stdout,
288
- stderr: message.stderr,
289
- },
290
- });
291
- return;
292
- }
293
- // Anything else — an explicit `error` message at a MATCHING protocol
294
- // version, a `pong` (protocol confusion), or a well-formed-but-wrong-shape
295
- // body — is treated identically to a connection failure: no partial trust.
296
- fail("unreachable");
286
+ finish({ ok: true, message });
297
287
  });
298
288
 
299
289
  socket.on("error", () => fail("unreachable"));
300
290
  socket.on("close", () => fail("unreachable"));
301
291
  });
302
292
  }
293
+
294
+ /** Attempts a shell-hook daemon evaluation while preserving the failure category. */
295
+ export async function attemptDaemonHook(
296
+ req: DaemonHookRequest,
297
+ opts?: DaemonRequestOptions,
298
+ ): Promise<DaemonAttempt> {
299
+ const attempt = await attemptDaemonRequest(
300
+ {
301
+ type: "hook",
302
+ protocolVersion: PROTOCOL_VERSION,
303
+ hookEvent: req.hookEvent,
304
+ cli: req.cli,
305
+ stdin: req.stdin,
306
+ cwd: req.cwd,
307
+ },
308
+ opts,
309
+ );
310
+ if (!attempt.ok) return attempt;
311
+
312
+ const message = attempt.message;
313
+ if (
314
+ message.type === "hookResult" &&
315
+ typeof message.exitCode === "number" &&
316
+ typeof message.stdout === "string" &&
317
+ typeof message.stderr === "string"
318
+ ) {
319
+ return {
320
+ ok: true,
321
+ response: {
322
+ exitCode: message.exitCode,
323
+ stdout: message.stdout,
324
+ stderr: message.stderr,
325
+ },
326
+ };
327
+ }
328
+
329
+ // An explicit matching-version error, pong, or malformed hook result is not
330
+ // an evaluation. Never grant partial trust to a daemon that answered the
331
+ // connection but not the request we sent.
332
+ return { ok: false, failure: "unreachable" };
333
+ }
334
+
335
+ function isStringArray(value: unknown): value is string[] {
336
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
337
+ }
338
+
339
+ /**
340
+ * Attempts the structured request used by native in-process integrations.
341
+ * A matching protocol version is not sufficient: pre-native v1 daemons speak
342
+ * `hook` but cannot deserialize `policyEvaluation`, so only a complete,
343
+ * well-shaped `policyResult` proves this capability exists.
344
+ */
345
+ export async function attemptDaemonPolicyEvaluation(
346
+ req: DaemonPolicyEvaluationRequest,
347
+ opts?: DaemonRequestOptions,
348
+ ): Promise<DaemonPolicyEvaluationAttempt> {
349
+ const attempt = await attemptDaemonRequest(
350
+ {
351
+ type: "policyEvaluation",
352
+ protocolVersion: PROTOCOL_VERSION,
353
+ integration: req.integration,
354
+ event: req.event,
355
+ payload: req.payload,
356
+ cwd: req.cwd,
357
+ },
358
+ opts,
359
+ );
360
+ if (!attempt.ok) return attempt;
361
+
362
+ const message = attempt.message;
363
+ const validOptionalString = (value: unknown) =>
364
+ value === null || value === undefined || typeof value === "string";
365
+ if (
366
+ message.type === "policyResult" &&
367
+ (message.decision === "allow" || message.decision === "deny" || message.decision === "instruct") &&
368
+ isStringArray(message.policyNames) &&
369
+ validOptionalString(message.reason) &&
370
+ isStringArray(message.matchedPolicies) &&
371
+ typeof message.durationMs === "number" &&
372
+ Number.isInteger(message.durationMs) &&
373
+ message.durationMs >= 0 &&
374
+ validOptionalString(message.toolName)
375
+ ) {
376
+ return { ok: true };
377
+ }
378
+
379
+ return { ok: false, failure: "unreachable" };
380
+ }
@@ -1070,6 +1070,36 @@ export async function probeDaemonEndToEnd(): Promise<boolean> {
1070
1070
  return (await probeDaemon()).ok;
1071
1071
  }
1072
1072
 
1073
+ /**
1074
+ * Proves that the daemon supports the structured request native plugins use.
1075
+ *
1076
+ * This deliberately follows the ordinary end-to-end hook probe. Besides
1077
+ * retaining its startup-race retry, that makes the failure meaningful: an old
1078
+ * daemon can be fully healthy for shell hooks while still lacking the
1079
+ * `policyEvaluation` variant added for Hermes. Protocol v1 predates that
1080
+ * variant, so version equality alone cannot establish the capability.
1081
+ */
1082
+ export async function probeDaemonPolicyEvaluation(): Promise<boolean> {
1083
+ if (!(await probeDaemonEndToEnd())) return false;
1084
+ try {
1085
+ const { attemptDaemonPolicyEvaluation } = await import("./daemon-client");
1086
+ const attempt = await attemptDaemonPolicyEvaluation(
1087
+ {
1088
+ integration: "hermes",
1089
+ event: "on_session_start",
1090
+ payload: {
1091
+ hook_event_name: "on_session_start",
1092
+ source: "failproofai-native-policy-health-probe",
1093
+ },
1094
+ },
1095
+ { responseTimeoutMs: DAEMON_PROBE_TIMEOUT_MS },
1096
+ );
1097
+ return attempt.ok;
1098
+ } catch {
1099
+ return false;
1100
+ }
1101
+ }
1102
+
1073
1103
  /**
1074
1104
  * Waits for the service to report running and to HOLD it — a `Type=simple` unit
1075
1105
  * is active the moment it forks, so one optimistic reading passes a daemon that
@@ -124,8 +124,9 @@ export interface Integration {
124
124
  displayName: string;
125
125
  /**
126
126
  * This integration has no in-process/CLI fallback and cannot evaluate policy
127
- * unless failproofaid answers an end-to-end probe. Installation must stop
128
- * before writing any hook or plugin when the daemon is unavailable.
127
+ * unless failproofaid answers the structured native-policy probe. Installation
128
+ * must stop before writing any hook or plugin when the daemon is unavailable
129
+ * or predates the `policyEvaluation` capability.
129
130
  */
130
131
  requiresHealthyDaemon?: boolean;
131
132
  /** Settings scopes this integration supports (e.g. claude: user/project/local; codex: user/project). */
@@ -29,7 +29,7 @@ import { CORE_SOURCE, addPack, setPackPolicyEnabled } from "./pack-store";
29
29
  import type { ResolvedPack } from "./pack-manifest";
30
30
  import { hasInstalledPacks, readInstalledPacks } from "./pack-manifest";
31
31
  import { packPolicyParamKey } from "./policy-evaluator";
32
- import { probeDaemonEndToEnd } from "./daemon-service";
32
+ import { probeDaemonPolicyEvaluation } from "./daemon-service";
33
33
  import {
34
34
  chip,
35
35
  note,
@@ -477,12 +477,12 @@ async function installHooksImpl(
477
477
  const daemonRequiredBy = selectedIntegrations
478
478
  .map(({ integration }) => integration)
479
479
  .filter((integration) => integration.requiresHealthyDaemon);
480
- if (daemonRequiredBy.length > 0 && !(await probeDaemonEndToEnd())) {
480
+ if (daemonRequiredBy.length > 0 && !(await probeDaemonPolicyEvaluation())) {
481
481
  const names = daemonRequiredBy.map((integration) => integration.displayName).join(", ");
482
482
  const verb = daemonRequiredBy.length === 1 ? "requires" : "require";
483
483
  throw new CliError(
484
- `${names} ${verb} a healthy failproofaid daemon before FailproofAI enforcement can be enabled.\n` +
485
- "Run `failproofai config` to install and configure the daemon, then retry.",
484
+ `${names} ${verb} a compatible failproofaid daemon with native policy evaluation before FailproofAI enforcement can be enabled.\n` +
485
+ "Run `failproofai config` to install or update the daemon, then retry.",
486
486
  );
487
487
  }
488
488