failproofai 1.0.12-beta.1 → 1.0.12

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 (236) 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 +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]__08ch3staq3ejw._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__167mipqmjprq5._.js +3 -0
  69. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0b78b0j8iyv6l._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/{_1a3tubds7s7ow._.js → _0ouu886szh6yj._.js} +4 -4
  75. package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/ssr/_1d98fyfrcaqhy._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
  79. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
  83. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_1i8oid1gc7s1f.js → node_modules_html-to-image_es_index_0tqc05n832v7a.js} +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
  89. package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
  90. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  91. package/.next/standalone/.next/server/pages/404.html +1 -1
  92. package/.next/standalone/.next/server/pages/500.html +1 -1
  93. package/.next/standalone/.next/server/preview-props.json +3 -3
  94. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  96. package/.next/standalone/.next/static/chunks/{1b32-afqacaen.js → 0el9n3h9_zp5m.js} +1 -1
  97. package/.next/standalone/.next/static/chunks/0k7g_8x3o6gd-.js +1 -0
  98. package/.next/standalone/.next/static/chunks/{42e8zab314654.js → 0umzct31duk5g.js} +1 -1
  99. package/.next/standalone/.next/static/chunks/{24w3x2da9sk49.js → 0wobqgosirs8p.js} +1 -1
  100. package/.next/standalone/.next/static/chunks/12rug3jilwqzs.js +1 -0
  101. package/.next/standalone/.next/static/chunks/{2-tf6_7u623kd.js → 1kduj74-715jh.js} +1 -1
  102. package/.next/standalone/.next/static/chunks/2701amhrbsbot.js +1 -0
  103. package/.next/standalone/.next/static/chunks/{18433qz-8njv3.js → 27cavoc8paelg.js} +1 -1
  104. package/.next/standalone/.next/static/chunks/{0_zfad4co-t2z.js → 2ve23a7keupjw.js} +1 -1
  105. package/.next/standalone/.next/static/chunks/3bv6xaoitjpnh.js +6 -0
  106. package/.next/standalone/app/actions/update-jev-config.ts +5 -3
  107. package/.next/standalone/app/settings/jev-panel.tsx +1 -1
  108. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +28 -18
  109. package/.next/standalone/fp-cloud-cli/README.md +127 -9
  110. package/.next/standalone/fp-cloud-cli/fp_cli/app.py +15 -1
  111. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +199 -34
  112. package/.next/standalone/fp-cloud-cli/fp_cli/cloud_output.py +523 -0
  113. package/.next/standalone/fp-cloud-cli/fp_cli/commands/_cloud.py +45 -0
  114. package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +3 -1
  115. package/.next/standalone/fp-cloud-cli/fp_cli/commands/account_cmds.py +171 -0
  116. package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +80 -7
  117. package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +23 -0
  118. package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +2 -0
  119. package/.next/standalone/fp-cloud-cli/fp_cli/commands/contracts_cmds.py +67 -0
  120. package/.next/standalone/fp-cloud-cli/fp_cli/commands/dashboards_cmds.py +159 -0
  121. package/.next/standalone/fp-cloud-cli/fp_cli/commands/evaluators_cmds.py +380 -0
  122. package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +14 -1
  123. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +12 -216
  124. package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +82 -6
  125. package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +4 -1
  126. package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +17 -0
  127. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +143 -1
  128. package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +70 -0
  129. package/.next/standalone/fp-cloud-cli/fp_cli/commands/sentiment_cmds.py +49 -0
  130. package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +95 -1
  131. package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +2 -0
  132. package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +21 -1
  133. package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +14 -0
  134. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +7 -53
  135. package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +3 -0
  136. package/.next/standalone/fp-cloud-cli/fp_cli/evaluator_spec.py +266 -0
  137. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -152
  138. package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +21 -24
  139. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +124 -23
  140. package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +2 -1
  141. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +173 -22
  142. package/.next/standalone/fp-cloud-cli/tests/fake_cloud_smoke.py +312 -0
  143. package/.next/standalone/fp-cloud-cli/tests/test_cloud_account.py +125 -0
  144. package/.next/standalone/fp-cloud-cli/tests/test_cloud_agent.py +253 -0
  145. package/.next/standalone/fp-cloud-cli/tests/test_cloud_human_views.py +287 -0
  146. package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +32 -197
  147. package/.next/standalone/fp-cloud-cli/tests/test_cloud_observability.py +116 -0
  148. package/.next/standalone/fp-cloud-cli/tests/test_cloud_transport.py +141 -0
  149. package/.next/standalone/fp-cloud-cli/tests/test_commands.py +3 -3
  150. package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +3 -3
  151. package/.next/standalone/fp-cloud-cli/tests/test_evaluator_authoring.py +449 -0
  152. package/.next/standalone/fp-cloud-cli/tests/test_facets.py +1 -1
  153. package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +15 -0
  154. package/.next/standalone/fp-cloud-cli/tests/test_operator.py +1 -1
  155. package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +1 -1
  156. package/.next/standalone/fp-cloud-cli/tests/test_output.py +37 -37
  157. package/.next/standalone/fp-cloud-cli/tests/test_round4_behaviour.py +144 -0
  158. package/.next/standalone/fp-cloud-cli/tests/test_screen_language.py +80 -0
  159. package/.next/standalone/fp-cloud-cli/tests/test_session_reevaluate.py +33 -0
  160. package/.next/standalone/fp-cloud-cli/tests/test_staging_parity.py +198 -0
  161. package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +1 -1
  162. package/.next/standalone/hermes-plugin/__init__.py +11 -0
  163. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  164. package/.next/standalone/package.json +11 -10
  165. package/.next/standalone/sdk/python/uv.lock +31 -8
  166. package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +68 -176
  167. package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +2 -2
  168. package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +4 -4
  169. package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +1 -1
  170. package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package-lock.json +7 -7
  171. package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package.json +1 -1
  172. package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package-lock.json +7 -7
  173. package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package.json +1 -1
  174. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +98 -115
  175. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +3 -3
  176. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package-lock.json +4 -4
  177. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package.json +1 -1
  178. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +4 -4
  179. package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +1 -1
  180. package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package-lock.json +149 -171
  181. package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package.json +2 -2
  182. package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package-lock.json +4 -4
  183. package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package.json +1 -1
  184. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +25 -25
  185. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +1 -1
  186. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +7 -7
  187. package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +1 -1
  188. package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +10 -10
  189. package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +1 -1
  190. package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package-lock.json +7 -7
  191. package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package.json +1 -1
  192. package/.next/standalone/server.js +1 -1
  193. package/dist/cli.mjs +1760 -638
  194. package/dist/worker.mjs +426 -76
  195. package/hermes-plugin/__init__.py +11 -0
  196. package/hermes-plugin/plugin.yaml +1 -1
  197. package/package.json +11 -10
  198. package/src/audit/open-browser.ts +24 -7
  199. package/src/hooks/agent-roster.ts +9 -1
  200. package/src/hooks/agent-skill.ts +206 -0
  201. package/src/hooks/cloud-connection.ts +27 -51
  202. package/src/hooks/cloud-introspect.ts +5 -2
  203. package/src/hooks/cloud-login.ts +248 -0
  204. package/src/hooks/cloud-managed-policies.ts +54 -4
  205. package/src/hooks/codex-system-hooks.ts +10 -4
  206. package/src/hooks/config-status.ts +9 -3
  207. package/src/hooks/configure-wizard.ts +356 -32
  208. package/src/hooks/delivery-health.ts +47 -2
  209. package/src/hooks/effective-reviewers.ts +14 -0
  210. package/src/hooks/fp-config.ts +19 -0
  211. package/src/hooks/fp-home.ts +1 -1
  212. package/src/hooks/handler.ts +43 -5
  213. package/src/hooks/hook-activity-store.ts +7 -1
  214. package/src/hooks/integrations.ts +18 -3
  215. package/src/hooks/jev-activity.ts +117 -1
  216. package/src/hooks/jev-cli.ts +7 -9
  217. package/src/hooks/manager.ts +80 -46
  218. package/src/hooks/policy-evaluator.ts +17 -0
  219. package/src/hooks/policy-reviewability.ts +4 -2
  220. package/src/hooks/semantic/cloud-jev.ts +219 -28
  221. package/src/hooks/semantic/combine.ts +26 -1
  222. package/src/hooks/semantic/intent.ts +127 -24
  223. package/src/hooks/semantic/jev-client.ts +1 -1
  224. package/src/hooks/semantic/jev-config.ts +6 -27
  225. package/src/hooks/semantic/jev-review.ts +111 -4
  226. package/src/hooks/tui.ts +280 -0
  227. package/src/hooks/uninstall-cli.ts +12 -1
  228. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +0 -3
  229. package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +0 -3
  230. package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +0 -1
  231. package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +0 -1
  232. package/.next/standalone/.next/static/chunks/1p-hsi58i-6h0.js +0 -6
  233. package/.next/standalone/.next/static/chunks/3-pku9qwv1yfu.js +0 -1
  234. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_buildManifest.js +0 -0
  235. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_clientMiddlewareManifest.js +0 -0
  236. /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_ssgManifest.js +0 -0
