failproofai 1.0.1 → 1.0.2-beta.0
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 +6 -6
- 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 +2 -2
- 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 +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- 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 +2 -2
- 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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- 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 +5 -4
- 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 +2 -2
- 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 +4 -3
- 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 +4 -4
- 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 +4 -4
- 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 +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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- 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 +66 -22
- package/.next/standalone/.next/server/app/policies/page.js +5 -4
- 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 +2 -2
- 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 +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 +2 -2
- 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 +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- 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/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
- package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- 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/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
- package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
- package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
- package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
- package/.next/standalone/SECURITY.md +5 -4
- package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
- package/.next/standalone/app/actions/pack-actions.ts +153 -0
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
- package/.next/standalone/app/policies/hooks-client.tsx +320 -14
- package/.next/standalone/components/navbar.tsx +2 -28
- package/.next/standalone/components/reach-developers.tsx +8 -1
- package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
- package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
- package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
- package/.next/standalone/docs-old/i18n/README.de.md +1 -0
- package/.next/standalone/docs-old/i18n/README.es.md +1 -0
- package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.he.md +1 -0
- package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.it.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
- package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
- package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
- package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
- package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
- package/.next/standalone/fp-cloud-cli/README.md +259 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
- package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
- package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
- package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
- package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
- package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
- package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
- package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
- package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
- package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
- package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
- package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
- package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
- package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
- package/.next/standalone/package.json +29 -22
- package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
- package/.next/standalone/sdk/python/LICENSE +42 -0
- package/.next/standalone/sdk/python/MANIFEST.in +21 -0
- package/.next/standalone/sdk/python/README.md +466 -0
- package/.next/standalone/sdk/python/docs/README.md +249 -0
- package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
- package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
- package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
- package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
- package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
- package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
- package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
- package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
- package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
- package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
- package/.next/standalone/sdk/python/pyproject.toml +92 -0
- package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
- package/.next/standalone/sdk/python/skill/references/events.md +234 -0
- package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
- package/.next/standalone/sdk/python/skill/references/install.md +100 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
- package/.next/standalone/sdk/python/tests/__init__.py +0 -0
- package/.next/standalone/sdk/python/tests/conftest.py +169 -0
- package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
- package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
- package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
- package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
- package/.next/standalone/sdk/python/tests/test_context.py +410 -0
- package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
- package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
- package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
- package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
- package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
- package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
- package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
- package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
- package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
- package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
- package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
- package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
- package/.next/standalone/sdk/python/uv.lock +6005 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +77 -21
- package/bin/failproofai.mjs +145 -11
- package/dist/cli.mjs +4482 -2205
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2399 -1978
- package/package.json +29 -22
- package/policy-pack/SHA256SUMS +2 -0
- package/policy-pack/failproofai-pack.json +732 -0
- package/policy-pack/failproofai-pack.mjs +2017 -0
- package/scripts/build-policy-pack.mjs +121 -0
- package/scripts/changelog-section.py +77 -0
- package/scripts/prune-standalone.mjs +3 -0
- package/scripts/python-version.py +232 -0
- package/scripts/translate-docs/cli.ts +21 -0
- package/scripts/translate-docs/mintlify-nav.ts +85 -13
- package/scripts/translate-docs/translator.ts +66 -11
- package/src/audit/cache.ts +40 -3
- package/src/audit/cli.ts +1 -1
- package/src/audit/index.ts +1 -1
- package/src/audit/replay.ts +89 -3
- package/src/audit/report.ts +5 -5
- package/src/audit/schedule-cli.ts +49 -53
- package/src/hooks/builtin-policies.ts +217 -579
- package/src/hooks/cloud-enrollment-cli.ts +66 -27
- package/src/hooks/cloud-managed-policies.ts +6 -1
- package/src/hooks/configure-wizard.ts +1 -1
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +26 -2
- package/src/hooks/handler.ts +181 -11
- package/src/hooks/harness-cli.ts +79 -25
- package/src/hooks/hook-activity-store.ts +13 -3
- package/src/hooks/install-prompt.ts +25 -3
- package/src/hooks/manager.ts +418 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +505 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +344 -0
- package/src/hooks/pack-store.ts +930 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- package/src/hooks/policy-presets.ts +4 -2
- package/src/hooks/policy-registry.ts +8 -2
- package/src/hooks/policy-types.ts +26 -0
- package/src/hooks/session-pause-cli.ts +33 -9
- package/src/hooks/tui.ts +570 -0
- package/src/hooks/types.ts +15 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
- package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"""Tests for session() / agent() / tool_call().
|
|
2
|
+
|
|
3
|
+
The exception table in `_scopes.agent` is the contract most likely to be broken
|
|
4
|
+
by a well-meaning refactor, so every row of it has a test *and* every test
|
|
5
|
+
asserts event ORDER, not just presence: the dashboard closes the agent span at
|
|
6
|
+
`agent_end`, so an `error` emitted after it is attributed to nothing.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
import failproofai_sdk
|
|
15
|
+
import failproofai_sdk._context as _context
|
|
16
|
+
import failproofai_sdk._runtime as _runtime
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def indexes(entries, event_type):
|
|
20
|
+
return [i for i, e in enumerate(entries) if e["type"] == event_type]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def only(entries, event_type):
|
|
24
|
+
matches = [e for e in entries if e["type"] == event_type]
|
|
25
|
+
assert len(matches) == 1, f"expected one {event_type}, got {len(matches)}"
|
|
26
|
+
return matches[0]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# session()
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
def test_session_emits_no_events(events):
|
|
34
|
+
with failproofai_sdk.session("s-1"):
|
|
35
|
+
pass
|
|
36
|
+
assert events.entries == []
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_session_binds_agent_id_without_emitting(events):
|
|
40
|
+
with failproofai_sdk.session("s-1", agent_id="worker"):
|
|
41
|
+
_runtime.event.agent_start()
|
|
42
|
+
entry = only(events.entries, "agent_start")
|
|
43
|
+
assert entry["session_id"] == "s-1"
|
|
44
|
+
assert entry["agent_id"] == "worker"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# agent() — happy path
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
def test_agent_brackets_the_run(events):
|
|
52
|
+
with failproofai_sdk.agent("planner", session_id="s-1", goal="find it"):
|
|
53
|
+
pass
|
|
54
|
+
assert events.types() == ["agent_start", "agent_end"]
|
|
55
|
+
start, end = events.entries
|
|
56
|
+
assert start["agent_id"] == "planner"
|
|
57
|
+
assert start["goal"] == "find it"
|
|
58
|
+
assert "parent_id" not in start
|
|
59
|
+
assert end["outcome"] == "success"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_agent_generates_a_session_id(events):
|
|
63
|
+
with failproofai_sdk.agent("planner") as ident:
|
|
64
|
+
assert len(ident.session_id) == 32
|
|
65
|
+
assert events.entries[0]["session_id"] == ident.session_id
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_agent_custom_outcome_and_summary(events):
|
|
69
|
+
with failproofai_sdk.agent("a", session_id="s", outcome="partial", summary="did half"):
|
|
70
|
+
pass
|
|
71
|
+
end = only(events.entries, "agent_end")
|
|
72
|
+
assert end["outcome"] == "partial"
|
|
73
|
+
assert end["summary"] == "did half"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_agent_extra_fields_land_on_agent_start(events):
|
|
77
|
+
with failproofai_sdk.agent("a", session_id="s", framework="langgraph", fw_node="retrieve"):
|
|
78
|
+
pass
|
|
79
|
+
start = only(events.entries, "agent_start")
|
|
80
|
+
assert start["framework"] == "langgraph"
|
|
81
|
+
assert start["fw_node"] == "retrieve"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_agent_default_id_is_main(events):
|
|
85
|
+
with failproofai_sdk.agent(session_id="s"):
|
|
86
|
+
pass
|
|
87
|
+
assert events.entries[0]["agent_id"] == "main"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
# Nesting and parent_id
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
def test_parent_ids_follow_the_stack(events):
|
|
95
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
96
|
+
with failproofai_sdk.agent("b"):
|
|
97
|
+
with failproofai_sdk.agent("c"):
|
|
98
|
+
assert failproofai_sdk.current().depth == 3
|
|
99
|
+
assert failproofai_sdk.current().parent_id == "b"
|
|
100
|
+
|
|
101
|
+
starts = [e for e in events.entries if e["type"] == "agent_start"]
|
|
102
|
+
assert [e["agent_id"] for e in starts] == ["a", "b", "c"]
|
|
103
|
+
assert "parent_id" not in starts[0]
|
|
104
|
+
assert starts[1]["parent_id"] == "a"
|
|
105
|
+
assert starts[2]["parent_id"] == "b"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_explicit_parent_none_forces_a_root_span(events):
|
|
109
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
110
|
+
with failproofai_sdk.agent("b", parent_id=None):
|
|
111
|
+
pass
|
|
112
|
+
start_b = [e for e in events.entries if e["type"] == "agent_start"][1]
|
|
113
|
+
assert "parent_id" not in start_b
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_explicit_parent_string_overrides(events):
|
|
117
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
118
|
+
with failproofai_sdk.agent("b", parent_id="somewhere-else"):
|
|
119
|
+
pass
|
|
120
|
+
start_b = [e for e in events.entries if e["type"] == "agent_start"][1]
|
|
121
|
+
assert start_b["parent_id"] == "somewhere-else"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_nested_agent_inherits_the_session(events):
|
|
125
|
+
with failproofai_sdk.agent("a", session_id="s-outer"):
|
|
126
|
+
with failproofai_sdk.agent("b"):
|
|
127
|
+
pass
|
|
128
|
+
assert {e["session_id"] for e in events.entries} == {"s-outer"}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_stack_unwinds_after_an_exception_through_three_scopes(events):
|
|
132
|
+
class Boom(Exception):
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
137
|
+
with failproofai_sdk.agent("b"):
|
|
138
|
+
with failproofai_sdk.agent("c"):
|
|
139
|
+
raise Boom("deep")
|
|
140
|
+
except Boom as exc:
|
|
141
|
+
assert str(exc) == "deep"
|
|
142
|
+
else:
|
|
143
|
+
pytest.fail("Boom was not raised")
|
|
144
|
+
|
|
145
|
+
assert failproofai_sdk.current().depth == 0
|
|
146
|
+
assert _context.snapshot() == (None, ())
|
|
147
|
+
# Each level reports the failure on its own span, innermost first.
|
|
148
|
+
assert events.types() == [
|
|
149
|
+
"agent_start", "agent_start", "agent_start",
|
|
150
|
+
"error", "agent_end",
|
|
151
|
+
"error", "agent_end",
|
|
152
|
+
"error", "agent_end",
|
|
153
|
+
]
|
|
154
|
+
ends = [e for e in events.entries if e["type"] == "agent_end"]
|
|
155
|
+
assert [e["agent_id"] for e in ends] == ["c", "b", "a"]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_stack_unwinds_even_when_emission_fails(events, monkeypatch):
|
|
159
|
+
real_agent_end = _runtime.event.agent_end
|
|
160
|
+
|
|
161
|
+
def explode(**kwargs):
|
|
162
|
+
if kwargs.get("agent_id") == "b":
|
|
163
|
+
raise RuntimeError("writer is down")
|
|
164
|
+
return real_agent_end(**kwargs)
|
|
165
|
+
|
|
166
|
+
monkeypatch.setattr(_runtime.event, "agent_end", explode)
|
|
167
|
+
|
|
168
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
169
|
+
with pytest.raises(RuntimeError, match="writer is down"):
|
|
170
|
+
with failproofai_sdk.agent("b"):
|
|
171
|
+
pass
|
|
172
|
+
assert failproofai_sdk.current().agent_id == "a"
|
|
173
|
+
assert failproofai_sdk.current().depth == 1
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_failed_agent_start_does_not_leave_a_frame(events):
|
|
177
|
+
with pytest.raises(ValueError, match="Reserved field"):
|
|
178
|
+
with failproofai_sdk.agent("a", session_id="s", timestamp="nope"):
|
|
179
|
+
pass
|
|
180
|
+
assert _context.snapshot() == (None, ())
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
# The exception table — one test per row, asserting order
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def test_no_exception_emits_only_agent_end(events):
|
|
188
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
189
|
+
pass
|
|
190
|
+
assert events.types() == ["agent_start", "agent_end"]
|
|
191
|
+
assert only(events.entries, "agent_end")["outcome"] == "success"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_exception_emits_error_before_agent_end(events):
|
|
195
|
+
try:
|
|
196
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
197
|
+
raise ValueError("nope")
|
|
198
|
+
except ValueError as exc:
|
|
199
|
+
assert str(exc) == "nope"
|
|
200
|
+
else:
|
|
201
|
+
pytest.fail("ValueError was not raised")
|
|
202
|
+
|
|
203
|
+
types = events.types()
|
|
204
|
+
assert types.index("error") < types.index("agent_end")
|
|
205
|
+
err = only(events.entries, "error")
|
|
206
|
+
assert err["error_type"] == "ValueError"
|
|
207
|
+
assert err["message"] == "nope"
|
|
208
|
+
assert "ValueError: nope" in err["traceback"]
|
|
209
|
+
end = only(events.entries, "agent_end")
|
|
210
|
+
# "failed", never "failure": only error|failed|timeout|rejected count as a
|
|
211
|
+
# failure server-side.
|
|
212
|
+
assert end["outcome"] == "failed"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _assert_failed_base_exception(events, expected_type):
|
|
216
|
+
types = events.types()
|
|
217
|
+
assert types.index("error") < types.index("agent_end")
|
|
218
|
+
assert only(events.entries, "agent_end")["outcome"] == "failed"
|
|
219
|
+
assert only(events.entries, "error")["error_type"] == expected_type.__name__
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_keyboard_interrupt_is_reported_as_failed(events):
|
|
223
|
+
try:
|
|
224
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
225
|
+
raise KeyboardInterrupt()
|
|
226
|
+
except KeyboardInterrupt:
|
|
227
|
+
pass
|
|
228
|
+
else:
|
|
229
|
+
pytest.fail("KeyboardInterrupt was not raised")
|
|
230
|
+
_assert_failed_base_exception(events, KeyboardInterrupt)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_system_exit_is_reported_as_failed(events):
|
|
234
|
+
try:
|
|
235
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
236
|
+
raise SystemExit()
|
|
237
|
+
except SystemExit:
|
|
238
|
+
pass
|
|
239
|
+
else:
|
|
240
|
+
pytest.fail("SystemExit was not raised")
|
|
241
|
+
_assert_failed_base_exception(events, SystemExit)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _assert_cancelled_without_error(events):
|
|
245
|
+
assert events.types() == ["agent_start", "agent_end"]
|
|
246
|
+
assert only(events.entries, "agent_end")["outcome"] == "cancelled"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_cancelled_error_is_not_an_error(events):
|
|
250
|
+
try:
|
|
251
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
252
|
+
raise asyncio.CancelledError()
|
|
253
|
+
except asyncio.CancelledError:
|
|
254
|
+
pass
|
|
255
|
+
else:
|
|
256
|
+
pytest.fail("CancelledError was not raised")
|
|
257
|
+
_assert_cancelled_without_error(events)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_generator_exit_is_not_an_error(events):
|
|
261
|
+
try:
|
|
262
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
263
|
+
raise GeneratorExit()
|
|
264
|
+
except GeneratorExit:
|
|
265
|
+
pass
|
|
266
|
+
else:
|
|
267
|
+
pytest.fail("GeneratorExit was not raised")
|
|
268
|
+
_assert_cancelled_without_error(events)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def test_real_task_cancellation_is_reported_as_cancelled(events):
|
|
272
|
+
async def main():
|
|
273
|
+
started = asyncio.Event()
|
|
274
|
+
|
|
275
|
+
async def body():
|
|
276
|
+
async with failproofai_sdk.agent("a", session_id="s"):
|
|
277
|
+
started.set()
|
|
278
|
+
await asyncio.sleep(60)
|
|
279
|
+
|
|
280
|
+
task = asyncio.create_task(body())
|
|
281
|
+
await started.wait()
|
|
282
|
+
task.cancel()
|
|
283
|
+
with pytest.raises(asyncio.CancelledError):
|
|
284
|
+
cancelled_result = await task
|
|
285
|
+
pytest.fail(f"cancelled task returned {cancelled_result!r}")
|
|
286
|
+
|
|
287
|
+
asyncio.run(main())
|
|
288
|
+
assert events.types() == ["agent_start", "agent_end"]
|
|
289
|
+
assert only(events.entries, "agent_end")["outcome"] == "cancelled"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# ---------------------------------------------------------------------------
|
|
293
|
+
# tool_call()
|
|
294
|
+
# ---------------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
def test_tool_call_pairs_and_carries_output(events):
|
|
297
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
298
|
+
with failproofai_sdk.tool_call("web_search", input={"q": "x"}) as t:
|
|
299
|
+
t.output = {"hits": 3}
|
|
300
|
+
call_id = t.id
|
|
301
|
+
|
|
302
|
+
assert events.types() == ["agent_start", "tool_use", "tool_result", "agent_end"]
|
|
303
|
+
use = only(events.entries, "tool_use")
|
|
304
|
+
res = only(events.entries, "tool_result")
|
|
305
|
+
assert use["tool_call_id"] == res["tool_call_id"] == call_id
|
|
306
|
+
assert use["input"] == {"q": "x"}
|
|
307
|
+
assert res["output"] == {"hits": 3}
|
|
308
|
+
assert "error" not in res
|
|
309
|
+
assert isinstance(res["duration_ms"], int)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_tool_call_id_is_read_only():
|
|
313
|
+
box = failproofai_sdk._scopes.ToolCall("abc")
|
|
314
|
+
assert box.id == "abc"
|
|
315
|
+
with pytest.raises(AttributeError):
|
|
316
|
+
box.id = "other"
|
|
317
|
+
# __slots__: no stray attributes
|
|
318
|
+
with pytest.raises(AttributeError):
|
|
319
|
+
box.whatever = 1
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_tool_call_accepts_an_explicit_id(events):
|
|
323
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
324
|
+
with failproofai_sdk.tool_call("t", tool_call_id="run-42") as t:
|
|
325
|
+
assert t.id == "run-42"
|
|
326
|
+
assert only(events.entries, "tool_use")["tool_call_id"] == "run-42"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def test_tool_call_failure_emits_tool_result_error_and_no_error_event(events):
|
|
330
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
331
|
+
try:
|
|
332
|
+
with failproofai_sdk.tool_call("t"):
|
|
333
|
+
raise TypeError("bad arg")
|
|
334
|
+
except TypeError as exc:
|
|
335
|
+
assert str(exc) == "bad arg"
|
|
336
|
+
else:
|
|
337
|
+
pytest.fail("TypeError was not raised")
|
|
338
|
+
|
|
339
|
+
# No `error` event from the tool: an exception the agent loop catches is
|
|
340
|
+
# not a run-level error.
|
|
341
|
+
assert events.types() == ["agent_start", "tool_use", "tool_result", "agent_end"]
|
|
342
|
+
assert only(events.entries, "tool_result")["error"] == "TypeError: bad arg"
|
|
343
|
+
assert only(events.entries, "agent_end")["outcome"] == "success"
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def test_propagating_tool_failure_is_reported_exactly_once(events):
|
|
347
|
+
try:
|
|
348
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
349
|
+
with failproofai_sdk.tool_call("t"):
|
|
350
|
+
raise TypeError("bad arg")
|
|
351
|
+
except TypeError as exc:
|
|
352
|
+
assert str(exc) == "bad arg"
|
|
353
|
+
else:
|
|
354
|
+
pytest.fail("TypeError was not raised")
|
|
355
|
+
|
|
356
|
+
assert events.types() == [
|
|
357
|
+
"agent_start", "tool_use", "tool_result", "error", "agent_end",
|
|
358
|
+
]
|
|
359
|
+
assert len(indexes(events.entries, "error")) == 1
|
|
360
|
+
types = events.types()
|
|
361
|
+
assert types.index("error") < types.index("agent_end")
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def test_tool_call_uses_the_enclosing_agent_id(events):
|
|
365
|
+
with failproofai_sdk.agent("outer", session_id="s"):
|
|
366
|
+
with failproofai_sdk.agent("inner"):
|
|
367
|
+
with failproofai_sdk.tool_call("t"):
|
|
368
|
+
pass
|
|
369
|
+
use = only(events.entries, "tool_use")
|
|
370
|
+
res = only(events.entries, "tool_result")
|
|
371
|
+
assert use["agent_id"] == res["agent_id"] == "inner"
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def test_tool_call_without_a_session_raises_naming_the_fix(events):
|
|
375
|
+
with pytest.raises(TypeError, match="propagate"):
|
|
376
|
+
with failproofai_sdk.tool_call("t"):
|
|
377
|
+
pass
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def test_awkward_payload_does_not_break_the_scope(events):
|
|
381
|
+
"""A non-JSON-serialisable output must not stop `agent_end` landing.
|
|
382
|
+
|
|
383
|
+
Coercion happens in the writer (`json.dumps(default=str)`), so the scope
|
|
384
|
+
itself must pass the object through untouched and still close the span.
|
|
385
|
+
"""
|
|
386
|
+
value = datetime(2026, 7, 29, 12, 0, 0, tzinfo=timezone.utc)
|
|
387
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
388
|
+
with failproofai_sdk.tool_call("t") as call:
|
|
389
|
+
call.output = value
|
|
390
|
+
|
|
391
|
+
assert events.types() == ["agent_start", "tool_use", "tool_result", "agent_end"]
|
|
392
|
+
assert only(events.entries, "tool_result")["output"] is value
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def test_awkward_payload_survives_the_real_writer(tmp_path):
|
|
396
|
+
"""The end-to-end version: through the actual JSONL writer, on disk."""
|
|
397
|
+
import json
|
|
398
|
+
|
|
399
|
+
from failproofai_sdk._events import EventNamespace
|
|
400
|
+
from failproofai_sdk._writer import EventWriter
|
|
401
|
+
|
|
402
|
+
writer = EventWriter(flush_interval=3600)
|
|
403
|
+
namespace = EventNamespace(writer)
|
|
404
|
+
original = _runtime.event
|
|
405
|
+
_runtime.event = namespace
|
|
406
|
+
try:
|
|
407
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
408
|
+
with failproofai_sdk.tool_call("t") as call:
|
|
409
|
+
call.output = datetime(2026, 7, 29, 12, 0, 0, tzinfo=timezone.utc)
|
|
410
|
+
writer.flush_now()
|
|
411
|
+
finally:
|
|
412
|
+
_runtime.event = original
|
|
413
|
+
|
|
414
|
+
path = next((tmp_path / "events").glob("*.jsonl"))
|
|
415
|
+
parsed = [json.loads(line) for line in path.read_text().splitlines()]
|
|
416
|
+
assert [e["type"] for e in parsed] == [
|
|
417
|
+
"agent_start", "tool_use", "tool_result", "agent_end",
|
|
418
|
+
]
|
|
419
|
+
assert parsed[2]["output"] == "2026-07-29 12:00:00+00:00"
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
# `async with` parity — the same body, both syntaxes, byte-identical dicts
|
|
424
|
+
# ---------------------------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
def _scrub(entries):
|
|
427
|
+
"""Drop the only fields that legitimately differ between two runs."""
|
|
428
|
+
return [{k: v for k, v in e.items() if k not in ("timestamp", "duration_ms")} for e in entries]
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _sync_body():
|
|
432
|
+
with failproofai_sdk.agent("planner", session_id="s-parity", goal="g", fw_node="n"):
|
|
433
|
+
with failproofai_sdk.agent("worker"):
|
|
434
|
+
with failproofai_sdk.tool_call("search", tool_call_id="tc-1", input={"q": "x"}) as t:
|
|
435
|
+
t.output = ["a", "b"]
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
async def _async_body():
|
|
439
|
+
async with failproofai_sdk.agent("planner", session_id="s-parity", goal="g", fw_node="n"):
|
|
440
|
+
async with failproofai_sdk.agent("worker"):
|
|
441
|
+
async with failproofai_sdk.tool_call("search", tool_call_id="tc-1", input={"q": "x"}) as t:
|
|
442
|
+
t.output = ["a", "b"]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _sync_failing():
|
|
446
|
+
with pytest.raises(ValueError):
|
|
447
|
+
with failproofai_sdk.agent("planner", session_id="s-parity"):
|
|
448
|
+
with failproofai_sdk.tool_call("search", tool_call_id="tc-1"):
|
|
449
|
+
raise ValueError("nope")
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
async def _async_failing():
|
|
453
|
+
with pytest.raises(ValueError):
|
|
454
|
+
async with failproofai_sdk.agent("planner", session_id="s-parity"):
|
|
455
|
+
async with failproofai_sdk.tool_call("search", tool_call_id="tc-1"):
|
|
456
|
+
raise ValueError("nope")
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@pytest.mark.parametrize(
|
|
460
|
+
"sync_body,async_body",
|
|
461
|
+
[(_sync_body, _async_body), (_sync_failing, _async_failing)],
|
|
462
|
+
ids=["happy", "failing"],
|
|
463
|
+
)
|
|
464
|
+
def test_async_with_is_identical_to_with(events, sync_body, async_body):
|
|
465
|
+
sync_body()
|
|
466
|
+
sync_entries = _scrub(events.entries)
|
|
467
|
+
events.entries.clear()
|
|
468
|
+
|
|
469
|
+
asyncio.run(async_body())
|
|
470
|
+
async_entries = _scrub(events.entries)
|
|
471
|
+
|
|
472
|
+
# `traceback` embeds the frame that raised, which differs by construction.
|
|
473
|
+
for entry in sync_entries + async_entries:
|
|
474
|
+
entry.pop("traceback", None)
|
|
475
|
+
|
|
476
|
+
assert sync_entries == async_entries
|
|
477
|
+
assert sync_entries # not vacuously equal
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def test_async_session_binds_and_unbinds(events):
|
|
481
|
+
async def main():
|
|
482
|
+
async with failproofai_sdk.session("s-async") as sid:
|
|
483
|
+
assert sid == "s-async"
|
|
484
|
+
assert failproofai_sdk.current().session_id == "s-async"
|
|
485
|
+
|
|
486
|
+
asyncio.run(main())
|
|
487
|
+
assert failproofai_sdk.current().session_id is None
|