failproofai 1.0.1 → 1.0.2-beta.1
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]__0l4748x._.js +3 -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]__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/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bwu160._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _0ns6k3a._.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/{3c808qrfcq0im.js → 01u1cf8js2yd6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 0izgu7nqh2zp-.js} +1 -1
- package/.next/standalone/.next/static/chunks/0u4-org_e7zde.js +1 -0
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 1tynnrrh711lr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 22rqvlr7p_1mv.js} +13 -13
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2whxqyskpirbk.js} +1 -1
- package/.next/standalone/.next/static/chunks/31mwk8k0q3_3g.js +6 -0
- package/.next/standalone/.next/static/chunks/3ms6gxfm82t6s.js +1 -0
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 3tbbs_d8xrvyp.js} +1 -1
- package/.next/standalone/.next/static/chunks/43nlc236oo898.js +1 -0
- 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 +163 -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 -15
- 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 +27 -21
- 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 +579 -306
- package/dist/cli.mjs +6499 -3662
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2390 -1978
- package/package.json +27 -21
- 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 +56 -369
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +34 -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 +424 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +885 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +352 -0
- package/src/hooks/pack-store.ts +891 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- 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 +695 -39
- 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]__07tbbf3._.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/server/chunks/ssr/_0lvyuv4._.js +0 -23
- package/.next/standalone/.next/static/chunks/2_2lhuojq2xop.js +0 -1
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3k7mxbp9hiom0.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/src/hooks/policy-presets.ts +0 -135
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"""Tests for the identity layer: contextvars, the agent stack, and propagate().
|
|
2
|
+
|
|
3
|
+
The tests that matter here are the ones a reviewer cannot derive from reading
|
|
4
|
+
the diff:
|
|
5
|
+
|
|
6
|
+
* the **adversarially interleaved** task test, which is the only shape that
|
|
7
|
+
fails against a module-global implementation;
|
|
8
|
+
* the **tuple test**, which is the only shape that fails against
|
|
9
|
+
`ContextVar[list]`;
|
|
10
|
+
* `propagate` under `pool.map`, which is the only shape that fails against
|
|
11
|
+
`partial(copy_context().run, fn)`.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import threading
|
|
16
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
20
|
+
import failproofai_sdk
|
|
21
|
+
import failproofai_sdk._context as _context
|
|
22
|
+
import failproofai_sdk._runtime as _runtime
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Basic binding
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def test_nothing_bound_by_default():
|
|
30
|
+
ident = failproofai_sdk.current()
|
|
31
|
+
assert ident.session_id is None
|
|
32
|
+
assert ident.agent_id is None
|
|
33
|
+
assert ident.parent_id is None
|
|
34
|
+
assert ident.depth == 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_session_binds_and_unbinds():
|
|
38
|
+
with failproofai_sdk.session("s-1") as sid:
|
|
39
|
+
assert sid == "s-1"
|
|
40
|
+
assert failproofai_sdk.current().session_id == "s-1"
|
|
41
|
+
assert failproofai_sdk.current().session_id is None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_session_generates_an_id_when_none_bound():
|
|
45
|
+
with failproofai_sdk.session() as sid:
|
|
46
|
+
assert isinstance(sid, str) and len(sid) == 32
|
|
47
|
+
assert failproofai_sdk.current().session_id == sid
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_nested_session_inherits_rather_than_generating(events):
|
|
51
|
+
with failproofai_sdk.session() as outer:
|
|
52
|
+
with failproofai_sdk.session() as inner:
|
|
53
|
+
assert inner == outer
|
|
54
|
+
with failproofai_sdk.agent("child") as ident:
|
|
55
|
+
assert ident.session_id == outer
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_agent_id_falls_back_to_main():
|
|
59
|
+
assert _context.agent_id() == _context.DEFAULT_AGENT_ID == "main"
|
|
60
|
+
with failproofai_sdk.session("s-1", agent_id="worker"):
|
|
61
|
+
assert _context.agent_id() == "worker"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Task isolation — must FAIL against a module-global implementation
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def test_tasks_are_isolated_when_adversarially_interleaved(events):
|
|
69
|
+
"""Two tasks bind different sessions, then are forced to interleave.
|
|
70
|
+
|
|
71
|
+
The `await asyncio.sleep(0)` between binding and emitting is the whole
|
|
72
|
+
point: it yields to the event loop *while the scope is open*, so task B
|
|
73
|
+
binds its session before task A emits. Against a module global, A's
|
|
74
|
+
tool_use would carry B's session_id and this assertion fails. Without the
|
|
75
|
+
forced interleave the same test passes on a global and proves nothing.
|
|
76
|
+
"""
|
|
77
|
+
order: list[str] = []
|
|
78
|
+
|
|
79
|
+
async def run(name: str) -> None:
|
|
80
|
+
async with failproofai_sdk.agent(name, session_id=f"sess-{name}"):
|
|
81
|
+
order.append(f"bound-{name}")
|
|
82
|
+
await asyncio.sleep(0)
|
|
83
|
+
await asyncio.sleep(0)
|
|
84
|
+
order.append(f"emit-{name}")
|
|
85
|
+
_runtime.event.tool_use(tool_name="t", tool_call_id=f"tc-{name}")
|
|
86
|
+
await asyncio.sleep(0)
|
|
87
|
+
|
|
88
|
+
async def main() -> None:
|
|
89
|
+
await asyncio.gather(run("a"), run("b"))
|
|
90
|
+
|
|
91
|
+
asyncio.run(main())
|
|
92
|
+
|
|
93
|
+
# Prove the interleave actually happened; otherwise the test is vacuous.
|
|
94
|
+
assert order.index("bound-b") < order.index("emit-a")
|
|
95
|
+
|
|
96
|
+
by_call = {e["tool_call_id"]: e for e in events.entries if e["type"] == "tool_use"}
|
|
97
|
+
assert by_call["tc-a"]["session_id"] == "sess-a"
|
|
98
|
+
assert by_call["tc-a"]["agent_id"] == "a"
|
|
99
|
+
assert by_call["tc-b"]["session_id"] == "sess-b"
|
|
100
|
+
assert by_call["tc-b"]["agent_id"] == "b"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_agent_stack_is_a_tuple_not_a_shared_list(events):
|
|
104
|
+
"""Task A's stack must be unmodified by task B pushing a nested agent.
|
|
105
|
+
|
|
106
|
+
A `ContextVar[list]` holds the *same list object* in both tasks, so B's
|
|
107
|
+
push would show up in A's view. This is the test that catches it.
|
|
108
|
+
"""
|
|
109
|
+
seen: dict[str, tuple[str, ...]] = {}
|
|
110
|
+
pushed = asyncio.Event()
|
|
111
|
+
|
|
112
|
+
async def first() -> None:
|
|
113
|
+
async with failproofai_sdk.agent("root-a", session_id="s"):
|
|
114
|
+
async with failproofai_sdk.agent("child-a"):
|
|
115
|
+
# Hand control to the other task, which pushes child-b.
|
|
116
|
+
await pushed.wait()
|
|
117
|
+
seen["a"] = _context._AGENT_STACK.get()
|
|
118
|
+
|
|
119
|
+
async def second() -> None:
|
|
120
|
+
async with failproofai_sdk.agent("root-b", session_id="s"):
|
|
121
|
+
async with failproofai_sdk.agent("child-b"):
|
|
122
|
+
seen["b"] = _context._AGENT_STACK.get()
|
|
123
|
+
pushed.set()
|
|
124
|
+
await asyncio.sleep(0)
|
|
125
|
+
|
|
126
|
+
async def main() -> None:
|
|
127
|
+
await asyncio.gather(first(), second())
|
|
128
|
+
|
|
129
|
+
asyncio.run(main())
|
|
130
|
+
|
|
131
|
+
assert seen["a"] == ("root-a", "child-a")
|
|
132
|
+
assert seen["b"] == ("root-b", "child-b")
|
|
133
|
+
assert isinstance(seen["a"], tuple)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_create_task_inherits_without_propagate(events):
|
|
137
|
+
"""Pinned deliberately: contextvars DO flow into asyncio tasks.
|
|
138
|
+
|
|
139
|
+
Nobody should later sprinkle `propagate()` over async code — a Task copies
|
|
140
|
+
the current context at creation, so the identity is already there.
|
|
141
|
+
"""
|
|
142
|
+
captured: list = []
|
|
143
|
+
|
|
144
|
+
async def child() -> None:
|
|
145
|
+
captured.append(failproofai_sdk.current())
|
|
146
|
+
|
|
147
|
+
async def main() -> None:
|
|
148
|
+
with failproofai_sdk.agent("parent", session_id="s-async"):
|
|
149
|
+
await asyncio.create_task(child())
|
|
150
|
+
|
|
151
|
+
asyncio.run(main())
|
|
152
|
+
assert captured[0].session_id == "s-async"
|
|
153
|
+
assert captured[0].agent_id == "parent"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
# Thread isolation and propagate()
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
def test_threads_do_not_inherit_identity():
|
|
161
|
+
seen: list = []
|
|
162
|
+
|
|
163
|
+
def worker() -> None:
|
|
164
|
+
seen.append(failproofai_sdk.current())
|
|
165
|
+
|
|
166
|
+
with failproofai_sdk.session("s-thread"):
|
|
167
|
+
t = threading.Thread(target=worker)
|
|
168
|
+
t.start()
|
|
169
|
+
t.join()
|
|
170
|
+
|
|
171
|
+
assert seen[0].session_id is None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_bare_submit_inside_agent_raises_naming_propagate(events):
|
|
175
|
+
def worker():
|
|
176
|
+
_runtime.event.tool_use(tool_name="t", tool_call_id="tc")
|
|
177
|
+
|
|
178
|
+
with failproofai_sdk.agent("a", session_id="s"):
|
|
179
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
|
180
|
+
future = pool.submit(worker)
|
|
181
|
+
with pytest.raises(TypeError) as excinfo:
|
|
182
|
+
future.result()
|
|
183
|
+
|
|
184
|
+
assert "propagate" in str(excinfo.value)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_propagate_with_pool_submit(events):
|
|
188
|
+
def worker(n):
|
|
189
|
+
_runtime.event.tool_use(tool_name="t", tool_call_id=f"tc-{n}")
|
|
190
|
+
return failproofai_sdk.current().session_id
|
|
191
|
+
|
|
192
|
+
with failproofai_sdk.agent("a", session_id="s-submit"):
|
|
193
|
+
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
194
|
+
results = [pool.submit(failproofai_sdk.propagate(worker), n).result() for n in range(3)]
|
|
195
|
+
|
|
196
|
+
assert results == ["s-submit"] * 3
|
|
197
|
+
tool_uses = [e for e in events.entries if e["type"] == "tool_use"]
|
|
198
|
+
assert {e["session_id"] for e in tool_uses} == {"s-submit"}
|
|
199
|
+
assert {e["agent_id"] for e in tool_uses} == {"a"}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_propagate_survives_reuse_via_pool_map(events):
|
|
203
|
+
"""`pool.map` calls ONE wrapper object many times, concurrently.
|
|
204
|
+
|
|
205
|
+
This is what `partial(contextvars.copy_context().run, fn)` cannot do: a
|
|
206
|
+
`Context` raises `RuntimeError: cannot enter context ... already entered`
|
|
207
|
+
on the second concurrent entry, and mutations inside it persist into the
|
|
208
|
+
next call. Snapshot/restore has neither problem.
|
|
209
|
+
"""
|
|
210
|
+
# Forces four workers to be *inside* the wrapper simultaneously. Without
|
|
211
|
+
# it the calls serialise and a copy_context() implementation passes by
|
|
212
|
+
# accident, which makes the test worthless.
|
|
213
|
+
barrier = threading.Barrier(4)
|
|
214
|
+
|
|
215
|
+
def worker(n):
|
|
216
|
+
ident = failproofai_sdk.current()
|
|
217
|
+
barrier.wait(timeout=5)
|
|
218
|
+
# Mutate the stack, to prove the mutation does not leak into the next
|
|
219
|
+
# invocation of the same wrapper.
|
|
220
|
+
with failproofai_sdk.agent(f"child-{n}", session_id=ident.session_id):
|
|
221
|
+
pass
|
|
222
|
+
return (ident.session_id, ident.agent_id, ident.depth)
|
|
223
|
+
|
|
224
|
+
with failproofai_sdk.agent("a", session_id="s-map"):
|
|
225
|
+
wrapped = failproofai_sdk.propagate(worker)
|
|
226
|
+
with ThreadPoolExecutor(max_workers=4) as pool:
|
|
227
|
+
results = list(pool.map(wrapped, range(8)))
|
|
228
|
+
|
|
229
|
+
assert results == [("s-map", "a", 1)] * 8
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_propagate_with_threading_thread(events):
|
|
233
|
+
seen: list = []
|
|
234
|
+
|
|
235
|
+
def worker():
|
|
236
|
+
seen.append(failproofai_sdk.current())
|
|
237
|
+
|
|
238
|
+
with failproofai_sdk.agent("a", session_id="s-thread2"):
|
|
239
|
+
t = threading.Thread(target=failproofai_sdk.propagate(worker))
|
|
240
|
+
t.start()
|
|
241
|
+
t.join()
|
|
242
|
+
|
|
243
|
+
assert seen[0].session_id == "s-thread2"
|
|
244
|
+
assert seen[0].agent_id == "a"
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def test_propagate_with_run_in_executor(events):
|
|
248
|
+
def worker():
|
|
249
|
+
return failproofai_sdk.current().session_id
|
|
250
|
+
|
|
251
|
+
async def main():
|
|
252
|
+
with failproofai_sdk.agent("a", session_id="s-executor"):
|
|
253
|
+
loop = asyncio.get_running_loop()
|
|
254
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
|
255
|
+
return await loop.run_in_executor(pool, failproofai_sdk.propagate(worker))
|
|
256
|
+
|
|
257
|
+
assert asyncio.run(main()) == "s-executor"
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_propagate_does_not_leak_into_the_worker_thread_afterwards():
|
|
261
|
+
seen: list = []
|
|
262
|
+
|
|
263
|
+
def worker():
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
def after():
|
|
267
|
+
seen.append(failproofai_sdk.current().session_id)
|
|
268
|
+
|
|
269
|
+
with failproofai_sdk.session("s-leak"):
|
|
270
|
+
wrapped = failproofai_sdk.propagate(worker)
|
|
271
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
|
272
|
+
pool.submit(wrapped).result()
|
|
273
|
+
pool.submit(after).result()
|
|
274
|
+
|
|
275
|
+
assert seen[0] is None
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_propagate_preserves_metadata():
|
|
279
|
+
def worker(a, b=2):
|
|
280
|
+
"""docstring."""
|
|
281
|
+
return a + b
|
|
282
|
+
|
|
283
|
+
wrapped = failproofai_sdk.propagate(worker)
|
|
284
|
+
assert wrapped.__name__ == "worker"
|
|
285
|
+
assert wrapped.__doc__ == "docstring."
|
|
286
|
+
assert wrapped(1, b=3) == 4
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
# Explicit identity always wins
|
|
291
|
+
# ---------------------------------------------------------------------------
|
|
292
|
+
|
|
293
|
+
def test_explicit_ids_override_the_context(events):
|
|
294
|
+
with failproofai_sdk.agent("ctx-agent", session_id="ctx-session"):
|
|
295
|
+
_runtime.event.tool_use(
|
|
296
|
+
session_id="explicit-session",
|
|
297
|
+
agent_id="explicit-agent",
|
|
298
|
+
tool_name="t",
|
|
299
|
+
tool_call_id="tc",
|
|
300
|
+
)
|
|
301
|
+
entry = [e for e in events.entries if e["type"] == "tool_use"][0]
|
|
302
|
+
assert entry["session_id"] == "explicit-session"
|
|
303
|
+
assert entry["agent_id"] == "explicit-agent"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_explicit_agent_id_only_keeps_context_session(events):
|
|
307
|
+
with failproofai_sdk.agent("ctx-agent", session_id="ctx-session"):
|
|
308
|
+
_runtime.event.tool_use(agent_id="other", tool_name="t", tool_call_id="tc")
|
|
309
|
+
entry = [e for e in events.entries if e["type"] == "tool_use"][0]
|
|
310
|
+
assert entry["session_id"] == "ctx-session"
|
|
311
|
+
assert entry["agent_id"] == "other"
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_missing_session_outside_any_scope_raises(events):
|
|
315
|
+
with pytest.raises(TypeError, match="propagate"):
|
|
316
|
+
_runtime.event.agent_start()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_agent_id_defaults_to_main_with_only_a_session(events):
|
|
320
|
+
with failproofai_sdk.session("s-only"):
|
|
321
|
+
_runtime.event.agent_start()
|
|
322
|
+
assert events.last()["agent_id"] == "main"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# ---------------------------------------------------------------------------
|
|
326
|
+
# Exception precedence is unchanged by the identity fallback
|
|
327
|
+
# ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
def test_duration_ms_guard_still_beats_the_identity_error(events):
|
|
330
|
+
"""No session bound, and duration_ms passed. The ValueError must win.
|
|
331
|
+
|
|
332
|
+
`_identity()` runs after the guard precisely so today's precedence holds.
|
|
333
|
+
"""
|
|
334
|
+
with pytest.raises(ValueError, match="duration_ms"):
|
|
335
|
+
_runtime.event.tool_result(tool_name="t", tool_call_id="tc", duration_ms=99)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_reserved_field_error_still_beats_the_identity_error(events):
|
|
339
|
+
with pytest.raises(ValueError, match="Reserved field"):
|
|
340
|
+
_runtime.event.agent_start(timestamp="nope")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_duplicate_session_id_still_raises_type_error(events):
|
|
344
|
+
with pytest.raises(TypeError):
|
|
345
|
+
_runtime.event.agent_start(session_id="s", **{"session_id": "s2"})
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# ---------------------------------------------------------------------------
|
|
349
|
+
# `_pending` correlation keys
|
|
350
|
+
# ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
def test_hook_id_no_longer_collides_with_tool_call_id(events):
|
|
353
|
+
ns = _runtime.event
|
|
354
|
+
ns.tool_use(session_id="s", agent_id="a", tool_name="t", tool_call_id="x1")
|
|
355
|
+
ns.hook_completed(session_id="s", agent_id="a", hook_name="h", hook_id="x1")
|
|
356
|
+
assert "duration_ms" not in events.last()
|
|
357
|
+
# And the tool's own pending entry is untouched, so it still pairs.
|
|
358
|
+
ns.tool_result(session_id="s", agent_id="a", tool_name="t", tool_call_id="x1")
|
|
359
|
+
assert "duration_ms" in events.last()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def test_hook_pair_still_produces_a_duration(events):
|
|
363
|
+
ns = _runtime.event
|
|
364
|
+
ns.hook_triggered(session_id="s", agent_id="a", hook_name="h", hook_id="h1")
|
|
365
|
+
ns.hook_completed(session_id="s", agent_id="a", hook_name="h", hook_id="h1")
|
|
366
|
+
assert "duration_ms" in events.last()
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def test_tool_keys_stay_bare_so_cross_agent_pairs_still_match(events):
|
|
370
|
+
"""A tool started under one agent and finished under another must pair.
|
|
371
|
+
|
|
372
|
+
Namespacing tool keys by agent_id would silently turn this correct
|
|
373
|
+
duration_ms into a missing one, and this is routine once frameworks run
|
|
374
|
+
tools inside sub-agents.
|
|
375
|
+
"""
|
|
376
|
+
ns = _runtime.event
|
|
377
|
+
ns.tool_use(session_id="s", agent_id="planner", tool_name="t", tool_call_id="tc")
|
|
378
|
+
ns.tool_result(session_id="s", agent_id="worker", tool_name="t", tool_call_id="tc")
|
|
379
|
+
assert "duration_ms" in events.last()
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def test_track_pending_is_thread_safe_at_the_cap(events):
|
|
383
|
+
"""Hammer `_track_pending` at the cap from several threads.
|
|
384
|
+
|
|
385
|
+
Unlocked, the read-modify-write lets two threads pick the same `oldest` and
|
|
386
|
+
the second `del` raises KeyError *inside* the caller's event.tool_use().
|
|
387
|
+
"""
|
|
388
|
+
from failproofai_sdk._events import _PENDING_CAP
|
|
389
|
+
|
|
390
|
+
ns = _runtime.event
|
|
391
|
+
for i in range(_PENDING_CAP):
|
|
392
|
+
ns._track_pending(f"seed-{i}", ns._now())
|
|
393
|
+
|
|
394
|
+
errors: list[Exception] = []
|
|
395
|
+
|
|
396
|
+
def hammer(worker: int) -> None:
|
|
397
|
+
try:
|
|
398
|
+
for i in range(200):
|
|
399
|
+
ns._track_pending(f"w{worker}-{i}", ns._now())
|
|
400
|
+
except Exception as exc:
|
|
401
|
+
errors.append(exc)
|
|
402
|
+
|
|
403
|
+
threads = [threading.Thread(target=hammer, args=(w,)) for w in range(8)]
|
|
404
|
+
for t in threads:
|
|
405
|
+
t.start()
|
|
406
|
+
for t in threads:
|
|
407
|
+
t.join()
|
|
408
|
+
|
|
409
|
+
assert errors == []
|
|
410
|
+
assert len(ns._pending) == _PENDING_CAP
|