@@ -0,0 +1,198 @@
1
+ """Commands aligned with the FailproofAI Cloud staging API after the #885 port:
2
+ every-kind policy backtests, Jev/both authoring, issue board filters and facets,
3
+ evaluation facets, session import and judge/Jev backfill."""
4
+
5
+ import gzip
6
+ import json
7
+
8
+ import pytest
9
+ import respx
10
+
11
+ from fp_cli.app import app
12
+
13
+ BASE = "http://dash.test"
14
+ CHECKS = [{"name": "prod-db", "question": "Does this touch the production database?"}]
15
+
16
+
17
+ def sse(events):
18
+ return "".join(f"data: {json.dumps(e)}\n\n" for e in events)
19
+
20
+
21
+ def test_backtest_regex_file_keeps_the_module_route(logged_in, runner, tmp_path):
22
+ src = tmp_path / "p.mjs"
23
+ src.write_text("export default {}")
24
+ with respx.mock as mock:
25
+ route = mock.post(f"{BASE}/api/agent/policy-backtest").respond(200, json={"matched": 0})
26
+ result = runner.invoke(app, ["--json", "policies", "backtest", str(src), "--agent-id", "a1", "--hours", "24"])
27
+ assert result.exit_code == 0, result.output
28
+ assert json.loads(route.calls[0].request.content) == {"source": "export default {}", "agentId": "a1", "hours": 24}
29
+
30
+
31
+ def test_backtest_published_jev_policy_uses_backtest_all_estimate(logged_in, runner):
32
+ with respx.mock as mock:
33
+ mock.get(f"{BASE}/api/enforcement/policies").respond(200, json=[
34
+ {"id": "p1", "version": 1, "kind": "jev", "semantic": CHECKS},
35
+ ])
36
+ route = mock.post(f"{BASE}/api/agent/policy-backtest-all").respond(200, json={"kind": "jev", "mode": "estimate"})
37
+ result = runner.invoke(app, ["--json", "policies", "backtest", "p1"])
38
+ assert result.exit_code == 0, result.output
39
+ assert json.loads(route.calls[0].request.content) == {
40
+ "kind": "jev", "semantic": CHECKS, "hours": 168, "mode": "estimate",
41
+ }
42
+
43
+
44
+ def test_backtest_both_from_files_with_run(logged_in, runner, tmp_path):
45
+ src, sem = tmp_path / "p.mjs", tmp_path / "checks.json"
46
+ src.write_text("export default {}")
47
+ sem.write_text(json.dumps(CHECKS))
48
+ with respx.mock as mock:
49
+ route = mock.post(f"{BASE}/api/agent/policy-backtest-all").respond(200, json={"kind": "both"})
50
+ result = runner.invoke(app, ["--json", "policies", "backtest", str(src), "--semantic", str(sem),
51
+ "--run", "--sample", "10"])
52
+ assert result.exit_code == 0, result.output
53
+ body = json.loads(route.calls[0].request.content)
54
+ assert body["kind"] == "both" and body["mode"] == "run" and body["sample"] == 10
55
+ assert body["source"] == "export default {}" and body["semantic"] == CHECKS
56
+
57
+
58
+ @pytest.mark.parametrize("argv", [
59
+ ["policies", "backtest"],
60
+ ["policies", "backtest", "--kind", "jev", "--semantic", "missing.json"],
61
+ ["policies", "backtest", "x.mjs", "--kind", "nope"],
62
+ ["policies", "backtest", "x.mjs", "--sample", "99"],
63
+ ])
64
+ def test_backtest_usage_errors(logged_in, runner, argv):
65
+ with respx.mock(assert_all_called=False):
66
+ result = runner.invoke(app, ["--json", *argv])
67
+ assert result.exit_code == 2, result.output
68
+
69
+
70
+ @pytest.mark.parametrize("kind,sent", [(None, None), ("regex", None), ("jev", "jev"), ("both", "both")])
71
+ def test_author_kind(logged_in, runner, kind, sent):
72
+ with respx.mock as mock:
73
+ route = mock.post(f"{BASE}/api/agent/policy-author").respond(
74
+ 200, text=sse([{"type": "done", "result": {"source": "x"}}]))
75
+ result = runner.invoke(app, ["--json", "policies", "author", "No prod DB",
76
+ *(["--kind", kind] if kind else [])])
77
+ assert result.exit_code == 0, result.output
78
+ assert json.loads(route.calls[0].request.content).get("kind") == sent
79
+
80
+
81
+ @pytest.mark.parametrize("key_mode", [False, True])
82
+ def test_issue_list_filters(logged_in, runner, key_mode):
83
+ prefix = "/v1" if key_mode else "/api"
84
+ with respx.mock as mock:
85
+ route = mock.get(f"{BASE}{prefix}/issues").respond(200, json=[])
86
+ result = runner.invoke(app, ["--json", *(["--api-key", "k"] if key_mode else []), "issues", "list",
87
+ "--agent-id", "a1,a2", "--source", "audit", "--assignee", "me",
88
+ "--archived", "all", "--search", "timeout"])
89
+ assert result.exit_code == 0, result.output
90
+ params = route.calls[0].request.url.params
91
+ assert params["agent"] == "a1,a2" and params["source"] == "audit"
92
+ assert params["assignee"] == "me" and params["archived"] == "all" and params["q"] == "timeout"
93
+ assert json.loads(result.stdout) == {"issues": []}
94
+
95
+
96
+ def test_issue_list_alert_id_alone_keeps_the_alert_route(logged_in, runner):
97
+ with respx.mock as mock:
98
+ route = mock.get(f"{BASE}/api/alerts/al1/issues").respond(200, json=[])
99
+ result = runner.invoke(app, ["--json", "issues", "list", "--alert-id", "al1"])
100
+ assert result.exit_code == 0, result.output
101
+ assert route.called
102
+
103
+
104
+ def test_issue_count_and_facets(logged_in, runner):
105
+ with respx.mock as mock:
106
+ count = mock.get(f"{BASE}/api/issues/count").respond(200, json={"count": 3})
107
+ facets = mock.get(f"{BASE}/api/issues/facets").respond(200, json={"agents": [{"value": "a1", "count": 3}]})
108
+ r1 = runner.invoke(app, ["--json", "issues", "count", "--env", "prod"])
109
+ r2 = runner.invoke(app, ["--json", "issues", "facets", "--state", "firing"])
110
+ assert r1.exit_code == 0 and json.loads(r1.stdout) == {"count": 3}
111
+ assert count.calls[0].request.url.params["environment"] == "prod"
112
+ assert r2.exit_code == 0, r2.output
113
+ assert facets.calls[0].request.url.params["state"] == "firing"
114
+
115
+
116
+ def test_issue_filter_validation(logged_in, runner):
117
+ with respx.mock(assert_all_called=False):
118
+ assert runner.invoke(app, ["issues", "list", "--archived", "maybe"]).exit_code == 2
119
+ assert runner.invoke(app, ["issues", "list", "--source", "slack"]).exit_code == 2
120
+
121
+
122
+ @pytest.mark.parametrize("name,path,v1,body,values", [
123
+ ("trigger_events", "/api/events/trigger_events", "/v1/events/trigger_events", ["PreToolUse"], ["PreToolUse"]),
124
+ ("eval_envs", "/api/evaluations/environments", "/v1/evaluations/environments", ["prod"], ["prod"]),
125
+ ("metric_keys", "/api/evaluations/metric-keys", "/v1/evaluations/metric_keys",
126
+ [{"name": "latency_ms", "unit": "ms", "count": 4}], ["latency_ms"]),
127
+ ])
128
+ @pytest.mark.parametrize("key_mode", [False, True])
129
+ def test_new_list_facets(logged_in, runner, name, path, v1, body, values, key_mode):
130
+ with respx.mock as mock:
131
+ mock.get(f"{BASE}{v1 if key_mode else path}").respond(200, json=body)
132
+ result = runner.invoke(app, ["--json", *(["--api-key", "k"] if key_mode else []), "list", name])
133
+ assert result.exit_code == 0, result.output
134
+ assert json.loads(result.stdout) == {"kind": name, "values": values}
135
+
136
+
137
+ def test_sessions_import_gzips_and_shares_one_cutoff(logged_in, runner, tmp_path):
138
+ first, second = tmp_path / "proj" / "s1.jsonl", tmp_path / "proj" / "s2.jsonl.gz"
139
+ first.parent.mkdir()
140
+ first.write_text('{"type":"user"}\n')
141
+ second.write_bytes(gzip.compress(b'{"type":"user"}\n'))
142
+ with respx.mock as mock:
143
+ route = mock.post(f"{BASE}/api/events/import").respond(200, json={"sessions": 1})
144
+ result = runner.invoke(app, ["--json", "sessions", "import", str(first), str(second),
145
+ "--format", "claude", "--env", "dev", "--dry-run"])
146
+ assert result.exit_code == 0, result.output
147
+ assert len(route.calls) == 2
148
+ a, b = (c.request for c in route.calls)
149
+ assert gzip.decompress(a.content) == b'{"type":"user"}\n'
150
+ assert gzip.decompress(b.content) == b'{"type":"user"}\n'
151
+ assert a.headers["content-type"] == "application/octet-stream"
152
+ assert a.url.params["path"] == "proj/s1.jsonl" and a.url.params["format"] == "claude"
153
+ assert a.url.params["dry_run"] == "true" and a.url.params["environment"] == "dev"
154
+ assert a.url.params["existing_before"] == b.url.params["existing_before"]
155
+ out = json.loads(result.stdout)
156
+ assert [f["file"] for f in out["files"]] == [str(first), str(second)]
157
+
158
+
159
+ def test_sessions_import_bad_format_and_missing_file(logged_in, runner, tmp_path):
160
+ with respx.mock(assert_all_called=False):
161
+ assert runner.invoke(app, ["sessions", "import", "x", "--format", "zip"]).exit_code == 2
162
+ assert runner.invoke(app, ["sessions", "import", str(tmp_path / "none.jsonl")]).exit_code == 2
163
+
164
+
165
+ def test_sessions_list_still_works_as_a_group_callback(logged_in, runner):
166
+ with respx.mock as mock:
167
+ mock.get(f"{BASE}/api/sessions").respond(200, json={"sessions": [], "next_cursor": None})
168
+ result = runner.invoke(app, ["--json", "sessions", "--limit", "5"])
169
+ assert result.exit_code == 0, result.output
170
+
171
+
172
+ def test_deploy_backfill_allows_judge(logged_in, runner):
173
+ with respx.mock as mock:
174
+ mock.get(f"{BASE}/api/evaluator-definitions").respond(200, json=[])
175
+ mock.post(f"{BASE}/api/evaluator-definitions").respond(201, json={"id": "d1"})
176
+ backfill = mock.post(f"{BASE}/api/evaluations/backfill").respond(200, json={"enqueued": 2})
177
+ result = runner.invoke(app, ["--json", "evaluators", "deploy", "relevance", "--judge",
178
+ "--criteria", "Grade relevance", "--threshold", "0.7", "--backfill", "7d"])
179
+ assert result.exit_code == 0, result.output
180
+ assert json.loads(backfill.calls[0].request.content)["eval_key"] == "relevance"
181
+ assert "model budget" in result.stderr
182
+
183
+
184
+ def test_usage_windows_needs_a_key(logged_in, runner):
185
+ with respx.mock as mock:
186
+ result = runner.invoke(app, ["--json", "usage", "windows"])
187
+ assert not mock.calls
188
+ assert result.exit_code == 2
189
+
190
+
191
+ def test_sessions_import_public_format_name_maps_to_the_wire_name(logged_in, runner, tmp_path):
192
+ f = tmp_path / "events.jsonl"
193
+ f.write_text('{"event_type":"agent_start"}\n')
194
+ with respx.mock as mock:
195
+ route = mock.post(f"{BASE}/api/events/import").respond(200, json={})
196
+ result = runner.invoke(app, ["--json", "sessions", "import", str(f), "--format", "failproofai"])
197
+ assert result.exit_code == 0, result.output
198
+ assert route.calls[0].request.url.params["format"] == "agenteye"
@@ -66,7 +66,7 @@ def test_whoami_human_renders_header_and_panels(logged_in, runner):
66
66
  assert "62230791-4811-4f04-b388-ae57bdcb422e" in out
