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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +2 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__167mipqmjprq5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0b78b0j8iyv6l._.js → [root-of-the-server]__1vofkb2oq7p7e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1a3tubds7s7ow._.js → _0ouu886szh6yj._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1d98fyfrcaqhy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- 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
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589gp4p60j._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me658zqmh_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{1b32-afqacaen.js → 0el9n3h9_zp5m.js} +1 -1
- package/.next/standalone/.next/static/chunks/0k7g_8x3o6gd-.js +1 -0
- package/.next/standalone/.next/static/chunks/{42e8zab314654.js → 0umzct31duk5g.js} +1 -1
- package/.next/standalone/.next/static/chunks/{24w3x2da9sk49.js → 0wobqgosirs8p.js} +1 -1
- package/.next/standalone/.next/static/chunks/12rug3jilwqzs.js +1 -0
- package/.next/standalone/.next/static/chunks/{2-tf6_7u623kd.js → 1kduj74-715jh.js} +1 -1
- package/.next/standalone/.next/static/chunks/2701amhrbsbot.js +1 -0
- package/.next/standalone/.next/static/chunks/{18433qz-8njv3.js → 27cavoc8paelg.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0_zfad4co-t2z.js → 2ve23a7keupjw.js} +1 -1
- package/.next/standalone/.next/static/chunks/3bv6xaoitjpnh.js +6 -0
- package/.next/standalone/app/actions/update-jev-config.ts +5 -3
- package/.next/standalone/app/settings/jev-panel.tsx +1 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +28 -18
- package/.next/standalone/fp-cloud-cli/README.md +127 -9
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +15 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +199 -34
- package/.next/standalone/fp-cloud-cli/fp_cli/cloud_output.py +523 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_cloud.py +45 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +3 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/account_cmds.py +171 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +80 -7
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +23 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/contracts_cmds.py +67 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/dashboards_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evaluators_cmds.py +380 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +14 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +12 -216
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +82 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +4 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +17 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +143 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +70 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sentiment_cmds.py +49 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +95 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +21 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +14 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +7 -53
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +3 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/evaluator_spec.py +266 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +189 -152
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +21 -24
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +124 -23
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +2 -1
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +173 -22
- package/.next/standalone/fp-cloud-cli/tests/fake_cloud_smoke.py +312 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_account.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_agent.py +253 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_human_views.py +287 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +32 -197
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_observability.py +116 -0
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_transport.py +141 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +3 -3
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +3 -3
- package/.next/standalone/fp-cloud-cli/tests/test_evaluator_authoring.py +449 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +15 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +1 -1
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +37 -37
- package/.next/standalone/fp-cloud-cli/tests/test_round4_behaviour.py +144 -0
- package/.next/standalone/fp-cloud-cli/tests/test_screen_language.py +80 -0
- package/.next/standalone/fp-cloud-cli/tests/test_session_reevaluate.py +33 -0
- package/.next/standalone/fp-cloud-cli/tests/test_staging_parity.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +1 -1
- package/.next/standalone/hermes-plugin/__init__.py +11 -0
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/package.json +11 -10
- package/.next/standalone/sdk/python/uv.lock +31 -8
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package-lock.json +68 -176
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-4/package.json +2 -2
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-5/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-6/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/ai-7/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package-lock.json +98 -115
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-0.3/package.json +3 -3
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-1/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/langchain-dup-core/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package-lock.json +149 -171
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.11/package.json +2 -2
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package-lock.json +4 -4
- package/.next/standalone/sdk/typescript/integration/fixtures/llamaindex-0.12/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package-lock.json +25 -25
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-0/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/mastra-1/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +10 -10
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package.json +1 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package-lock.json +7 -7
- package/.next/standalone/sdk/typescript/integration/fixtures/runtimes/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +1760 -638
- package/dist/worker.mjs +426 -76
- package/hermes-plugin/__init__.py +11 -0
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +11 -10
- package/src/audit/open-browser.ts +24 -7
- package/src/hooks/agent-roster.ts +9 -1
- package/src/hooks/agent-skill.ts +206 -0
- package/src/hooks/cloud-connection.ts +27 -51
- package/src/hooks/cloud-introspect.ts +5 -2
- package/src/hooks/cloud-login.ts +248 -0
- package/src/hooks/cloud-managed-policies.ts +54 -4
- package/src/hooks/codex-system-hooks.ts +10 -4
- package/src/hooks/config-status.ts +9 -3
- package/src/hooks/configure-wizard.ts +356 -32
- package/src/hooks/delivery-health.ts +47 -2
- package/src/hooks/effective-reviewers.ts +14 -0
- package/src/hooks/fp-config.ts +19 -0
- package/src/hooks/fp-home.ts +1 -1
- package/src/hooks/handler.ts +43 -5
- package/src/hooks/hook-activity-store.ts +7 -1
- package/src/hooks/integrations.ts +18 -3
- package/src/hooks/jev-activity.ts +117 -1
- package/src/hooks/jev-cli.ts +7 -9
- package/src/hooks/manager.ts +80 -46
- package/src/hooks/policy-evaluator.ts +17 -0
- package/src/hooks/policy-reviewability.ts +4 -2
- package/src/hooks/semantic/cloud-jev.ts +219 -28
- package/src/hooks/semantic/combine.ts +26 -1
- package/src/hooks/semantic/intent.ts +127 -24
- package/src/hooks/semantic/jev-client.ts +1 -1
- package/src/hooks/semantic/jev-config.ts +6 -27
- package/src/hooks/semantic/jev-review.ts +111 -4
- package/src/hooks/tui.ts +280 -0
- package/src/hooks/uninstall-cli.ts +12 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +0 -3
- package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +0 -1
- package/.next/standalone/.next/static/chunks/114hbolvrv94e.js +0 -1
- package/.next/standalone/.next/static/chunks/1p-hsi58i-6h0.js +0 -6
- package/.next/standalone/.next/static/chunks/3-pku9qwv1yfu.js +0 -1
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{xq8UgF5yFxty1VWxY2hyn → D7LY1YSrw96ZxKSpxxrFw}/_ssgManifest.js +0 -0
|
@@ -31,6 +31,7 @@ from .errors import (
|
|
|
31
31
|
ApiError,
|
|
32
32
|
AuthError,
|
|
33
33
|
ForbiddenError,
|
|
34
|
+
FpCliError,
|
|
34
35
|
KeyModeUnsupportedError,
|
|
35
36
|
NetworkError,
|
|
36
37
|
NotFoundError,
|
|
@@ -134,6 +135,14 @@ _V1_MECHANICAL_FAMILIES = frozenset(
|
|
|
134
135
|
"alerts",
|
|
135
136
|
"audits",
|
|
136
137
|
"evaluations",
|
|
138
|
+
"evaluator-definitions",
|
|
139
|
+
"evaluator-instances",
|
|
140
|
+
"evaluator-schema-profile",
|
|
141
|
+
"evaluation-jobs",
|
|
142
|
+
"dashboards",
|
|
143
|
+
"otel",
|
|
144
|
+
"offers",
|
|
145
|
+
"team-trial",
|
|
137
146
|
"events",
|
|
138
147
|
"issues",
|
|
139
148
|
"keys",
|
|
@@ -152,6 +161,7 @@ _V1_MECHANICAL_FAMILIES = frozenset(
|
|
|
152
161
|
# family rule, which is why this must stay exact-match.
|
|
153
162
|
_V1_RENAMED = {
|
|
154
163
|
"/api/evaluations/score-keys": "/v1/evaluations/score_keys",
|
|
164
|
+
"/api/evaluations/metric-keys": "/v1/evaluations/metric_keys",
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
# Families with no `/v1` route, and why — the message a user actually sees.
|
|
@@ -173,6 +183,8 @@ _V1_NO_EQUIVALENT = {
|
|
|
173
183
|
"cloud-managed policies are an operator surface — the fleet routes are "
|
|
174
184
|
"deliberately absent from /v1, which is internet-facing"
|
|
175
185
|
),
|
|
186
|
+
"integrations": "dashboard integration routes need a signed-in session",
|
|
187
|
+
"orgs": "organization creation needs a signed-in user",
|
|
176
188
|
}
|
|
177
189
|
|
|
178
190
|
|
|
@@ -375,6 +387,40 @@ def _required_permission(response: httpx.Response) -> Optional[str]:
|
|
|
375
387
|
return None
|
|
376
388
|
|
|
377
389
|
|
|
390
|
+
def _unknown_route_response(response: httpx.Response) -> Optional[bool]:
|
|
391
|
+
"""Use explicit 404 details first; an empty/generic reply is ambiguous."""
|
|
392
|
+
try:
|
|
393
|
+
data = response.json()
|
|
394
|
+
except ValueError:
|
|
395
|
+
return None
|
|
396
|
+
if not isinstance(data, dict):
|
|
397
|
+
return None
|
|
398
|
+
code = data.get("code") or data.get("error_code") or data.get("error") or ""
|
|
399
|
+
if isinstance(code, dict):
|
|
400
|
+
code = code.get("code", "")
|
|
401
|
+
code = str(code).lower()
|
|
402
|
+
if code in ("unknown_route", "route_not_found", "unknown_endpoint", "endpoint_not_found"):
|
|
403
|
+
return True
|
|
404
|
+
if code in ("record_not_found", "resource_not_found", "entity_not_found", "session_not_found", "query_not_found"):
|
|
405
|
+
return False
|
|
406
|
+
message = _extract_error(response)
|
|
407
|
+
if not message:
|
|
408
|
+
return None
|
|
409
|
+
lower = message.lower().strip().rstrip(".")
|
|
410
|
+
if re.search(
|
|
411
|
+
r"\b(?:unknown|unrecognized|unsupported|missing|no such)\s+(?:api\s+)?(?:route|endpoint)\b"
|
|
412
|
+
r"|\b(?:route|endpoint)\b.*\b(?:not found|not available|does not exist|absent|unknown)\b"
|
|
413
|
+
r"|\bcannot (?:get|post|put|patch|delete)\s+/",
|
|
414
|
+
lower,
|
|
415
|
+
):
|
|
416
|
+
return True
|
|
417
|
+
if lower in ("not found", "not_found", "404", "404 not found"):
|
|
418
|
+
return None
|
|
419
|
+
# A specific response ("session not found", "offer expired", etc.) came
|
|
420
|
+
# from a handler, rather than the router. Do not replace its useful detail.
|
|
421
|
+
return False
|
|
422
|
+
|
|
423
|
+
|
|
378
424
|
def _raise_for_status(response: httpx.Response, ctx: ClientContext) -> None:
|
|
379
425
|
key_mode = ctx.auth_mode is AuthMode.API_KEY
|
|
380
426
|
if response.status_code < 400:
|
|
@@ -474,7 +520,9 @@ def _raise_for_status(response: httpx.Response, ctx: ClientContext) -> None:
|
|
|
474
520
|
request_id=request_id,
|
|
475
521
|
hint="point --base-url at the server itself, e.g. http://localhost:8080",
|
|
476
522
|
)
|
|
477
|
-
|
|
523
|
+
error = NotFoundError(message or "Not found.", request_id=request_id)
|
|
524
|
+
error.route_unknown = _unknown_route_response(response)
|
|
525
|
+
raise error
|
|
478
526
|
if response.status_code == 429:
|
|
479
527
|
retry_after = response.headers.get("retry-after")
|
|
480
528
|
wait = f" Retry after {retry_after}s." if retry_after else " Please wait a moment and try again."
|
|
@@ -606,6 +654,121 @@ def _delete(ctx: ClientContext, path: str) -> Any:
|
|
|
606
654
|
return _request_json(ctx, "DELETE", path)
|
|
607
655
|
|
|
608
656
|
|
|
657
|
+
def cloud_request(
|
|
658
|
+
ctx: ClientContext, method: str, path: str, *,
|
|
659
|
+
body: Any = None, params: Optional[Dict[str, Any]] = None,
|
|
660
|
+
) -> Any:
|
|
661
|
+
"""New Cloud capabilities, with a predictable older-deployment failure.
|
|
662
|
+
|
|
663
|
+
Existing callers retain their response and error contracts. Paths still pass
|
|
664
|
+
through the credential and identifier guards; this is not a second transport.
|
|
665
|
+
"""
|
|
666
|
+
try:
|
|
667
|
+
if method == "GET":
|
|
668
|
+
return _get_json(ctx, path, params)
|
|
669
|
+
return _request_json(ctx, method, path, json_body=body, params=params)
|
|
670
|
+
except (NotFoundError, ApiError) as exc:
|
|
671
|
+
if isinstance(exc, ApiError) and exc.status != 404:
|
|
672
|
+
raise
|
|
673
|
+
raise _cloud_not_found(exc, path, body=body, params=params) from exc
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _cloud_not_found(exc: FpCliError, path: str, *, body=None, params=None) -> NotFoundError:
|
|
677
|
+
"""Classify ambiguous 404s from the request; never claim a record is a route.
|
|
678
|
+
|
|
679
|
+
Top-level collections and named capability endpoints have no record segment.
|
|
680
|
+
Scoped collections (/dashboards/ID/tiles) and actions on records default to a
|
|
681
|
+
missing record unless the response explicitly says the route is unknown.
|
|
682
|
+
"""
|
|
683
|
+
route_unknown = getattr(exc, "route_unknown", None)
|
|
684
|
+
if isinstance(exc, ApiError):
|
|
685
|
+
# The transport only raises HTTP 404 ApiError for an HTML front door.
|
|
686
|
+
route_unknown = True
|
|
687
|
+
if route_unknown is None:
|
|
688
|
+
segments = path.removeprefix("/api/").split("/")
|
|
689
|
+
family = segments[0]
|
|
690
|
+
capability = len(segments) == 1 or (
|
|
691
|
+
family == "agent" and len(segments) == 2
|
|
692
|
+
or family == "otel" and segments == ["otel", "status"]
|
|
693
|
+
or family == "offers" and segments[1:] in (["me"], ["claim"])
|
|
694
|
+
or family == "team-trial" and segments == ["team-trial", "me"]
|
|
695
|
+
or family == "integrations" and segments == ["integrations", "slack"]
|
|
696
|
+
or family == "events" and segments in (
|
|
697
|
+
["events", "summary"], ["events", "latency_aggregate"],
|
|
698
|
+
["events", "sentiment"], ["events", "sentiment", "messages"],
|
|
699
|
+
["events", "sentiment", "sessions"],
|
|
700
|
+
)
|
|
701
|
+
or family == "evaluator-definitions" and segments == ["evaluator-definitions", "dry-run"]
|
|
702
|
+
or family == "evaluations" and segments == ["evaluations", "backfill"]
|
|
703
|
+
or family == "queries" and segments[1:] in (["run"], ["schema"])
|
|
704
|
+
or family == "audits" and segments == ["audits", "contracts"]
|
|
705
|
+
or family == "alerts" and segments == ["alerts", "recipients"]
|
|
706
|
+
or family == "users" and segments == ["users", "defaults"]
|
|
707
|
+
or family == "usage" and segments == ["usage", "windows"]
|
|
708
|
+
or family == "settings" and segments == ["settings", "model-context-windows"]
|
|
709
|
+
or family == "settings" and len(segments) == 2
|
|
710
|
+
)
|
|
711
|
+
# Some collection operations name their target in a query or body.
|
|
712
|
+
request_body = body if isinstance(body, dict) else {}
|
|
713
|
+
record_selected = (
|
|
714
|
+
(params or {}).get("session_id") or (params or {}).get("model")
|
|
715
|
+
or request_body.get("query_id") or request_body.get("callId")
|
|
716
|
+
or request_body.get("session_ids") or request_body.get("offer_key")
|
|
717
|
+
or request_body.get("conversationId")
|
|
718
|
+
)
|
|
719
|
+
route_unknown = capability and not record_selected
|
|
720
|
+
if route_unknown:
|
|
721
|
+
return NotFoundError(
|
|
722
|
+
"not available on this FailproofAI Cloud yet",
|
|
723
|
+
hint="upgrade your Cloud deployment to use this route",
|
|
724
|
+
request_id=exc.request_id,
|
|
725
|
+
)
|
|
726
|
+
message = exc.message
|
|
727
|
+
if message.lower().strip().rstrip(".") in (
|
|
728
|
+
"not found", "not_found", "record_not_found", "resource_not_found",
|
|
729
|
+
"entity_not_found", "session_not_found", "query_not_found",
|
|
730
|
+
):
|
|
731
|
+
message = f"Record not found at {path}"
|
|
732
|
+
return NotFoundError(message, request_id=exc.request_id)
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def import_events_file(
|
|
736
|
+
ctx: ClientContext, data: bytes, params: Dict[str, Any],
|
|
737
|
+
) -> Any:
|
|
738
|
+
"""POST /api/events/import — one file's bytes (gzip accepted), as the dashboard's
|
|
739
|
+
import dialog sends it. ``params``: format, path, environment, dry_run, existing_before."""
|
|
740
|
+
clean = {k: v for k, v in params.items() if v is not None} or None
|
|
741
|
+
url = _path(ctx, "/api/events/import")
|
|
742
|
+
try:
|
|
743
|
+
with _client(ctx) as client:
|
|
744
|
+
response = client.request(
|
|
745
|
+
"POST", url, content=data, params=clean,
|
|
746
|
+
headers={"Content-Type": "application/octet-stream"},
|
|
747
|
+
)
|
|
748
|
+
except httpx.RequestError as exc:
|
|
749
|
+
raise NetworkError(
|
|
750
|
+
f"Cannot reach FailproofAI Cloud at {ctx.base_url}: {exc}",
|
|
751
|
+
request_id=_sent_request_id(exc),
|
|
752
|
+
)
|
|
753
|
+
_raise_for_status(response, ctx)
|
|
754
|
+
if not response.content:
|
|
755
|
+
return {}
|
|
756
|
+
try:
|
|
757
|
+
return response.json()
|
|
758
|
+
except ValueError:
|
|
759
|
+
raise ApiError("The dashboard returned an invalid import response.")
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def cloud_stream(ctx: ClientContext, path: str, body: Dict[str, Any]) -> Iterator[Dict[str, Any]]:
|
|
763
|
+
"""Dashboard SSE with the same additive-capability 404 contract."""
|
|
764
|
+
try:
|
|
765
|
+
yield from _stream_sse(ctx, path, body)
|
|
766
|
+
except (NotFoundError, ApiError) as exc:
|
|
767
|
+
if isinstance(exc, ApiError) and exc.status != 404:
|
|
768
|
+
raise
|
|
769
|
+
raise _cloud_not_found(exc, path, body=body) from exc
|
|
770
|
+
|
|
771
|
+
|
|
609
772
|
# --- Auth / identity --------------------------------------------------------
|
|
610
773
|
|
|
611
774
|
|
|
@@ -793,6 +956,10 @@ _FACET_PATHS = {
|
|
|
793
956
|
# Evaluation score keys (a distinct endpoint, not /api/events) — the source for
|
|
794
957
|
# the sessions-page score-filter dropdown; needs `evaluations:read`.
|
|
795
958
|
"score_filters": "/api/evaluations/score-keys",
|
|
959
|
+
# Evaluation-side facets: environments with evaluation results, and metric
|
|
960
|
+
# keys (the server returns {name, unit, count}; `list` shows the names).
|
|
961
|
+
"eval_environments": "/api/evaluations/environments",
|
|
962
|
+
"metric_keys": "/api/evaluations/metric-keys",
|
|
796
963
|
}
|
|
797
964
|
FACET_KINDS = tuple(_FACET_PATHS.keys())
|
|
798
965
|
|
|
@@ -800,7 +967,9 @@ FACET_KINDS = tuple(_FACET_PATHS.keys())
|
|
|
800
967
|
def list_facet(ctx: ClientContext, kind: str) -> List[str]:
|
|
801
968
|
"""GET /api/events/<kind> — distinct facet values (a bare JSON array)."""
|
|
802
969
|
data = _get_json(ctx, _FACET_PATHS[kind])
|
|
803
|
-
|
|
970
|
+
if not isinstance(data, list):
|
|
971
|
+
return []
|
|
972
|
+
return [str(x.get("name", "")) if isinstance(x, dict) else str(x) for x in data]
|
|
804
973
|
|
|
805
974
|
|
|
806
975
|
def get_usage(ctx: ClientContext) -> Dict[str, Any]:
|
|
@@ -1195,20 +1364,28 @@ def list_incidents(
|
|
|
1195
1364
|
state: Optional[str] = None,
|
|
1196
1365
|
alert_id: Optional[str] = None,
|
|
1197
1366
|
limit: Optional[int] = None,
|
|
1367
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
1198
1368
|
) -> List[Incident]:
|
|
1199
1369
|
"""GET /api/issues (or /api/alerts/{id}/issues when alert_id given). The
|
|
1200
1370
|
alert-scoped path honours ``state``/``limit`` too — pass them so the filters aren't silently
|
|
1201
1371
|
dropped on that path (``_get_json`` omits None params)."""
|
|
1202
|
-
if
|
|
1372
|
+
extra = {k: v for k, v in (filters or {}).items() if v is not None}
|
|
1373
|
+
if alert_id and not extra:
|
|
1203
1374
|
data = _get_json(ctx, f"/api/alerts/{alert_id}/issues", {"state": state, "limit": limit})
|
|
1375
|
+
elif alert_id:
|
|
1376
|
+
# The board's own filters live on /issues, which takes alert_id as one of them.
|
|
1377
|
+
data = _get_json(ctx, "/api/issues", {"state": state, "limit": limit, "alert_id": alert_id, **extra})
|
|
1204
1378
|
else:
|
|
1205
|
-
data = _get_json(ctx, "/api/issues", {"state": state, "limit": limit})
|
|
1379
|
+
data = _get_json(ctx, "/api/issues", {"state": state, "limit": limit, **extra})
|
|
1206
1380
|
return [Incident.from_dict(i) for i in (data if isinstance(data, list) else [])]
|
|
1207
1381
|
|
|
1208
1382
|
|
|
1209
|
-
def count_incidents(
|
|
1383
|
+
def count_incidents(
|
|
1384
|
+
ctx: ClientContext, *, state: Optional[str] = None, filters: Optional[Dict[str, Any]] = None,
|
|
1385
|
+
) -> int:
|
|
1210
1386
|
"""GET /api/issues/count — {count}."""
|
|
1211
|
-
|
|
1387
|
+
extra = {k: v for k, v in (filters or {}).items() if v is not None}
|
|
1388
|
+
data = _get_json(ctx, "/api/issues/count", {"state": state, **extra})
|
|
1212
1389
|
if not isinstance(data, dict):
|
|
1213
1390
|
return 0
|
|
1214
1391
|
try:
|
|
@@ -1532,6 +1709,8 @@ def agent_chat_oneshot(
|
|
|
1532
1709
|
conversation_id: Optional[str] = None,
|
|
1533
1710
|
page_context: Optional[str] = None,
|
|
1534
1711
|
model: Optional[str] = None,
|
|
1712
|
+
on_ask_user: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
1713
|
+
on_artifact: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
1535
1714
|
) -> Dict[str, Any]:
|
|
1536
1715
|
"""POST /api/agent/chat — accumulate the streamed answer.
|
|
1537
1716
|
|
|
@@ -1551,20 +1730,32 @@ def agent_chat_oneshot(
|
|
|
1551
1730
|
body["pageContext"] = page_context
|
|
1552
1731
|
if model:
|
|
1553
1732
|
body["model"] = model
|
|
1733
|
+
if on_artifact:
|
|
1734
|
+
body["pageActions"] = [{"action": "open_artifact"}, {"action": "home"}]
|
|
1554
1735
|
parts: List[str] = []
|
|
1555
1736
|
tools: List[str] = []
|
|
1556
1737
|
interrupted = False
|
|
1557
1738
|
error: Optional[str] = None
|
|
1558
|
-
for ev in
|
|
1739
|
+
for ev in cloud_stream(ctx, "/api/agent/chat", body):
|
|
1559
1740
|
kind = ev.get("type")
|
|
1560
1741
|
if kind == "text-delta":
|
|
1561
1742
|
parts.append(str(ev.get("text", "")))
|
|
1562
1743
|
elif kind == "tool-start":
|
|
1563
1744
|
tools.append(str(ev.get("tool", "")))
|
|
1564
1745
|
elif kind == "ask-user":
|
|
1746
|
+
if on_ask_user and ev.get("callId"):
|
|
1747
|
+
on_ask_user(ev)
|
|
1748
|
+
continue
|
|
1565
1749
|
interrupted = True
|
|
1566
1750
|
error = str(ev.get("question") or "the assistant needs interactive input")
|
|
1567
1751
|
break
|
|
1752
|
+
elif kind == "ui-action" and on_artifact:
|
|
1753
|
+
if ev.get("action") == "open_artifact" and isinstance(ev.get("artifact"), dict):
|
|
1754
|
+
on_artifact(ev["artifact"])
|
|
1755
|
+
elif ev.get("action") == "home" and isinstance(ev.get("home"), dict):
|
|
1756
|
+
for block in ev["home"].get("blocks", []):
|
|
1757
|
+
if isinstance(block, dict):
|
|
1758
|
+
on_artifact(block)
|
|
1568
1759
|
elif kind == "error":
|
|
1569
1760
|
error = str(ev.get("message", "assistant error"))
|
|
1570
1761
|
elif kind == "done":
|
|
@@ -1785,42 +1976,16 @@ def deploy_policies(
|
|
|
1785
1976
|
ctx: ClientContext,
|
|
1786
1977
|
machine_id: str,
|
|
1787
1978
|
policies: Sequence[PolicyRef],
|
|
1788
|
-
jev_mode: Optional[str] = None,
|
|
1789
1979
|
) -> Deployment:
|
|
1790
1980
|
"""PUT /api/enforcement/deployments/{id} — REPLACES the machine's whole set.
|
|
1791
1981
|
|
|
1792
|
-
|
|
1793
|
-
server reads an ABSENT ``jevMode`` as "keep the machine's current one", so a
|
|
1794
|
-
deploy that says nothing about Jev never changes it. ``local`` stops
|
|
1795
|
-
FailproofAI Cloud overriding the machine's own mode.
|
|
1982
|
+
Never carries a Jev mode: FailproofAI Cloud derives it from the set.
|
|
1796
1983
|
"""
|
|
1797
1984
|
path = f"/api/enforcement/deployments/{machine_id}"
|
|
1798
1985
|
body: Dict[str, Any] = {"policies": [p.to_dict() for p in policies]}
|
|
1799
|
-
if jev_mode is not None:
|
|
1800
|
-
body["jevMode"] = jev_mode
|
|
1801
1986
|
return Deployment.from_dict(_request_json(ctx, "PUT", path, json_body=body) or {})
|
|
1802
1987
|
|
|
1803
1988
|
|
|
1804
|
-
def set_jev_mode(ctx: ClientContext, machine_id: str, jev_mode: str) -> Tuple[Deployment, bool]:
|
|
1805
|
-
"""PUT /api/enforcement/deployments/{id}/jev-mode — change ONLY the Jev mode.
|
|
1806
|
-
|
|
1807
|
-
``jev_mode`` is ``off|observe|enforce|local`` (``local`` stops FailproofAI
|
|
1808
|
-
Cloud overriding the machine's own mode). Unlike a deploy, this never
|
|
1809
|
-
touches the machine's policy set: a deploy is a full replace of the set, and
|
|
1810
|
-
the set a client can read leaves out disabled policies' assignments, so a
|
|
1811
|
-
mode change sent as a deploy deleted them — and reverted any edit made since
|
|
1812
|
-
the set was read. It still mints a new generation, which is how the machine
|
|
1813
|
-
picks the mode up.
|
|
1814
|
-
|
|
1815
|
-
Returns the deployment and whether anything changed: setting the mode the
|
|
1816
|
-
machine already has changes nothing and mints nothing (``changed: false``).
|
|
1817
|
-
A machine with no deployment is a 404 — a deploy is how it gets its first.
|
|
1818
|
-
"""
|
|
1819
|
-
path = f"/api/enforcement/deployments/{machine_id}/jev-mode"
|
|
1820
|
-
data = _request_json(ctx, "PUT", path, json_body={"jevMode": jev_mode}) or {}
|
|
1821
|
-
return Deployment.from_dict(data), bool(data.get("changed", True))
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
1989
|
def deployment_history(ctx: ClientContext, machine_id: str) -> List[Dict[str, Any]]:
|
|
1825
1990
|
"""GET /api/enforcement/deployments/{id}/history — every generation, newest first."""
|
|
1826
1991
|
path = f"/api/enforcement/deployments/{machine_id}/history"
|