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,380 @@
1
+ """Author, test and deploy managed evaluators, independently of result readers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ from datetime import datetime, timedelta, timezone
8
+ from typing import List, Optional
9
+
10
+ import typer
11
+
12
+ from .. import client as api, cloud_output, dates, evaluator_spec as spec, output
13
+ from .._context import GLOBALS_EPILOG, require_auth
14
+ from ..errors import ApiError, NotFoundError
15
+ from . import _cloud, _write
16
+
17
+ PATH = "/api/evaluator-definitions"
18
+
19
+
20
+ def definitions(cctx) -> list:
21
+ return _cloud.rows(api.cloud_request(cctx, "GET", PATH), "definitions")
22
+
23
+
24
+ def select(items: list, handle: str) -> dict:
25
+ key, _, version = handle.partition("@")
26
+ matches = [r for r in items if r.get("eval_key") == key and (not version or str(r.get("eval_version")) == version)]
27
+ if not matches:
28
+ raise NotFoundError(f"no evaluator {handle}; run `fp evaluators list --all`")
29
+ if not version:
30
+ enabled = [r for r in matches if r.get("enabled", True)]
31
+ matches = enabled or matches
32
+ if len(matches) != 1:
33
+ spec.fail(f"{handle} matches multiple versions; use KEY@VERSION")
34
+ return matches[0]
35
+
36
+
37
+ def list_defs(ctx: typer.Context, key: Optional[str] = typer.Option(None, "--key"),
38
+ all_versions: bool = typer.Option(False, "--all")) -> None:
39
+ """List definitions (enabled only unless --all). Needs evaluations:read."""
40
+ items = definitions(require_auth(ctx.obj))
41
+ items = [r for r in items if (not key or r.get("eval_key") == key) and (all_versions or r.get("enabled", True))]
42
+ _cloud.emit(ctx.obj, items, "Evaluators")
43
+
44
+
45
+ def show(ctx: typer.Context, handle: str = typer.Argument(..., metavar="KEY[@VERSION]")) -> None:
46
+ """Show one definition, including its full source."""
47
+ _cloud.emit(ctx.obj, select(definitions(require_auth(ctx.obj)), handle), "Evaluator")
48
+
49
+
50
+ def _create(state, cctx, body, sessions, disabled=False, force=False) -> dict:
51
+ if sessions:
52
+ if spec.source_kind(body) == "jev":
53
+ output.warn("Jev dry-runs spend model budget.")
54
+ preview = api.cloud_request(cctx, "POST", f"{PATH}/dry-run", body=spec.dry_run_body(body, sessions))
55
+ if not state.json:
56
+ cloud_output.dry_run(preview)
57
+ if dry_run_failed(preview) and force:
58
+ output.warn("Dry-run failed; --force authorizes creating the evaluator anyway.")
59
+ else:
60
+ check_dry_run(preview)
61
+ _write.confirm(state, f"Create evaluator {body['eval_key']}@{body['eval_version']}", assume_yes=False)
62
+ row = api.cloud_request(cctx, "POST", PATH, body=body)
63
+ if not isinstance(row, dict) or not row.get("id"):
64
+ raise ApiError("Cloud created no identifiable evaluator; check evaluators list --all before retrying")
65
+ if disabled:
66
+ api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"enabled": False})
67
+ row = {**row, "enabled": False}
68
+ _write.record_action("evaluator_created", resource="evaluator", mode=spec.source_kind(body), success=True)
69
+ return row
70
+
71
+
72
+ def create(
73
+ ctx: typer.Context,
74
+ key: Optional[str] = typer.Option(None, "--key"),
75
+ name: Optional[str] = typer.Option(None, "--name"),
76
+ version: Optional[str] = typer.Option(None, "--version"),
77
+ label: Optional[List[str]] = typer.Option(None, "--label"),
78
+ condition: Optional[str] = typer.Option(None, "--condition", help="Python expression or @file."),
79
+ code: Optional[str] = typer.Option(None, "--code", help="Restricted Python expression file, or - for stdin."),
80
+ result_kind: Optional[str] = typer.Option(None, "--result-kind"),
81
+ timeout: Optional[float] = typer.Option(None, "--timeout"),
82
+ judge: bool = typer.Option(False, "--judge"),
83
+ criteria: Optional[str] = typer.Option(None, "--criteria"),
84
+ step: Optional[List[str]] = typer.Option(None, "--step"),
85
+ threshold: Optional[float] = typer.Option(None, "--threshold"),
86
+ jev: Optional[str] = typer.Option(None, "--jev", help="noul or score."),
87
+ instructions: Optional[str] = typer.Option(None, "--instructions"),
88
+ true: Optional[str] = typer.Option(None, "--true"),
89
+ false: Optional[str] = typer.Option(None, "--false"),
90
+ level: Optional[List[str]] = typer.Option(None, "--level", help="3–5 distinct levels, worst to best."),
91
+ file: Optional[str] = typer.Option(None, "--file"),
92
+ dry_run_on: Optional[List[str]] = typer.Option(None, "--dry-run-on"),
93
+ disabled: bool = typer.Option(False, "--disabled"),
94
+ force: bool = typer.Option(False, "--force", help="Create despite failed --dry-run-on results (review the failures first)."),
95
+ ) -> None:
96
+ """Create a code, LLM judge or Jev evaluator (enabled by default).
97
+
98
+ Needs evaluations:trigger; session dry-run also needs events:read.
99
+
100
+ Code: `fp --json evaluators create --key concise --name Concise --code concise.py --result-kind score --dry-run-on run-001`
101
+
102
+ Judge: `fp --json evaluators create --key relevance --name Relevance --judge --criteria "Grade relevance to the question" --step "Assess answer specificity" --threshold 0.7`
103
+
104
+ Jev: `fp --json evaluators create --key resolved --name Resolved --jev noul --instructions "Assess whether the request was resolved" --true "Fully resolved" --false "Not resolved" --threshold 0.8`
105
+
106
+ Spec: `fp --json evaluators create --file evaluator.json`
107
+ """
108
+ body = spec.build(
109
+ key=key, name=name, version=version, label=label, condition=condition,
110
+ code=code, result_kind=result_kind, timeout=timeout, judge=judge, criteria=criteria,
111
+ step=step, threshold_value=threshold, jev=jev, instructions=instructions,
112
+ true=true, false=false, level=level, file=file,
113
+ )
114
+ # Validate before any request, including the judge refusal / session bound.
115
+ if dry_run_on:
116
+ spec.dry_run_body(body, dry_run_on)
117
+ row = _create(ctx.obj, require_auth(ctx.obj), body, dry_run_on, disabled, force)
118
+ if ctx.obj.json:
119
+ output.emit_json(row)
120
+ else:
121
+ cloud_output.evaluator({**body, **row}, "Evaluator created")
122
+
123
+
124
+ def deploy(
125
+ ctx: typer.Context,
126
+ key: str = typer.Argument(..., metavar="EVAL_KEY"),
127
+ name: Optional[str] = typer.Option(None, "--name"),
128
+ version: Optional[str] = typer.Option(None, "--version"),
129
+ label: Optional[List[str]] = typer.Option(None, "--label"),
130
+ condition: Optional[str] = typer.Option(None, "--condition"),
131
+ code: Optional[str] = typer.Option(None, "--code"),
132
+ result_kind: Optional[str] = typer.Option(None, "--result-kind"),
133
+ timeout: Optional[float] = typer.Option(None, "--timeout"),
134
+ judge: bool = typer.Option(False, "--judge"),
135
+ criteria: Optional[str] = typer.Option(None, "--criteria"),
136
+ step: Optional[List[str]] = typer.Option(None, "--step"),
137
+ threshold: Optional[float] = typer.Option(None, "--threshold"),
138
+ jev: Optional[str] = typer.Option(None, "--jev"),
139
+ instructions: Optional[str] = typer.Option(None, "--instructions"),
140
+ true: Optional[str] = typer.Option(None, "--true"),
141
+ false: Optional[str] = typer.Option(None, "--false"),
142
+ level: Optional[List[str]] = typer.Option(None, "--level"),
143
+ file: Optional[str] = typer.Option(None, "--file"),
144
+ dry_run_on: Optional[List[str]] = typer.Option(None, "--dry-run-on"),
145
+ backfill: Optional[str] = typer.Option(None, "--backfill", help="Score stored sessions too, e.g. 7d (up to 90d). Judge/Jev backfills spend model budget."),
146
+ force: bool = typer.Option(False, "--force", help="Deploy despite failed --dry-run-on results (review the failures first)."),
147
+ ) -> None:
148
+ """Publish a NEW immutable version, then disable enabled predecessors.
149
+
150
+ Numeric versions auto-increment; for other version names pass --version.
151
+ A failed predecessor PATCH leaves the new version enabled and reports its id.
152
+ """
153
+ body = spec.build(
154
+ key=None if file else key, name=name, version=None if file else version, label=label,
155
+ condition=condition, code=code, result_kind=result_kind, timeout=timeout,
156
+ judge=judge, criteria=criteria, step=step, threshold_value=threshold, jev=jev,
157
+ instructions=instructions, true=true, false=false, level=level, file=file, complete=False,
158
+ )
159
+ if file and body.get("eval_key") is not None and body["eval_key"] != key:
160
+ spec.fail("The spec eval_key must match the deploy argument")
161
+ body["eval_key"] = key
162
+ if version is not None:
163
+ body["eval_version"] = version
164
+ if dry_run_on:
165
+ spec.dry_run_body(body, dry_run_on)
166
+ backfill_body = None
167
+ if backfill:
168
+ if not re.fullmatch(r"[1-9][0-9]*d", backfill) or int(backfill[:-1]) > 90:
169
+ spec.fail("--backfill must be a duration in days from 1d to 90d")
170
+ now = datetime.now(timezone.utc)
171
+ backfill_body = {
172
+ "from": dates._iso(now - timedelta(days=int(backfill[:-1]))),
173
+ "to": dates._iso(now),
174
+ "eval_key": key,
175
+ "dry_run": False,
176
+ }
177
+ if spec.source_kind(body) != "code":
178
+ output.warn("Backfilling a judge/Jev evaluator spends the org's model budget.")
179
+ cctx = require_auth(ctx.obj)
180
+ existing = [r for r in definitions(cctx) if r.get("eval_key") == key]
181
+ if "eval_version" not in body:
182
+ versions = [str(r.get("eval_version", "")) for r in existing]
183
+ if any(not v.isdigit() for v in versions):
184
+ spec.fail("Non-numeric versions exist; specify a new --version")
185
+ body["eval_version"] = str(max([int(v) for v in versions] or [0]) + 1)
186
+ if any(str(r.get("eval_version")) == body["eval_version"] for r in existing):
187
+ spec.fail(f"Version {key}@{body['eval_version']} already exists; deploy needs a NEW version")
188
+ body.setdefault("display_name", existing[-1].get("display_name", key) if existing else key)
189
+ if existing and not file:
190
+ # A new version keeps the previous version's scope and labels unless a flag
191
+ # changes them; silently dropping the condition would widen the evaluator to
192
+ # every session. A --file spec states every field itself.
193
+ latest = max(existing, key=lambda r: str(r.get("created_at") or ""))
194
+ if condition is None:
195
+ body["condition_source"] = latest.get("condition_source")
196
+ if not label:
197
+ body["labels"] = list(latest.get("labels") or [])
198
+ spec.validate(body)
199
+ created = _create(ctx.obj, cctx, body, dry_run_on, force=force)
200
+ disabled_ids = []
201
+ try:
202
+ for old in existing:
203
+ if old.get("enabled", True):
204
+ api.cloud_request(cctx, "PATCH", f"{PATH}/{old['id']}", body={"enabled": False})
205
+ disabled_ids.append(old["id"])
206
+ if backfill_body:
207
+ api.cloud_request(cctx, "POST", "/api/evaluations/backfill", body=backfill_body)
208
+ except Exception as exc:
209
+ output.warn(f"Published {key}@{body['eval_version']} (id {created['id']}); disabled {len(disabled_ids)} predecessors. Follow-up failed; inspect evaluators list --all before retrying.")
210
+ raise exc
211
+ _write.record_action("evaluator_deployed", resource="evaluator", success=True)
212
+ if ctx.obj.json:
213
+ output.emit_json({"created": created, "disabled": disabled_ids})
214
+ else:
215
+ cloud_output.deployed({**body, **created}, [r for r in existing if r["id"] in disabled_ids])
216
+
217
+
218
+ def draft(
219
+ ctx: typer.Context,
220
+ intent: str = typer.Argument(...),
221
+ kind: str = typer.Option("code", "--kind"),
222
+ out: str = typer.Option("evaluator.json", "--out"),
223
+ ) -> None:
224
+ """AI-compose an API spec file. Session only; review it before deployment."""
225
+ _cloud.session_only(ctx.obj, "evaluators draft")
226
+ if kind not in ("code", "judge", "jev"):
227
+ spec.fail("--kind must be code, judge or jev")
228
+ data = api.cloud_request(require_auth(ctx.obj), "POST", "/api/agent/compose-eval",
229
+ body={"intent": f"Author a {kind} evaluator. {intent}", "resultKind": "score"})
230
+ if not isinstance(data, dict):
231
+ spec.fail("Cloud draft was not an evaluator JSON spec")
232
+ body = spec.from_draft(data, kind)
233
+ _cloud.write_file(out, json.dumps(body, indent=2, ensure_ascii=False) + "\n")
234
+ _cloud.emit(ctx.obj, {"out": out, "spec": body}, "Evaluator draft")
235
+
236
+
237
+ def dry_run(
238
+ ctx: typer.Context,
239
+ code: Optional[str] = typer.Option(None, "--code"),
240
+ result_kind: Optional[str] = typer.Option(None, "--result-kind"),
241
+ timeout: Optional[float] = typer.Option(None, "--timeout"),
242
+ condition: Optional[str] = typer.Option(None, "--condition"),
243
+ judge: bool = typer.Option(False, "--judge"),
244
+ criteria: Optional[str] = typer.Option(None, "--criteria"),
245
+ step: Optional[List[str]] = typer.Option(None, "--step"),
246
+ threshold: Optional[float] = typer.Option(None, "--threshold"),
247
+ jev: Optional[str] = typer.Option(None, "--jev"),
248
+ instructions: Optional[str] = typer.Option(None, "--instructions"),
249
+ true: Optional[str] = typer.Option(None, "--true"),
250
+ false: Optional[str] = typer.Option(None, "--false"),
251
+ level: Optional[List[str]] = typer.Option(None, "--level"),
252
+ file: Optional[str] = typer.Option(None, "--file"),
253
+ session: Optional[List[str]] = typer.Option(None, "--session"),
254
+ ) -> None:
255
+ """Compile only, or run on ≤10 sessions. Cloud refuses judge dry-runs.
256
+
257
+ Jev dry-runs execute model calls and spend budget, including before deployment.
258
+ """
259
+ body = spec.build(
260
+ code=code, result_kind=result_kind, timeout=timeout, condition=condition,
261
+ judge=judge, criteria=criteria, step=step, threshold_value=threshold, jev=jev,
262
+ instructions=instructions, true=true, false=false, level=level, file=file, complete=False,
263
+ )
264
+ body = spec.dry_run_body(body, session)
265
+ if spec.source_kind(body) == "jev":
266
+ output.warn("Jev dry-runs spend model budget.")
267
+ result = api.cloud_request(require_auth(ctx.obj), "POST", f"{PATH}/dry-run", body=body)
268
+ _cloud.emit(ctx.obj, result, "Evaluator dry-run")
269
+ if dry_run_failed(result):
270
+ # Preserve the full dry-run result in --json, but do not signal success.
271
+ raise typer.Exit(code=1)
272
+
273
+
274
+ def dry_run_failed(result):
275
+ return isinstance(result, dict) and (
276
+ result.get("ok") is False
277
+ or any(r.get("ok") is False for r in result.get("runs", []) if isinstance(r, dict))
278
+ or result.get("summary", {}).get("failed", 0) > 0
279
+ )
280
+
281
+
282
+ def check_dry_run(result):
283
+ if dry_run_failed(result):
284
+ failures = [r for r in result.get("runs", []) if r.get("ok") is False]
285
+ detail = "; ".join(str(r.get("error_message") or r.get("error_kind") or "run failed") for r in failures)
286
+ raise ApiError(f"Dry-run failed; evaluator was not created. {detail}".strip())
287
+
288
+
289
+ def toggle(ctx, handle, enabled):
290
+ cctx = require_auth(ctx.obj)
291
+ row = select(definitions(cctx), handle)
292
+ _write.confirm(ctx.obj, f"{'Enable' if enabled else 'Disable'} {handle}", assume_yes=False)
293
+ result = api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"enabled": enabled})
294
+ _write.record_action("evaluator_toggled", resource="evaluator", success=True)
295
+ if ctx.obj.json:
296
+ output.emit_json(result or {**row, "enabled": enabled})
297
+ else:
298
+ cloud_output.evaluator({**row, **(result or {}), "enabled": enabled})
299
+
300
+
301
+ def enable(ctx: typer.Context, handle: str = typer.Argument(...)) -> None:
302
+ """Enable KEY[@VERSION]."""
303
+ toggle(ctx, handle, True)
304
+
305
+
306
+ def disable(ctx: typer.Context, handle: str = typer.Argument(...)) -> None:
307
+ """Disable KEY[@VERSION]."""
308
+ toggle(ctx, handle, False)
309
+
310
+
311
+ def label_def(ctx: typer.Context, handle: str = typer.Argument(...),
312
+ add: Optional[List[str]] = typer.Option(None, "--add"),
313
+ remove: Optional[List[str]] = typer.Option(None, "--remove")) -> None:
314
+ """Add/remove labels on KEY[@VERSION]."""
315
+ if not add and not remove:
316
+ spec.fail("Pass --add or --remove")
317
+ if set(add or []) & set(remove or []):
318
+ spec.fail("A label cannot be both added and removed")
319
+ cctx = require_auth(ctx.obj)
320
+ row = select(definitions(cctx), handle)
321
+ labels = list(dict.fromkeys([v for v in row.get("labels", []) if v not in (remove or [])] + (add or [])))
322
+ spec.validate_labels(labels)
323
+ _write.confirm(ctx.obj, f"Label {handle}", assume_yes=False)
324
+ data = api.cloud_request(cctx, "PATCH", f"{PATH}/{row['id']}", body={"labels": labels})
325
+ if ctx.obj.json:
326
+ output.emit_json(data or {**row, "labels": labels})
327
+ else:
328
+ cloud_output.evaluator({**row, **(data or {}), "labels": labels}, "Evaluator labels")
329
+
330
+
331
+ def instances(ctx: typer.Context) -> None:
332
+ """List managed evaluator instances."""
333
+ _cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluator-instances"), "Instances")
334
+
335
+
336
+ def schema(ctx: typer.Context) -> None:
337
+ """Schema profile. Needs evaluations:trigger and events:read."""
338
+ _cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluator-schema-profile"), "Schema profile")
339
+
340
+
341
+ def jobs(ctx: typer.Context) -> None:
342
+ """List evaluation jobs."""
343
+ _cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/evaluation-jobs"), "Evaluation jobs")
344
+
345
+
346
+ def backfill(ctx: typer.Context, frm: str = typer.Option(..., "--from"),
347
+ to: str = typer.Option(..., "--to"), key: Optional[str] = typer.Option(None, "--key"),
348
+ dry_run: bool = typer.Option(False, "--dry-run")) -> None:
349
+ """Score stored sessions an evaluator has not scored yet (at most 90 days).
350
+
351
+ Covers code, LLM judge and Jev evaluators (every enabled one, or only --key).
352
+ Only missing (session, evaluator) pairs are queued; --dry-run counts them.
353
+ Needs evaluations:trigger.
354
+ """
355
+ try:
356
+ frm = dates._validate_iso(frm, "--from")
357
+ to = dates._validate_iso(to, "--to")
358
+ start = datetime.fromisoformat(frm.replace("Z", "+00:00"))
359
+ end = datetime.fromisoformat(to.replace("Z", "+00:00"))
360
+ except ValueError as exc:
361
+ spec.fail(str(exc))
362
+ if end <= start or end - start > timedelta(days=90):
363
+ spec.fail("Backfill needs an increasing range of at most 90 days")
364
+ if not dry_run:
365
+ output.warn("Judge and Jev evaluators in the backfill spend the org's model budget; --dry-run counts the work first.")
366
+ _write.confirm(ctx.obj, "Backfill evaluations", assume_yes=dry_run)
367
+ _cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "POST", "/api/evaluations/backfill",
368
+ body={"from": dates._iso(start), "to": dates._iso(end), "eval_key": key, "dry_run": dry_run}), "Evaluation backfill")
369
+
370
+
371
+ def register(app: typer.Typer) -> None:
372
+ group = typer.Typer(no_args_is_help=True, rich_markup_mode="markdown", help="Author, test and deploy code, LLM-judge and Jev evaluators.", context_settings={"help_option_names": ["-h", "--help"]})
373
+ for name, fn in (
374
+ ("list", list_defs), ("show", show), ("create", create), ("deploy", deploy),
375
+ ("draft", draft), ("dry-run", dry_run), ("enable", enable), ("disable", disable),
376
+ ("label", label_def), ("instances", instances), ("schema", schema), ("jobs", jobs),
377
+ ("backfill", backfill),
378
+ ):
379
+ group.command(name, epilog=GLOBALS_EPILOG)(fn)
380
+ app.add_typer(group, name="evaluators")
@@ -70,6 +70,8 @@ def events(
70
70
  * `fp --json events --session-id run-001 --all` — a run's full timeline: event types + summaries (light, fast)
71
71
  * `fp --json events --full --session-id run-001 --all | jq '.events[].payload'` — that run's raw payloads (full feed, bounded to one run)
72
72
  """