67
67
  assert "instance admin" in out
68
68
  # permissions panel
69
- assert "permissions" in out
69
+ assert "PERMISSIONS" in out
70
70
  assert "dashboards" in out and "read" in out and "write" in out and "delete" in out
71
71
  # orgs panel + switch hint to the non-active org
72
72
  assert "your orgs" in out
@@ -104,6 +104,17 @@ def _profile_name() -> str:
104
104
 
105
105
  def _profile_settings_path(profile: str) -> str | None:
106
106
  """Evidence of the profile running this plugin, not a transcript guess."""
107
+ try:
108
+ # The host's own resolver first: it honours a per-task profile
109
+ # override as well as HERMES_HOME, so each profile of a multi-profile
110
+ # gateway reports its own config (and roster identity), not the
111
+ # process-wide one.
112
+ from hermes_constants import get_hermes_home
113
+
114
+ hosted = Path(get_hermes_home()).expanduser() / "config.yaml"
115
+ return str(hosted.absolute()) if hosted.is_file() else None
116
+ except Exception:
117
+ pass
107
118
  configured = os.environ.get("HERMES_HOME", "").strip()
108
119
  if configured:
109
120
  selected = Path(configured).expanduser() / "config.yaml"
@@ -1,5 +1,5 @@
1
1
  name: failproofai
