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,752 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from failproofai_sdk import _context
|
|
6
|
+
|
|
7
|
+
from failproofai_sdk._schema import (
|
|
8
|
+
AgentEndEvent,
|
|
9
|
+
AgentPauseEvent,
|
|
10
|
+
AgentResumeEvent,
|
|
11
|
+
AgentStartEvent,
|
|
12
|
+
ErrorEvent,
|
|
13
|
+
HookCompletedEvent,
|
|
14
|
+
HookTriggeredEvent,
|
|
15
|
+
HumanInputEvent,
|
|
16
|
+
HumanInterruptEvent,
|
|
17
|
+
HumanPauseEvent,
|
|
18
|
+
HumanWaitEvent,
|
|
19
|
+
ModelRequestEvent,
|
|
20
|
+
ModelResponseEvent,
|
|
21
|
+
ToolResultEvent,
|
|
22
|
+
ToolUseEvent,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
# session_id and agent_id are explicit signature params on every method, so Python raises
|
|
28
|
+
# TypeError before our validator runs if a caller tries to pass them as extra **fields.
|
|
29
|
+
# timestamp and type are not in the signature, so they land in **fields and are caught here.
|
|
30
|
+
_RESERVED = frozenset({"timestamp", "session_id", "agent_id", "type", "environment"})
|
|
31
|
+
|
|
32
|
+
# Payload keys ingest lifts out of the JSON blob into unsigned 32-bit columns via
|
|
33
|
+
# `pu32()`. Everything else is stored as-is and can be any shape, but these three
|
|
34
|
+
# are read with a typed accessor that returns None on a mismatch — and a None
|
|
35
|
+
# there is written as NULL under a 200 OK. Nothing is logged, nothing is
|
|
36
|
+
# rejected, and the row still arrives, so the only symptom is a column that is
|
|
37
|
+
# empty for some events and not others.
|
|
38
|
+
#
|
|
39
|
+
# `duration_ms` is refused outright on the four events that MEASURE it. These
|
|
40
|
+
# checks cover the other way in: any of the three passed as a custom field on an
|
|
41
|
+
# event that does not name it, plus `model_response`'s own two parameters, which
|
|
42
|
+
# are the ones a caller is most likely to fill straight from a provider response.
|
|
43
|
+
_PROMOTED_NUMERIC = frozenset({"duration_ms", "input_tokens", "output_tokens"})
|
|
44
|
+
|
|
45
|
+
#: Payload keys ingest lifts into an INDEXED STRING column via `ps()`, which
|
|
46
|
+
#: reads JSON strings and stores NULL for anything else — the same silent-at-200
|
|
47
|
+
#: failure `_PROMOTED_NUMERIC` guards, on the eight keys that had no guard at
|
|
48
|
+
#: all. `None` is the realistic way in: `tool_name=getattr(tool, "name", None)`
|
|
49
|
+
#: is ordinary code, and it produced a row that is invisible to every tool-name
|
|
50
|
+
#: filter and whose `tool_result` never pairs.
|
|
51
|
+
_PROMOTED_STRING = frozenset(
|
|
52
|
+
{
|
|
53
|
+
"tool_name",
|
|
54
|
+
"tool_call_id",
|
|
55
|
+
"hook_name",
|
|
56
|
+
"hook_id",
|
|
57
|
+
"input_id",
|
|
58
|
+
"pause_id",
|
|
59
|
+
"error_type",
|
|
60
|
+
"model",
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
_U32_MAX = 2**32 - 1
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _validate_promoted_numeric(name: str, value) -> None:
|
|
68
|
+
"""Reject anything `pu32()` would silently turn into NULL.
|
|
69
|
+
|
|
70
|
+
Rejecting rather than coercing, and at the boundary rather than in the
|
|
71
|
+
writer, for the same reason `_validated_interval` does: this is the last
|
|
72
|
+
point where the caller still has a stack trace pointing at their own call.
|
|
73
|
+
A float is a mistake worth hearing about — the server drops it whole rather
|
|
74
|
+
than rounding it — and rounding it here would hide that from the one person
|
|
75
|
+
who could fix the source of it.
|
|
76
|
+
|
|
77
|
+
`bool` is checked before `int` because it IS an int in Python, and `True`
|
|
78
|
+
would otherwise sail through and store as 1.
|
|
79
|
+
"""
|
|
80
|
+
if value is None:
|
|
81
|
+
return
|
|
82
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"{name} must be an int (the server reads it as an unsigned 32-bit "
|
|
85
|
+
f"integer and stores NULL for anything else), got {type(value).__name__}: {value!r}"
|
|
86
|
+
)
|
|
87
|
+
if not 0 <= value <= _U32_MAX:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"{name} must be between 0 and {_U32_MAX} (an unsigned 32-bit "
|
|
90
|
+
f"integer), got {value!r}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _validate_promoted_string(name: str, value) -> None:
|
|
95
|
+
"""Reject anything `ps()` would silently turn into NULL.
|
|
96
|
+
|
|
97
|
+
Same reasoning as `_validate_promoted_numeric`, and the same boundary: this
|
|
98
|
+
is the last point where the caller still has a stack trace pointing at their
|
|
99
|
+
own call. `_build` copies the base dict verbatim, so a `None` here reached
|
|
100
|
+
the wire as an explicit JSON `null`, the row was accepted at 200 OK, and the
|
|
101
|
+
column was empty for some events and not others with nothing logged anywhere.
|
|
102
|
+
"""
|
|
103
|
+
if value is None:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"{name} must be a string (the server lifts it into an indexed column "
|
|
106
|
+
f"and stores NULL for anything else, so the event would be accepted at "
|
|
107
|
+
f"200 OK and be invisible to every filter on {name}), got None"
|
|
108
|
+
)
|
|
109
|
+
if not isinstance(value, str):
|
|
110
|
+
raise TypeError(
|
|
111
|
+
f"{name} must be a string (the server lifts it into an indexed column "
|
|
112
|
+
f"and stores NULL for anything else), got {type(value).__name__}: {value!r}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _validate_identity(name: str, value) -> None:
|
|
117
|
+
"""Reject an id the server will skip, at the point the caller can see it.
|
|
118
|
+
|
|
119
|
+
`session_id` and `agent_id` are on every one of the 15 event types and are
|
|
120
|
+
what everything downstream groups by. Ingest requires each to be a JSON
|
|
121
|
+
string: hand it anything else and the row is SKIPPED — and the response is
|
|
122
|
+
`200 OK` with `{"accepted": 0, "skipped": 1}`, so nothing upstream learns.
|
|
123
|
+
The SDK reports success, the collector deletes the batch, and the event is
|
|
124
|
+
gone. Verified against the live server for int, null and object.
|
|
125
|
+
|
|
126
|
+
`None` is the realistic way in — an uninitialised variable, a dict lookup
|
|
127
|
+
that missed, an id threaded through a code path that forgot to set it. The
|
|
128
|
+
caller does not get a wrong number here, they get no data at all, and the
|
|
129
|
+
only symptom is a dashboard that is emptier than it should be.
|
|
130
|
+
|
|
131
|
+
Empty and whitespace-only are refused as well, and those the server DOES
|
|
132
|
+
accept. That is the worse outcome of the two: every event lands, grouped
|
|
133
|
+
under one blank id, so the data looks present and is silently merged.
|
|
134
|
+
"""
|
|
135
|
+
if not isinstance(value, str):
|
|
136
|
+
# TypeError, not ValueError: the wrong TYPE was supplied, and this is
|
|
137
|
+
# also what a caller got before identity became optional — omitting a
|
|
138
|
+
# required keyword argument has always been a TypeError, so code that
|
|
139
|
+
# catches one keeps working.
|
|
140
|
+
raise TypeError(
|
|
141
|
+
f"{name} must be a str — the server skips any event whose {name} is "
|
|
142
|
+
f"not a JSON string, and answers 200 as though it stored it. "
|
|
143
|
+
f"Got {type(value).__name__}: {value!r}"
|
|
144
|
+
)
|
|
145
|
+
if not value.strip():
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"{name} must not be empty — the server accepts it, so every event "
|
|
148
|
+
f"sent this way is silently grouped under one blank id."
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def _resolve_identity(session_id, agent_id) -> "tuple[str, str]":
|
|
152
|
+
"""Fill an omitted `session_id`/`agent_id` from the ambient scope, then validate.
|
|
153
|
+
|
|
154
|
+
Both arguments were required on all 15 methods, which meant threading them
|
|
155
|
+
through every function that might emit — the diff nobody wants to review, and
|
|
156
|
+
the reason the reference integration shipped a contextvars wrapper as markdown
|
|
157
|
+
for customers to paste in. `session()` / `agent()` bind them instead.
|
|
158
|
+
|
|
159
|
+
ORDER MATTERS. The validation runs on the RESOLVED value, not the argument.
|
|
160
|
+
Validating first would reject every ambient call; resolving without validating
|
|
161
|
+
would put the silent-skip back: ingest drops an event whose `session_id` is not
|
|
162
|
+
a JSON string and answers `200 OK` with `{"accepted":0,"skipped":1}`, so a run
|
|
163
|
+
with nothing bound would vanish rather than fail.
|
|
164
|
+
|
|
165
|
+
Called AFTER `_validate_fields`, deliberately. A reserved `**field` is a
|
|
166
|
+
fault in the call itself and reads identically from anywhere, so reporting it
|
|
167
|
+
first gives a stable, reproducible message; the identity error depends on
|
|
168
|
+
where the call was made from, and is the less useful of the two to hear when
|
|
169
|
+
both are true.
|
|
170
|
+
|
|
171
|
+
`agent_id` falls back to `DEFAULT_AGENT_ID` rather than raising — an event
|
|
172
|
+
emitted inside `session()` with no `agent()` around it lands somewhere sensible,
|
|
173
|
+
which is the convention the skill already teaches. `session_id` has no such
|
|
174
|
+
default: inventing one would scatter a run across as many sessions as it has
|
|
175
|
+
emit sites.
|
|
176
|
+
"""
|
|
177
|
+
if session_id is None:
|
|
178
|
+
session_id = _context.session_id()
|
|
179
|
+
if agent_id is None:
|
|
180
|
+
agent_id = _context.agent_id() # DEFAULT_AGENT_ID when no agent scope
|
|
181
|
+
|
|
182
|
+
if session_id is None:
|
|
183
|
+
# TypeError for the same reason: this is a missing required argument,
|
|
184
|
+
# which is what it literally was until the scopes made it optional.
|
|
185
|
+
raise TypeError(
|
|
186
|
+
"session_id is required and nothing is bound. Pass session_id=..., or "
|
|
187
|
+
"wrap the call in `with failproofai_sdk.session():` / "
|
|
188
|
+
"`with failproofai_sdk.agent(\"name\"):`. A new thread does not inherit "
|
|
189
|
+
"the ambient scope — hand work to it with "
|
|
190
|
+
"`failproofai_sdk.propagate(fn)`."
|
|
191
|
+
)
|
|
192
|
+
_validate_identity("session_id", session_id)
|
|
193
|
+
_validate_identity("agent_id", agent_id)
|
|
194
|
+
return session_id, agent_id
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _measured_duration_ms(start_ts, end_ts) -> "int | None":
|
|
198
|
+
"""Whole milliseconds between a paired start and end, or None.
|
|
199
|
+
|
|
200
|
+
The four paired events each computed this inline, and none of them applied
|
|
201
|
+
the range the SERVER enforces — the same range `_validate_promoted_numeric`
|
|
202
|
+
refuses a caller for. `pu32()` reads `duration_ms` as an unsigned 32-bit
|
|
203
|
+
integer and stores NULL for anything outside it, at `200 OK`, so an
|
|
204
|
+
out-of-range duration is not an error anywhere: the row lands with an empty
|
|
205
|
+
column and nothing says why.
|
|
206
|
+
|
|
207
|
+
Two ways to leave the range, both reachable without anything being wrong
|
|
208
|
+
with the caller:
|
|
209
|
+
|
|
210
|
+
* **over.** 2**32 ms is ~49.7 days. A `human_wait` answered after a long
|
|
211
|
+
weekend, or an `agent_pause` resumed a month later, is an ordinary
|
|
212
|
+
lifetime for these pairs, not an abuse of them.
|
|
213
|
+
* **under.** These are wall-clock readings from `datetime.now()`, so an NTP
|
|
214
|
+
step backwards between start and end yields a NEGATIVE interval. `round()`
|
|
215
|
+
keeps the sign, and a negative into an unsigned column is the same silent
|
|
216
|
+
NULL.
|
|
217
|
+
|
|
218
|
+
Omitted rather than clamped. A clamped 49.7 days is indistinguishable from a
|
|
219
|
+
measurement, and the whole reason `duration_ms` is computed here instead of
|
|
220
|
+
accepted from the caller is that a reported duration is unfalsifiable. An
|
|
221
|
+
absent field is at least honest, and the timestamps are still on both events
|
|
222
|
+
for anyone who wants to do the subtraction themselves.
|
|
223
|
+
"""
|
|
224
|
+
if start_ts is None:
|
|
225
|
+
return None
|
|
226
|
+
ms = round((end_ts - start_ts).total_seconds() * 1000)
|
|
227
|
+
if not 0 <= ms <= _U32_MAX:
|
|
228
|
+
logger.warning(
|
|
229
|
+
"Failproof AI omitted duration_ms=%d: outside the unsigned 32-bit range "
|
|
230
|
+
"the server stores it in (0..%d). The event is unaffected.",
|
|
231
|
+
ms,
|
|
232
|
+
_U32_MAX,
|
|
233
|
+
)
|
|
234
|
+
return None
|
|
235
|
+
return ms
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Hard cap on `_pending` correlation map size. Orphaned starts (a `tool_use` with
|
|
239
|
+
# no `tool_result`, a `human_wait` the user never answers, etc.) would otherwise
|
|
240
|
+
# grow this dict unbounded in a long-running process. At the cap we evict the
|
|
241
|
+
# oldest entry FIFO — Python dicts preserve insertion order since 3.7.
|
|
242
|
+
_PENDING_CAP = 10_000
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# Every pairing in `_pending` is keyed by what it pairs and by the SESSION it
|
|
246
|
+
# belongs to — and deliberately NOT by the agent.
|
|
247
|
+
#
|
|
248
|
+
# The rule: include what makes the id unique, exclude what can legitimately
|
|
249
|
+
# change between the start event and the end event.
|
|
250
|
+
#
|
|
251
|
+
# * KIND belongs in the key. Tool pairs keyed on the bare `tool_call_id` and hook
|
|
252
|
+
# pairs on the bare `hook_id` shared one flat keyspace, so a caller whose tool
|
|
253
|
+
# call and hook happened to share an id — not exotic, both are frequently the
|
|
254
|
+
# harness's own step id — got a `hook_completed` that consumed the `tool_use`
|
|
255
|
+
# timestamp, and then a `tool_result` with no duration at all.
|
|
256
|
+
#
|
|
257
|
+
# * SESSION belongs in the key. `_pending` lives on one process-wide namespace,
|
|
258
|
+
# so two sessions in one process — a supervisor running agents concurrently,
|
|
259
|
+
# the ordinary multi-agent shape — collided on any shared step id. Starting
|
|
260
|
+
# `step-1` in session A and then in session B overwrote A's timestamp; A's
|
|
261
|
+
# result reported B's interval and B's reported none.
|
|
262
|
+
#
|
|
263
|
+
# * AGENT DOES NOT. This was the tempting third component and it is wrong. Once a
|
|
264
|
+
# framework runs tools inside sub-agents, a `tool_use` opened under `planner`
|
|
265
|
+
# and closed under `worker` is routine — LangGraph and CrewAI both do it — and
|
|
266
|
+
# an agent-scoped key makes those pairs miss entirely, silently dropping
|
|
267
|
+
# `duration_ms` for exactly the nested runs that most need it. A session cannot
|
|
268
|
+
# change under a pair; an agent can.
|
|
269
|
+
#
|
|
270
|
+
# These are correlation keys only; they are never emitted and never leave the
|
|
271
|
+
# process, so the shape changes no wire format. It only changes `duration_ms` in
|
|
272
|
+
# the colliding cases, from a fabricated or missing value to a correct one.
|
|
273
|
+
def _tool_key(session_id: str, tool_call_id: str) -> str:
|
|
274
|
+
return f"tool:{session_id}:{tool_call_id}"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _hook_key(session_id: str, hook_id: str) -> str:
|
|
278
|
+
return f"hook:{session_id}:{hook_id}"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class EventNamespace:
|
|
282
|
+
def __init__(self, writer) -> None:
|
|
283
|
+
self._writer = writer
|
|
284
|
+
self._pending: dict[str, datetime] = {}
|
|
285
|
+
|
|
286
|
+
def _track_pending(self, key: str, ts: datetime) -> None:
|
|
287
|
+
# Evicting has to tolerate another thread doing the same thing, because
|
|
288
|
+
# this runs on the CALLER'S agent loop and a raise here is a crash in
|
|
289
|
+
# their code, not a lost measurement.
|
|
290
|
+
#
|
|
291
|
+
# `len()` then `next(iter())` then remove is a read-modify-write, and
|
|
292
|
+
# nothing serialises the three. Two threads arriving at the cap together
|
|
293
|
+
# pick the SAME victim, and the second `del` raised KeyError straight
|
|
294
|
+
# out of `event.tool_use()`. Reproduced at 24 crashes per 30_000 calls
|
|
295
|
+
# across 10 threads — and only once `_pending` is full, which is the
|
|
296
|
+
# long-running multi-agent process this cap exists for in the first
|
|
297
|
+
# place. `next(iter())` has two more shapes for the same reason:
|
|
298
|
+
# StopIteration if the dict was emptied, RuntimeError if it was resized
|
|
299
|
+
# between the iterator and the first step.
|
|
300
|
+
#
|
|
301
|
+
# No lock, deliberately. `_pending` operations are nanoseconds and a
|
|
302
|
+
# lock held at the instant of a `fork()` is inherited locked by a thread
|
|
303
|
+
# that does not exist in the child — the exact hazard `_writer` rebuilds
|
|
304
|
+
# its Event and lock to avoid. Tolerant operations have no such edge.
|
|
305
|
+
#
|
|
306
|
+
# The cost is that the cap is approximate under contention: several
|
|
307
|
+
# threads may insert after each evicting once. That is the same trade
|
|
308
|
+
# `EventWriter.submit` already makes — a backstop against unbounded
|
|
309
|
+
# growth, not an exact quota.
|
|
310
|
+
if len(self._pending) >= _PENDING_CAP:
|
|
311
|
+
try:
|
|
312
|
+
oldest = next(iter(self._pending))
|
|
313
|
+
except (StopIteration, RuntimeError): # emptied or resized under us
|
|
314
|
+
pass
|
|
315
|
+
else:
|
|
316
|
+
self._pending.pop(oldest, None) # may already be gone
|
|
317
|
+
self._pending[key] = ts
|
|
318
|
+
|
|
319
|
+
def _validate_fields(self, fields: dict) -> None:
|
|
320
|
+
bad = _RESERVED & fields.keys()
|
|
321
|
+
if bad:
|
|
322
|
+
raise ValueError(f"Reserved field names cannot be used as custom fields: {sorted(bad)}")
|
|
323
|
+
for name in _PROMOTED_NUMERIC & fields.keys():
|
|
324
|
+
_validate_promoted_numeric(name, fields[name])
|
|
325
|
+
for name in _PROMOTED_STRING & fields.keys():
|
|
326
|
+
_validate_promoted_string(name, fields[name])
|
|
327
|
+
|
|
328
|
+
@staticmethod
|
|
329
|
+
def _now() -> datetime:
|
|
330
|
+
return datetime.now(timezone.utc)
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def _fmt_ts(dt: datetime) -> str:
|
|
334
|
+
return dt.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z"
|
|
335
|
+
|
|
336
|
+
def tool_use(
|
|
337
|
+
self,
|
|
338
|
+
*,
|
|
339
|
+
session_id: str | None = None,
|
|
340
|
+
agent_id: str | None = None,
|
|
341
|
+
tool_name: str,
|
|
342
|
+
tool_call_id: str,
|
|
343
|
+
input: dict | None = None,
|
|
344
|
+
**fields,
|
|
345
|
+
) -> None:
|
|
346
|
+
self._validate_fields(fields)
|
|
347
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
348
|
+
ts = self._now()
|
|
349
|
+
self._track_pending(_tool_key(session_id, tool_call_id), ts)
|
|
350
|
+
self._writer.submit(
|
|
351
|
+
ToolUseEvent(
|
|
352
|
+
timestamp=self._fmt_ts(ts),
|
|
353
|
+
session_id=session_id,
|
|
354
|
+
agent_id=agent_id,
|
|
355
|
+
tool_name=tool_name,
|
|
356
|
+
tool_call_id=tool_call_id,
|
|
357
|
+
input=input,
|
|
358
|
+
extra_fields=fields,
|
|
359
|
+
).to_dict()
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
def tool_result(
|
|
363
|
+
self,
|
|
364
|
+
*,
|
|
365
|
+
session_id: str | None = None,
|
|
366
|
+
agent_id: str | None = None,
|
|
367
|
+
tool_name: str,
|
|
368
|
+
tool_call_id: str,
|
|
369
|
+
output: Any | None = None,
|
|
370
|
+
error: str | None = None,
|
|
371
|
+
**fields,
|
|
372
|
+
) -> None:
|
|
373
|
+
if "duration_ms" in fields:
|
|
374
|
+
raise ValueError("duration_ms is auto-computed by the SDK and cannot be passed by the caller")
|
|
375
|
+
self._validate_fields(fields)
|
|
376
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
377
|
+
ts = self._now()
|
|
378
|
+
start_ts = self._pending.pop(_tool_key(session_id, tool_call_id), None)
|
|
379
|
+
duration_ms = _measured_duration_ms(start_ts, ts)
|
|
380
|
+
self._writer.submit(
|
|
381
|
+
ToolResultEvent(
|
|
382
|
+
timestamp=self._fmt_ts(ts),
|
|
383
|
+
session_id=session_id,
|
|
384
|
+
agent_id=agent_id,
|
|
385
|
+
tool_name=tool_name,
|
|
386
|
+
tool_call_id=tool_call_id,
|
|
387
|
+
output=output,
|
|
388
|
+
error=error,
|
|
389
|
+
duration_ms=duration_ms,
|
|
390
|
+
extra_fields=fields,
|
|
391
|
+
).to_dict()
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
def model_request(
|
|
395
|
+
self,
|
|
396
|
+
*,
|
|
397
|
+
session_id: str | None = None,
|
|
398
|
+
agent_id: str | None = None,
|
|
399
|
+
model: str | None = None,
|
|
400
|
+
messages: list[dict] | None = None,
|
|
401
|
+
system: Any | None = None,
|
|
402
|
+
tools: list[dict] | None = None,
|
|
403
|
+
request_id: str | None = None,
|
|
404
|
+
**fields,
|
|
405
|
+
) -> None:
|
|
406
|
+
self._validate_fields(fields)
|
|
407
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
408
|
+
ts = self._now()
|
|
409
|
+
self._writer.submit(
|
|
410
|
+
ModelRequestEvent(
|
|
411
|
+
timestamp=self._fmt_ts(ts),
|
|
412
|
+
session_id=session_id,
|
|
413
|
+
agent_id=agent_id,
|
|
414
|
+
model=model,
|
|
415
|
+
messages=messages,
|
|
416
|
+
system=system,
|
|
417
|
+
tools=tools,
|
|
418
|
+
request_id=request_id,
|
|
419
|
+
extra_fields=fields,
|
|
420
|
+
).to_dict()
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
def model_response(
|
|
424
|
+
self,
|
|
425
|
+
*,
|
|
426
|
+
session_id: str | None = None,
|
|
427
|
+
agent_id: str | None = None,
|
|
428
|
+
model: str | None = None,
|
|
429
|
+
stop_reason: str | None = None,
|
|
430
|
+
input_tokens: int | None = None,
|
|
431
|
+
output_tokens: int | None = None,
|
|
432
|
+
content: Any | None = None,
|
|
433
|
+
role: str | None = None,
|
|
434
|
+
request_id: str | None = None,
|
|
435
|
+
**fields,
|
|
436
|
+
) -> None:
|
|
437
|
+
# Named parameters, so they never reach `_validate_fields`. They are also
|
|
438
|
+
# the likeliest of the three to arrive wrong: a caller reading them off a
|
|
439
|
+
# provider's usage object gets whatever that object holds.
|
|
440
|
+
_validate_promoted_numeric("input_tokens", input_tokens)
|
|
441
|
+
_validate_promoted_numeric("output_tokens", output_tokens)
|
|
442
|
+
self._validate_fields(fields)
|
|
443
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
444
|
+
ts = self._now()
|
|
445
|
+
self._writer.submit(
|
|
446
|
+
ModelResponseEvent(
|
|
447
|
+
timestamp=self._fmt_ts(ts),
|
|
448
|
+
session_id=session_id,
|
|
449
|
+
agent_id=agent_id,
|
|
450
|
+
model=model,
|
|
451
|
+
stop_reason=stop_reason,
|
|
452
|
+
input_tokens=input_tokens,
|
|
453
|
+
output_tokens=output_tokens,
|
|
454
|
+
content=content,
|
|
455
|
+
role=role,
|
|
456
|
+
request_id=request_id,
|
|
457
|
+
extra_fields=fields,
|
|
458
|
+
).to_dict()
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
def agent_start(
|
|
462
|
+
self,
|
|
463
|
+
*,
|
|
464
|
+
session_id: str | None = None,
|
|
465
|
+
agent_id: str | None = None,
|
|
466
|
+
goal: str | None = None,
|
|
467
|
+
parent_id: str | None = None,
|
|
468
|
+
**fields,
|
|
469
|
+
) -> None:
|
|
470
|
+
self._validate_fields(fields)
|
|
471
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
472
|
+
ts = self._now()
|
|
473
|
+
self._writer.submit(
|
|
474
|
+
AgentStartEvent(
|
|
475
|
+
timestamp=self._fmt_ts(ts),
|
|
476
|
+
session_id=session_id,
|
|
477
|
+
agent_id=agent_id,
|
|
478
|
+
goal=goal,
|
|
479
|
+
parent_id=parent_id,
|
|
480
|
+
extra_fields=fields,
|
|
481
|
+
).to_dict()
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
def agent_end(
|
|
485
|
+
self,
|
|
486
|
+
*,
|
|
487
|
+
session_id: str | None = None,
|
|
488
|
+
agent_id: str | None = None,
|
|
489
|
+
outcome: str | None = None,
|
|
490
|
+
summary: str | None = None,
|
|
491
|
+
**fields,
|
|
492
|
+
) -> None:
|
|
493
|
+
self._validate_fields(fields)
|
|
494
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
495
|
+
ts = self._now()
|
|
496
|
+
self._writer.submit(
|
|
497
|
+
AgentEndEvent(
|
|
498
|
+
timestamp=self._fmt_ts(ts),
|
|
499
|
+
session_id=session_id,
|
|
500
|
+
agent_id=agent_id,
|
|
501
|
+
outcome=outcome,
|
|
502
|
+
summary=summary,
|
|
503
|
+
extra_fields=fields,
|
|
504
|
+
).to_dict()
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
def agent_pause(
|
|
508
|
+
self,
|
|
509
|
+
*,
|
|
510
|
+
session_id: str | None = None,
|
|
511
|
+
agent_id: str | None = None,
|
|
512
|
+
pause_id: str,
|
|
513
|
+
reason: str | None = None,
|
|
514
|
+
user_id: str | None = None,
|
|
515
|
+
**fields,
|
|
516
|
+
) -> None:
|
|
517
|
+
self._validate_fields(fields)
|
|
518
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
519
|
+
ts = self._now()
|
|
520
|
+
self._track_pending(f"pause:{session_id}:{pause_id}", ts)
|
|
521
|
+
self._writer.submit(
|
|
522
|
+
AgentPauseEvent(
|
|
523
|
+
timestamp=self._fmt_ts(ts),
|
|
524
|
+
session_id=session_id,
|
|
525
|
+
agent_id=agent_id,
|
|
526
|
+
pause_id=pause_id,
|
|
527
|
+
reason=reason,
|
|
528
|
+
user_id=user_id,
|
|
529
|
+
extra_fields=fields,
|
|
530
|
+
).to_dict()
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
def agent_resume(
|
|
534
|
+
self,
|
|
535
|
+
*,
|
|
536
|
+
session_id: str | None = None,
|
|
537
|
+
agent_id: str | None = None,
|
|
538
|
+
pause_id: str,
|
|
539
|
+
reason: str | None = None,
|
|
540
|
+
user_id: str | None = None,
|
|
541
|
+
**fields,
|
|
542
|
+
) -> None:
|
|
543
|
+
if "duration_ms" in fields:
|
|
544
|
+
raise ValueError("duration_ms is auto-computed by the SDK and cannot be passed by the caller")
|
|
545
|
+
self._validate_fields(fields)
|
|
546
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
547
|
+
ts = self._now()
|
|
548
|
+
start_ts = self._pending.pop(f"pause:{session_id}:{pause_id}", None)
|
|
549
|
+
duration_ms = _measured_duration_ms(start_ts, ts)
|
|
550
|
+
self._writer.submit(
|
|
551
|
+
AgentResumeEvent(
|
|
552
|
+
timestamp=self._fmt_ts(ts),
|
|
553
|
+
session_id=session_id,
|
|
554
|
+
agent_id=agent_id,
|
|
555
|
+
pause_id=pause_id,
|
|
556
|
+
duration_ms=duration_ms,
|
|
557
|
+
reason=reason,
|
|
558
|
+
user_id=user_id,
|
|
559
|
+
extra_fields=fields,
|
|
560
|
+
).to_dict()
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def hook_triggered(
|
|
564
|
+
self,
|
|
565
|
+
*,
|
|
566
|
+
session_id: str | None = None,
|
|
567
|
+
agent_id: str | None = None,
|
|
568
|
+
hook_name: str,
|
|
569
|
+
hook_id: str,
|
|
570
|
+
trigger_event: str | None = None,
|
|
571
|
+
input: Any | None = None,
|
|
572
|
+
**fields,
|
|
573
|
+
) -> None:
|
|
574
|
+
self._validate_fields(fields)
|
|
575
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
576
|
+
ts = self._now()
|
|
577
|
+
self._track_pending(_hook_key(session_id, hook_id), ts)
|
|
578
|
+
self._writer.submit(
|
|
579
|
+
HookTriggeredEvent(
|
|
580
|
+
timestamp=self._fmt_ts(ts),
|
|
581
|
+
session_id=session_id,
|
|
582
|
+
agent_id=agent_id,
|
|
583
|
+
hook_name=hook_name,
|
|
584
|
+
hook_id=hook_id,
|
|
585
|
+
trigger_event=trigger_event,
|
|
586
|
+
input=input,
|
|
587
|
+
extra_fields=fields,
|
|
588
|
+
).to_dict()
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
def hook_completed(
|
|
592
|
+
self,
|
|
593
|
+
*,
|
|
594
|
+
session_id: str | None = None,
|
|
595
|
+
agent_id: str | None = None,
|
|
596
|
+
hook_name: str,
|
|
597
|
+
hook_id: str,
|
|
598
|
+
outcome: str | None = None,
|
|
599
|
+
output: Any | None = None,
|
|
600
|
+
error: str | None = None,
|
|
601
|
+
**fields,
|
|
602
|
+
) -> None:
|
|
603
|
+
if "duration_ms" in fields:
|
|
604
|
+
raise ValueError("duration_ms is auto-computed by the SDK and cannot be passed by the caller")
|
|
605
|
+
self._validate_fields(fields)
|
|
606
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
607
|
+
ts = self._now()
|
|
608
|
+
start_ts = self._pending.pop(_hook_key(session_id, hook_id), None)
|
|
609
|
+
duration_ms = _measured_duration_ms(start_ts, ts)
|
|
610
|
+
self._writer.submit(
|
|
611
|
+
HookCompletedEvent(
|
|
612
|
+
timestamp=self._fmt_ts(ts),
|
|
613
|
+
session_id=session_id,
|
|
614
|
+
agent_id=agent_id,
|
|
615
|
+
hook_name=hook_name,
|
|
616
|
+
hook_id=hook_id,
|
|
617
|
+
outcome=outcome,
|
|
618
|
+
output=output,
|
|
619
|
+
error=error,
|
|
620
|
+
duration_ms=duration_ms,
|
|
621
|
+
extra_fields=fields,
|
|
622
|
+
).to_dict()
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
def error(
|
|
626
|
+
self,
|
|
627
|
+
*,
|
|
628
|
+
session_id: str | None = None,
|
|
629
|
+
agent_id: str | None = None,
|
|
630
|
+
error_type: str,
|
|
631
|
+
message: str,
|
|
632
|
+
traceback: str | None = None,
|
|
633
|
+
**fields,
|
|
634
|
+
) -> None:
|
|
635
|
+
self._validate_fields(fields)
|
|
636
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
637
|
+
ts = self._now()
|
|
638
|
+
self._writer.submit(
|
|
639
|
+
ErrorEvent(
|
|
640
|
+
timestamp=self._fmt_ts(ts),
|
|
641
|
+
session_id=session_id,
|
|
642
|
+
agent_id=agent_id,
|
|
643
|
+
error_type=error_type,
|
|
644
|
+
message=message,
|
|
645
|
+
traceback=traceback,
|
|
646
|
+
extra_fields=fields,
|
|
647
|
+
).to_dict()
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
def human_wait(
|
|
651
|
+
self,
|
|
652
|
+
*,
|
|
653
|
+
session_id: str | None = None,
|
|
654
|
+
agent_id: str | None = None,
|
|
655
|
+
input_id: str,
|
|
656
|
+
prompt: str | None = None,
|
|
657
|
+
options: list[str] | None = None,
|
|
658
|
+
reason: str | None = None,
|
|
659
|
+
**fields,
|
|
660
|
+
) -> None:
|
|
661
|
+
self._validate_fields(fields)
|
|
662
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
663
|
+
ts = self._now()
|
|
664
|
+
self._track_pending(f"human:{session_id}:{input_id}", ts)
|
|
665
|
+
self._writer.submit(
|
|
666
|
+
HumanWaitEvent(
|
|
667
|
+
timestamp=self._fmt_ts(ts),
|
|
668
|
+
session_id=session_id,
|
|
669
|
+
agent_id=agent_id,
|
|
670
|
+
input_id=input_id,
|
|
671
|
+
prompt=prompt,
|
|
672
|
+
options=options,
|
|
673
|
+
reason=reason,
|
|
674
|
+
extra_fields=fields,
|
|
675
|
+
).to_dict()
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
def human_input(
|
|
679
|
+
self,
|
|
680
|
+
*,
|
|
681
|
+
session_id: str | None = None,
|
|
682
|
+
agent_id: str | None = None,
|
|
683
|
+
input_id: str,
|
|
684
|
+
response: str | None = None,
|
|
685
|
+
**fields,
|
|
686
|
+
) -> None:
|
|
687
|
+
if "duration_ms" in fields:
|
|
688
|
+
raise ValueError("duration_ms is auto-computed by the SDK and cannot be passed by the caller")
|
|
689
|
+
self._validate_fields(fields)
|
|
690
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
691
|
+
ts = self._now()
|
|
692
|
+
start_ts = self._pending.pop(f"human:{session_id}:{input_id}", None)
|
|
693
|
+
duration_ms = _measured_duration_ms(start_ts, ts)
|
|
694
|
+
self._writer.submit(
|
|
695
|
+
HumanInputEvent(
|
|
696
|
+
timestamp=self._fmt_ts(ts),
|
|
697
|
+
session_id=session_id,
|
|
698
|
+
agent_id=agent_id,
|
|
699
|
+
input_id=input_id,
|
|
700
|
+
response=response,
|
|
701
|
+
duration_ms=duration_ms,
|
|
702
|
+
extra_fields=fields,
|
|
703
|
+
).to_dict()
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
def human_pause(
|
|
707
|
+
self,
|
|
708
|
+
*,
|
|
709
|
+
session_id: str | None = None,
|
|
710
|
+
agent_id: str | None = None,
|
|
711
|
+
reason: str | None = None,
|
|
712
|
+
user_id: str | None = None,
|
|
713
|
+
**fields,
|
|
714
|
+
) -> None:
|
|
715
|
+
self._validate_fields(fields)
|
|
716
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
717
|
+
ts = self._now()
|
|
718
|
+
self._writer.submit(
|
|
719
|
+
HumanPauseEvent(
|
|
720
|
+
timestamp=self._fmt_ts(ts),
|
|
721
|
+
session_id=session_id,
|
|
722
|
+
agent_id=agent_id,
|
|
723
|
+
reason=reason,
|
|
724
|
+
user_id=user_id,
|
|
725
|
+
extra_fields=fields,
|
|
726
|
+
).to_dict()
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
def human_interrupt(
|
|
730
|
+
self,
|
|
731
|
+
*,
|
|
732
|
+
session_id: str | None = None,
|
|
733
|
+
agent_id: str | None = None,
|
|
734
|
+
reason: str | None = None,
|
|
735
|
+
user_id: str | None = None,
|
|
736
|
+
at_step: str | None = None,
|
|
737
|
+
**fields,
|
|
738
|
+
) -> None:
|
|
739
|
+
self._validate_fields(fields)
|
|
740
|
+
session_id, agent_id = _resolve_identity(session_id, agent_id)
|
|
741
|
+
ts = self._now()
|
|
742
|
+
self._writer.submit(
|
|
743
|
+
HumanInterruptEvent(
|
|
744
|
+
timestamp=self._fmt_ts(ts),
|
|
745
|
+
session_id=session_id,
|
|
746
|
+
agent_id=agent_id,
|
|
747
|
+
reason=reason,
|
|
748
|
+
user_id=user_id,
|
|
749
|
+
at_step=at_step,
|
|
750
|
+
extra_fields=fields,
|
|
751
|
+
).to_dict()
|
|
752
|
+
)
|