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.
Files changed (389) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +6 -6
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  20. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  21. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
  22. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  24. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
  26. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  29. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
  31. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
  33. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
  35. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  38. package/.next/standalone/.next/server/app/audit/page.js +2 -2
  39. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/index.html +1 -1
  42. package/.next/standalone/.next/server/app/index.rsc +15 -15
  43. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  44. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  45. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/page.js +2 -2
  48. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +66 -22
  51. package/.next/standalone/.next/server/app/policies/page.js +5 -4
  52. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  55. package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
  56. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  59. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  60. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
  61. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/projects/page.js +2 -2
  65. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  68. package/.next/standalone/.next/server/app/settings/page.js +1 -1
  69. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/chunks/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
  72. package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
  73. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
  74. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
  76. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
  78. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
  80. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
  81. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
  83. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
  84. package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
  85. 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
  86. 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
  87. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
  89. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
  93. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
  98. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__07tbbf3._.js → [root-of-the-server]__0zig5sv._.js} +2 -2
  100. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
  101. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
  102. package/.next/standalone/.next/server/chunks/ssr/{_0lvyuv4._.js → _00e9tnf._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _12l4a_k._.js} +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
  108. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  109. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  111. package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
  112. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
  114. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  115. package/.next/standalone/.next/server/middleware.js +1 -1
  116. package/.next/standalone/.next/server/pages/404.html +1 -1
  117. package/.next/standalone/.next/server/pages/500.html +1 -1
  118. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
  120. package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 0eqky-7n7dgdm.js} +1 -1
  121. package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 0sa9v5mhc8861.js} +13 -13
  122. package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
  123. package/.next/standalone/.next/static/chunks/{3k7mxbp9hiom0.js → 1nuqytbta44af.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/1sl2xmn9inx3y.js +6 -0
  125. package/.next/standalone/.next/static/chunks/{2_2lhuojq2xop.js → 2aawsjld9ied_.js} +1 -1
  126. package/.next/standalone/.next/static/chunks/2gn7ut4svofm2.js +1 -0
  127. package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2hs7ycvsav7v2.js} +1 -1
  128. package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 2i2yt3hzjredm.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 35xg6retpe6rp.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 3y54nw0i7dsjl.js} +1 -1
  131. package/.next/standalone/SECURITY.md +5 -4
  132. package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
  133. package/.next/standalone/app/actions/pack-actions.ts +153 -0
  134. package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
  135. package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
  136. package/.next/standalone/app/policies/hooks-client.tsx +320 -14
  137. package/.next/standalone/components/navbar.tsx +2 -28
  138. package/.next/standalone/components/reach-developers.tsx +8 -1
  139. package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
  140. package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
  141. package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
  142. package/.next/standalone/docs-old/i18n/README.de.md +1 -0
  143. package/.next/standalone/docs-old/i18n/README.es.md +1 -0
  144. package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
  145. package/.next/standalone/docs-old/i18n/README.he.md +1 -0
  146. package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
  147. package/.next/standalone/docs-old/i18n/README.it.md +1 -0
  148. package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
  149. package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
  150. package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
  151. package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
  152. package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
  153. package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
  154. package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
  155. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
  156. package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
  157. package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
  158. package/.next/standalone/fp-cloud-cli/README.md +259 -0
  159. package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
  160. package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
  161. package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
  162. package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
  163. package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
  164. package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
  165. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
  166. package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
  167. package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
  168. package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
  169. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
  170. package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
  171. package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
  172. package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
  173. package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
  174. package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
  175. package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
  176. package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
  177. package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
  178. package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
  179. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
  180. package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
  181. package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
  182. package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
  183. package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
  184. package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
  185. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
  186. package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
  187. package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
  188. package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
  189. package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
  190. package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
  191. package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
  192. package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
  193. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
  194. package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
  195. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
  196. package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
  197. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
  198. package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
  199. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
  200. package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
  201. package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
  202. package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
  203. package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
  204. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
  205. package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
  206. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
  207. package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
  208. package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
  209. package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
  210. package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
  211. package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
  212. package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
  213. package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
  214. package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
  215. package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
  216. package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
  217. package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
  218. package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
  219. package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
  220. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
  221. package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
  222. package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
  223. package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
  224. package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
  225. package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
  226. package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
  227. package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
  228. package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
  229. package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
  230. package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
  231. package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
  232. package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
  233. package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
  234. package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
  235. package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
  236. package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
  237. package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
  238. package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
  239. package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
  240. package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
  241. package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
  242. package/.next/standalone/package.json +29 -22
  243. package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
  244. package/.next/standalone/sdk/python/LICENSE +42 -0
  245. package/.next/standalone/sdk/python/MANIFEST.in +21 -0
  246. package/.next/standalone/sdk/python/README.md +466 -0
  247. package/.next/standalone/sdk/python/docs/README.md +249 -0
  248. package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
  249. package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
  250. package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
  251. package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
  252. package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
  253. package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
  254. package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
  255. package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
  256. package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
  257. package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
  258. package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
  259. package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
  260. package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
  261. package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
  262. package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
  263. package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
  264. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
  265. package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
  266. package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
  267. package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
  268. package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
  269. package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
  270. package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
  271. package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
  272. package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
  273. package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
  274. package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
  275. package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
  276. package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
  277. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
  278. package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
  279. package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
  280. package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
  281. package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
  282. package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
  283. package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
  284. package/.next/standalone/sdk/python/pyproject.toml +92 -0
  285. package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
  286. package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
  287. package/.next/standalone/sdk/python/skill/references/events.md +234 -0
  288. package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
  289. package/.next/standalone/sdk/python/skill/references/install.md +100 -0
  290. package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
  291. package/.next/standalone/sdk/python/tests/__init__.py +0 -0
  292. package/.next/standalone/sdk/python/tests/conftest.py +169 -0
  293. package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
  294. package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
  295. package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
  296. package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
  297. package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
  298. package/.next/standalone/sdk/python/tests/test_context.py +410 -0
  299. package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
  300. package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
  301. package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
  302. package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
  303. package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
  304. package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
  305. package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
  306. package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
  307. package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
  308. package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
  309. package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
  310. package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
  311. package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
  312. package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
  313. package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
  314. package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
  315. package/.next/standalone/sdk/python/uv.lock +6005 -0
  316. package/.next/standalone/server.js +1 -1
  317. package/README.md +77 -21
  318. package/bin/failproofai.mjs +145 -11
  319. package/dist/cli.mjs +4482 -2205
  320. package/dist/index.js +5 -5
  321. package/dist/worker.mjs +2399 -1978
  322. package/package.json +29 -22
  323. package/policy-pack/SHA256SUMS +2 -0
  324. package/policy-pack/failproofai-pack.json +732 -0
  325. package/policy-pack/failproofai-pack.mjs +2017 -0
  326. package/scripts/build-policy-pack.mjs +121 -0
  327. package/scripts/changelog-section.py +77 -0
  328. package/scripts/prune-standalone.mjs +3 -0
  329. package/scripts/python-version.py +232 -0
  330. package/scripts/translate-docs/cli.ts +21 -0
  331. package/scripts/translate-docs/mintlify-nav.ts +85 -13
  332. package/scripts/translate-docs/translator.ts +66 -11
  333. package/src/audit/cache.ts +40 -3
  334. package/src/audit/cli.ts +1 -1
  335. package/src/audit/index.ts +1 -1
  336. package/src/audit/replay.ts +89 -3
  337. package/src/audit/report.ts +5 -5
  338. package/src/audit/schedule-cli.ts +49 -53
  339. package/src/hooks/builtin-policies.ts +217 -579
  340. package/src/hooks/cloud-enrollment-cli.ts +66 -27
  341. package/src/hooks/cloud-managed-policies.ts +6 -1
  342. package/src/hooks/configure-wizard.ts +1 -1
  343. package/src/hooks/custom-hooks-loader.ts +89 -6
  344. package/src/hooks/fp-home.ts +92 -4
  345. package/src/hooks/fp-reset.ts +26 -2
  346. package/src/hooks/handler.ts +181 -11
  347. package/src/hooks/harness-cli.ts +79 -25
  348. package/src/hooks/hook-activity-store.ts +13 -3
  349. package/src/hooks/install-prompt.ts +25 -3
  350. package/src/hooks/manager.ts +418 -172
  351. package/src/hooks/migrations.ts +2 -2
  352. package/src/hooks/pack-cli.ts +505 -0
  353. package/src/hooks/pack-failclosed.ts +189 -0
  354. package/src/hooks/pack-manifest.ts +344 -0
  355. package/src/hooks/pack-store.ts +930 -0
  356. package/src/hooks/policy-catalog.ts +535 -0
  357. package/src/hooks/policy-evaluator.ts +24 -16
  358. package/src/hooks/policy-presets.ts +4 -2
  359. package/src/hooks/policy-registry.ts +8 -2
  360. package/src/hooks/policy-types.ts +26 -0
  361. package/src/hooks/session-pause-cli.ts +33 -9
  362. package/src/hooks/tui.ts +570 -0
  363. package/src/hooks/types.ts +15 -1
  364. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
  365. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
  366. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
  367. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
  368. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
  369. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
  370. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
  371. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
  372. package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
  373. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
  374. package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
  375. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
  376. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
  377. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
  378. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
  379. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
  380. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
  381. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
  382. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
  383. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
  384. package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
  385. package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
  386. package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
  387. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_buildManifest.js +0 -0
  388. /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → nwbyWT06Ud0XdXOrVJyvS}/_clientMiddlewareManifest.js +0 -0
  389. /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
+ )