2
- version: 1.0.12-beta.1
2
+ version: 1.0.12
3
3
  description: Real-time FailproofAI policy enforcement for Hermes
4
4
  manifest_version: 2
5
5
  api_version: 1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "failproofai",
3
- "version": "1.0.12-beta.1",
3
+ "version": "1.0.12",
4
4
  "description": "Observability and enforcement for AI agent harnesses. 40 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",
@@ -118,22 +118,23 @@
118
118
  "postcss": "8.5.26",
119
119
  "eslint-plugin-react-hooks": "7.0.1",
120
120
  "vite": "8.1.6",
121
- "undici": "7.29.1",
121
+ "undici": "7.30.1",
122
+ "zod": "4.6.6",
122
123
  "brace-expansion": "5.0.12",
123
124
  "sharp": "0.35.5",
124
125
  "source-map-js": "1.2.2",
125
126
  "browserslist": "4.28.8"
126
127
  },
127
128
  "optionalDependencies": {
128
- "@failproofai/failproofaid-linux-x64": "1.0.12-beta.1",
129
- "@failproofai/failproofaid-linux-arm64": "1.0.12-beta.1",
130
- "@failproofai/failproofaid-darwin-x64": "1.0.12-beta.1",
131
- "@failproofai/failproofaid-darwin-arm64": "1.0.12-beta.1"
129
+ "@failproofai/failproofaid-linux-x64": "1.0.12",
130
+ "@failproofai/failproofaid-linux-arm64": "1.0.12",
131
+ "@failproofai/failproofaid-darwin-x64": "1.0.12",
132
+ "@failproofai/failproofaid-darwin-arm64": "1.0.12"
132
133
  },