73
+ if ctx.invoked_subcommand:
74
+ return
73
75
  state: AppState = ctx.obj
74
76
  cctx = require_auth(state)
75
77
  frm, to = resolve_dates(since, ts_from, ts_to)
@@ -155,5 +157,16 @@ def events(
155
157
  output.recheck_filters_hint(active_filters)
156
158
 
157
159
 
160
+ def latency(ctx: typer.Context, since: Optional[str] = typer.Option(None, "--since")) -> None:
161
+ """Aggregate event latency."""
162
+ from . import _cloud
163
+ frm, to = resolve_dates(since, None, None)
164
+ _cloud.emit(ctx.obj, api.cloud_request(require_auth(ctx.obj), "GET", "/api/events/latency_aggregate",
165
+ params={"ts_from": frm, "ts_to": to}), "Event latency")
166
+
167
+
158
168
  def register(app: typer.Typer) -> None:
159
- app.command("events", epilog=GLOBALS_EPILOG)(events)
169
+ group = typer.Typer(invoke_without_command=True, rich_markup_mode="markdown", context_settings={"help_option_names": ["-h", "--help"]})
170
+ group.callback(epilog=GLOBALS_EPILOG)(events)
171
+ group.command("latency", epilog=GLOBALS_EPILOG)(latency)
172
+ app.add_typer(group, name="events")