133
134
  "failproofaidBinaries": {
134
- "linux-x64": "df15e65b60066248038c8ac6d423bffcb0e38637e8a42fa22250afa111fd46ac",
135
- "linux-arm64": "59d7ae758c8981c5e0a89eb64be18ebd278213525dd4447891a51be6bb018ed9",
136
- "darwin-x64": "b0d3d39abaa02ed0a14dddc68277975a7a23e200830b7edbffdedde3af81ce73",
137
- "darwin-arm64": "a6d343b5926e84c0eb437af307f85b69b3128f94c6e34acbf9d0b13a7c7e9d63"
135
+ "linux-x64": "3b379ce2b03dd8a74393daed2c3120309f591fe6433e0c939d70c7b20da55be7",
136
+ "linux-arm64": "bd77d7fee68dc2723fb57cc9c7fcf957b7e939c797e61b099c412263880aa16e",
137
+ "darwin-x64": "fce3faab9406b1c2267955f0e07d04ef7a2b7a0b007e12ecd144fcfef563ebc8",
138
+ "darwin-arm64": "765732f8f64aa3d3dbd98e93b1aba71fc02956f65baeb73b08447b37fac3a17b"
138
139
  }
139
140
  }
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 5
3
3
  requires-python = ">=3.10"
4
4
  resolution-markers = [
5
5
  "python_full_version >= '3.14' and sys_platform == 'win32'",
@@ -1091,11 +1091,33 @@ provides-extras = ["dev", "langchain", "langgraph", "crewai", "llamaindex", "pyd
1091
1091
 
1092
1092
  [[package]]
1093
1093
  name = "filelock"
1094
- version = "3.32.3"
1094
+ version = "4.1.0"
1095
+ source = { registry = "https://pypi.org/simple" }
1096
+ resolution-markers = [
1097
+ "python_full_version < '3.11' and sys_platform == 'win32'",
1098
+ "python_full_version < '3.11' and sys_platform != 'win32'",
1099
+ ]
1100
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/58/6fd434bec86eff7c38a3168454cb132b762b2bea9b3ac094101a2f7bc32a/filelock-4.1.0.tar.gz", hash = "sha256:ad7f724afef953e731b1cc39bcd3a09166d72ed7fcdf29e6e88b1c3235c6715d", size = 561277, upload-time = "2026-10-09T19:57:20.34Z" }
1101
+ wheels = [
1102
+ { url = "https://files.pythonhosted.org/packages/ee/86/032133892a5de43b5a98200b01aadcad68cc255e274a762f08b8a76d2912/filelock-4.1.0-py3-none-any.whl", hash = "sha256:2ce9818e3e2d8f284c1a964414447ef148d42a5fd5e2a477a7118e574b293ec1", size = 133003, upload-time = "2026-10-09T19:57:18.716Z" },
1103
+ ]
1104
+
1105
+ [[package]]
1106
+ name = "filelock"
1107
+ version = "4.2.4"
1095
1108
  source = { registry = "https://pypi.org/simple" }
1096
- sdist = { url = "https://files.pythonhosted.org/packages/7d/64/a02e6765de08964ed371eca577870593245afc9dfac16d037de7c10d18e6/filelock-3.32.3.tar.gz", hash = "sha256:0ffa185a3540854c95caa7fa76b76cb219d907415e2c5dc9af25fd970563487f", size = 218135, upload-time = "2026-08-13T16:00:05.577Z" }
1109
+ resolution-markers = [
1110
+ "python_full_version >= '3.14' and sys_platform == 'win32'",
1111
+ "python_full_version >= '3.14' and sys_platform != 'win32'",
1112
+ "python_full_version == '3.13.*'",
1113
+ "python_full_version == '3.12.*' and sys_platform == 'win32'",
1114
+ "python_full_version == '3.12.*' and sys_platform != 'win32'",
1115
+ "python_full_version == '3.11.*' and sys_platform == 'win32'",
1116
+ "python_full_version == '3.11.*' and sys_platform != 'win32'",
1117
+ ]
1118
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/28/3893324bcadf8f89b1616637468d766af3f625008c0735bf704fdfa86617/filelock-4.2.4.tar.gz", hash = "sha256:c6d8663535e1650ee430507b4ffbac7b464dd64b7647ea0112a9f406499d163d", size = 652444, upload-time = "2026-10-19T01:57:51.001Z" }
1097
1119
  wheels = [
1098
- { url = "https://files.pythonhosted.org/packages/a7/8e/50f46a9c0ce8d2861a394c1347caae037ea0431d2f67d7feb151cbc4649a/filelock-3.32.3-py3-none-any.whl", hash = "sha256:7f0ca4bcc0e181c60dbbd8aa9ab5b120ebb99e4e064e83636340056f833a1f09", size = 98901, upload-time = "2026-08-13T16:00:03.974Z" },
1120
+ { url = "https://files.pythonhosted.org/packages/e0/1b/5e74289a1878e666bb4b65fe5805a04cafcac2aa5f519ef1ead67ef91c56/filelock-4.2.4-py3-none-any.whl", hash = "sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e", size = 162475, upload-time = "2026-10-19T01:57:49.419Z" },
1099
1121
  ]
1100
1122
 
1101
1123
  [[package]]
@@ -1679,7 +1701,8 @@ version = "1.28.0"
1679
1701
  source = { registry = "https://pypi.org/simple" }
1680
1702
  dependencies = [
1681
1703
  { name = "click" },
1682
- { name = "filelock" },
1704
+ { name = "filelock", version = "4.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
1705
+ { name = "filelock", version = "4.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
1683
1706
  { name = "fsspec" },
1684
1707
  { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
1685
1708
  { name = "httpx" },
@@ -4967,11 +4990,11 @@ wheels = [
4967
4990
 
4968
4991
  [[package]]
4969
4992
  name = "setuptools"
4970
- version = "84.0.0"
4993
+ version = "84.2.1"
4971
4994
  source = { registry = "https://pypi.org/simple" }
4972
- sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" }
4995
+ sdist = { url = "https://files.pythonhosted.org/packages/70/b3/c19426f783614d625699a4b247faf628ea9ab1db228674491767b417f9e1/setuptools-84.2.1.tar.gz", hash = "sha256:3ff8b3b5492a0aa66960f4419365a86b84e013070d4313e4d4a6894f5d64d6bc", size = 1230289, upload-time = "2026-10-20T19:01:37.742Z" }
4973
4996
  wheels = [
4974
- { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" },
4997
+ { url = "https://files.pythonhosted.org/packages/ef/55/6dc6d6bb953bdfc52519a0059592c52b6e3b8ad51cf8256dffceb55b0610/setuptools-84.2.1-py3-none-any.whl", hash = "sha256:ca140b45d5325a9478f880e351484f62fef15f336462e449ca0c03c873d589c2", size = 876263, upload-time = "2026-10-20T19:01:35.678Z" },
4975
4998
  ]
4976
4999
 
4977
5000
  [[package]]