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,352 @@
1
+ # Writing the integration
2
+
3
+ Identity is ambient. Bind it once per run with a context manager and every
4
+ `event.*` call inside — including calls in functions that have never heard of
5
+ Failproof AI — lands on the right session and agent.
6
+
7
+ You do not write the contextvar layer any more; it ships. This page is how to use
8
+ it, and what the two failure modes it exists to prevent look like when you route
9
+ around it.
10
+
11
+ On a supported framework (LangChain/LangGraph, CrewAI, LlamaIndex, Pydantic AI),
12
+ read `frameworks.md` first — `failproofai_sdk.instrument()` does all of this for you.
13
+
14
+ ## The three scopes
15
+
16
+ ```python
17
+ import failproofai_sdk
18
+
19
+ failproofai_sdk.configure(environment="production")
20
+
21
+ with failproofai_sdk.agent("planner", goal=question): # agent_start / agent_end
22
+ with failproofai_sdk.tool_call("web_search", input={"q": question}) as t:
23
+ t.output = search(question)
24
+ ```
25
+
26
+ | Scope | Emits | Yields |
27
+ |---|---|---|
28
+ | `failproofai_sdk.session(session_id=None, *, agent_id=None)` | **nothing** — identity only | the session id |
29
+ | `failproofai_sdk.agent(agent_id="main", *, session_id=None, goal=None, parent_id=AUTO, outcome="success", summary=None, **fields)` | `agent_start` on entry, `agent_end` on every exit | an `Identity` |
30
+ | `failproofai_sdk.tool_call(tool_name, *, tool_call_id=None, input=None, **fields)` | `tool_use` / `tool_result`, timed | a handle — set `.output`, read `.id` |
31
+
32
+ Every one of them works as `with` **and** `async with`, with identical semantics
33
+ and byte-identical events. Nothing in a scope awaits, so the async form is the
34
+ same code; use whichever matches the function you are in.
35
+
36
+ Read the current identity anywhere with `failproofai_sdk.current()`, which returns a
37
+ frozen `Identity(session_id, agent_id, parent_id, depth)`. `session_id is None`
38
+ means nothing is bound.
39
+
40
+ **`session()` versus `agent()`.** `session()` binds identity and emits nothing —
41
+ reach for it when the run is brackets you do not own (a request handler that
42
+ delegates to a framework), or when you want one session id to cover several
43
+ `agent()` blocks. `agent()` is what creates the session on the platform, because
44
+ a session is *defined* as something that emitted `agent_start`.
45
+
46
+ **Defaults, all of them chosen to be hard to get wrong:**
47
+
48
+ - `session_id=None` on `agent()`/`session()` inherits an already-bound session,
49
+ and generates a `uuid4().hex` only if there is none. A nested scope stays inside
50
+ one run rather than splitting it in two.
51
+ - `parent_id` defaults to the enclosing agent from the scope stack. Pass
52
+ `parent_id=None` to force a root span, or a string to override.
53
+ - `tool_call_id` defaults to `uuid4().hex` — unique process-wide, which is what
54
+ the correlation map needs (see `events.md`).
55
+
56
+ ## What `agent()` does on the way out
57
+
58
+ This is the exit path hand-written instrumentation always gets wrong, so it is
59
+ worth stating exactly:
60
+
61
+ | Exception | Events, in order | `outcome` |
62
+ |---|---|---|
63
+ | none | `agent_end` | `"success"` (or your `outcome=`) |
64
+ | any `Exception` | `error`, **then** `agent_end` | `"failed"` |
65
+ | `KeyboardInterrupt` / `SystemExit` | `error`, then `agent_end` | `"failed"` |
66
+ | `CancelledError` / `GeneratorExit` | `agent_end` only | `"cancelled"` |
67
+
68
+ The exception is always re-raised. `error` comes strictly *before* `agent_end`,
69
+ because the agent span closes at `agent_end` and anything after it is attributed
70
+ to nothing. A cancellation is not a failure and must not pollute the Errors
71
+ surface, so it emits no `error` event.
72
+
73
+ `tool_call()` on failure emits `tool_result(error="TypeName: message")` and **no
74
+ `error` event** — a tool failure the agent loop catches is not a run-level error,
75
+ and one that propagates is reported exactly once, by the enclosing `agent()`.
76
+
77
+ ## Explicit identity still works
78
+
79
+ `session_id` and `agent_id` are optional keyword arguments on all 15 `event.*`
80
+ methods, not removed ones. Passing them explicitly still works, and still wins
81
+ over whatever is bound:
82
+
83
+ ```python
84
+ failproofai_sdk.event.tool_use(session_id=sid, agent_id="planner",
85
+ tool_name="web_search", tool_call_id="toolu_01")
86
+ ```
87
+
88
+ Omit `session_id` with nothing bound and you get a **`TypeError`** naming both
89
+ fixes. That is deliberate: the alternative is dropping the event silently, and
90
+ this SDK already asks you to debug enough invisible failures. `agent_id` never
91
+ raises — it is a label, and it falls back to `"main"`.
92
+
93
+ ## Don't use a module global
94
+
95
+ The shortcut the scopes exist to replace, because it breaks silently:
96
+
97
+ ```python
98
+ # WRONG
99
+ _current_session = None # or self.session_id on a shared client
100
+ ```
101
+
102
+ The moment two runs overlap — two asyncio tasks, two threads, two requests in one
103
+ process — they share this variable. Events from both runs get whichever
104
+ `session_id` was written last. You do not get an error; you get **one session
105
+ containing two runs' events, interleaved**, and another session missing entirely.
106
+ Nothing about the output says the data is wrong.
107
+
108
+ The scopes bind contextvars instead: per-task and per-thread, so overlapping runs
109
+ cannot see each other's identity. The agent stack they keep is immutable for the
110
+ same reason — a mutable stack shared by reference between tasks is the same bug
111
+ wearing a contextvars costume, and it passes every single-threaded test.
112
+
113
+ ## Threads will drop your events — `propagate()` is the fix
114
+
115
+ This one is worth reading twice, because it is the failure everybody hits.
116
+
117
+ `contextvars` propagate into asyncio tasks automatically — a task started inside
118
+ an `agent()` block inherits the session. **They do not propagate into new
119
+ threads.** A fresh thread starts with an empty context, so nothing is bound
120
+ there.
121
+
122
+ ```python
123
+ # WRONG — the worker has no session
124
+ pool.submit(dispatch, tool_call)
125
+ ```
126
+
127
+ Wrap the callable:
128
+
129
+ ```python
130
+ pool.submit(failproofai_sdk.propagate(dispatch), tool_call)
131
+ pool.map(failproofai_sdk.propagate(work), items)
132
+ threading.Thread(target=failproofai_sdk.propagate(work)).start()
133
+ loop.run_in_executor(None, failproofai_sdk.propagate(work), x)
134
+ ```
135
+
136
+ `propagate(fn)` captures the identity bound *at the moment you call it* and binds
137
+ it inside the worker, then restores what was there. One name covers thread pools,
138
+ bare threads and `run_in_executor`.
139
+
140
+ **Do not reach for `contextvars.copy_context().run` instead.** A `Context` object
141
+ cannot be entered by two threads at once — the second gets `RuntimeError: cannot
142
+ enter context` — so the copy-context form crashes the caller's worker on any
143
+ reuse, which is exactly what `pool.map` and a retried submit do. Mutations made
144
+ inside `ctx.run` also persist in that `Context`, so a reused one leaks the
145
+ previous call's agent stack into the next run.
146
+
147
+ Since events now raise rather than vanish when nothing is bound, an un-propagated
148
+ worker announces itself with a `TypeError` naming `failproofai_sdk.propagate` instead of
149
+ producing a session that is quietly missing half its events.
150
+
151
+ ## Where to put the calls
152
+
153
+ **One tool dispatcher.** Most agents route every tool through one function. That
154
+ is one edit site for all tools:
155
+
156
+ ```python
157
+ def dispatch(tool_call):
158
+ with failproofai_sdk.tool_call(tool_call.name, tool_call_id=tool_call.id,
159
+ input=tool_call.input) as t:
160
+ t.output = TOOLS[tool_call.name](**tool_call.input)
161
+ return t.output
162
+ ```
163
+
164
+ Reuse the framework's own tool-call id — Anthropic and OpenAI both give you one.
165
+ It is already unique, and it makes the events line up with your provider logs.
166
+
167
+ **One LLM wrapper.** Same idea on the model side. Pass a `request_id` on both
168
+ halves so concurrent calls pair correctly, and set `duration_ms` yourself on the
169
+ response — it is not auto-computed for model events:
170
+
171
+ ```python
172
+ def call_model(messages, **kw):
173
+ rid = uuid.uuid4().hex
174
+ started = time.monotonic()
175
+ failproofai_sdk.event.model_request(model=MODEL, messages=messages, request_id=rid,
176
+ tools=kw.get("tools"))
177
+ try:
178
+ resp = client.messages.create(model=MODEL, messages=messages, **kw)
179
+ # BaseException, not Exception. `asyncio.CancelledError` inherits straight
180
+ # from BaseException on every supported Python, so `except Exception` does
181
+ # NOT see a cancelled tool — and a cancelled tool is a `tool_use` with no
182
+ # `tool_result` after it: an orphaned event that also holds its correlation
183
+ # slot until the cap evicts it. Cancellation is the ordinary way an async
184
+ # tool ends when a timeout fires or a caller gives up.
185
+ except BaseException as e:
186
+ failproofai_sdk.event.model_response(
187
+ model=MODEL, request_id=rid, error=f"{type(e).__name__}: {e}",
188
+ duration_ms=round((time.monotonic() - started) * 1000))
189
+ raise
190
+ failproofai_sdk.event.model_response(
191
+ model=resp.model, request_id=rid, stop_reason=resp.stop_reason,
192
+ input_tokens=resp.usage.input_tokens, output_tokens=resp.usage.output_tokens,
193
+ role=resp.role, duration_ms=round((time.monotonic() - started) * 1000))
194
+ return resp
195
+ ```
196
+
197
+ `duration_ms` must be a whole-millisecond **`int`** — a float raises `ValueError`
198
+ at the call site rather than silently emptying the column.
199
+
200
+ **Sub-agents.** One session, several actors. Nest the scopes and the wiring is
201
+ automatic — the inner `agent()` inherits the session and takes the outer agent as
202
+ its `parent_id`:
203
+
204
+ ```python
205
+ async def researcher(topic):
206
+ async with failproofai_sdk.agent("researcher", goal=topic):
207
+ ...
208
+
209
+ async with failproofai_sdk.agent("planner", goal=question):
210
+ await researcher(topic) # session inherited, parent_id="planner"
211
+ ```
212
+
213
+ **Yes, this means several `agent_start` events on one `session_id` — that is
214
+ correct and intended.** `SKILL.md` §2 says "emit `agent_start` at the top of the
215
+ run"; read that as *once per agent*, not once per session. One `agent_start` per
216
+ actor is what makes sub-agents appear as distinct, nested spans.
217
+
218
+ If a sub-agent runs in a different function that is called *outside* the parent's
219
+ block, pass `session_id=failproofai_sdk.current().session_id` explicitly rather than
220
+ letting it generate a new one.
221
+
222
+ **A service.** For an HTTP handler or a queue worker you usually already have a
223
+ per-run id — request id, job id, trace id. **Use it as the `session_id`** rather
224
+ than generating one: then a session in Failproof AI and a request in your own logs
225
+ are the same string, and cross-referencing an incident stops being detective
226
+ work.
227
+
228
+ ```python
229
+ @app.post("/chat")
230
+ async def chat(req: Request, body: ChatBody):
231
+ async with failproofai_sdk.agent("assistant", session_id=req.headers["x-request-id"],
232
+ goal=body.message):
233
+ return await run_agent(body.message)
234
+ ```
235
+
236
+ ## Frameworks
237
+
238
+ | Framework | What to do |
239
+ |---|---|
240
+ | LangChain / LangGraph | `failproofai_sdk.instrument()` — see `frameworks.md` |
241
+ | CrewAI | `failproofai_sdk.instrument()` |
242
+ | LlamaIndex | `failproofai_sdk.instrument()` |
243
+ | Pydantic AI | `failproofai_sdk.instrument()` |
244
+ | a plain agent loop | `agent()` around the loop, `tool_call()` in the dispatcher |
245
+ | an HTTP agent service | `agent()` in request middleware, keyed on the request id |
246
+ | a queue worker | `agent()` around the job handler, keyed on the job id |
247
+ | anything else with a callback surface | `agent()` at its outermost boundary, `tool_call()` in its tool hook |
248
+
249
+ Adapters and hand-written scopes compose: instrument the framework, wrap the call
250
+ in your own `failproofai_sdk.agent(...)`, and the adapter's events join **that** session
251
+ with your agent as their parent. `frameworks.md` has the details.
252
+
253
+ ## Testing your integration
254
+
255
+ Point the SDK somewhere disposable and read what came out:
256
+
257
+ ```python
258
+ # conftest.py
259
+ import json, pathlib, pytest
260
+
261
+ @pytest.fixture
262
+ def events(tmp_path, monkeypatch):
263
+ # `configure(base_dir=...)` below is what actually redirects the spool.
264
+ # Do NOT reach for AGENTEYE_HOME here: the SDK no longer reads it, so it
265
+ # would leave events going to the real ~/.failproofai/custom-agents while
266
+ # this fixture read an empty tmp_path.
267
+ monkeypatch.setenv("FAILPROOFAI_HOME", str(tmp_path))
268
+ import failproofai_sdk
269
+ # flush_interval is huge on purpose: it parks the background thread so it
270
+ # cannot race our explicit flush. Both paths write a file named only to the
271
+ # millisecond, so two flushes in the same millisecond clobber each other and
272
+ # you lose events — a flaky test with a real cause.
273
+ failproofai_sdk.configure(base_dir=tmp_path, environment="test", flush_interval=3600)
274
+ yield lambda: [
275
+ json.loads(line)
276
+ for f in sorted((tmp_path / "events").glob("*.jsonl"))
277
+ for line in f.read_text().splitlines()
278
+ ]
279
+ ```
280
+
281
+ ```python
282
+ def test_run_emits_a_session(events):
283
+ import failproofai_sdk
284
+ with failproofai_sdk.agent("planner", goal="test"):
285
+ pass
286
+ failproofai_sdk._writer.flush_now() # don't wait for the flush thread
287
+ types = [e["type"] for e in events()]
288
+ assert types == ["agent_start", "agent_end"]
289
+
290
+ def test_failure_is_recorded_as_failed(events):
291
+ import failproofai_sdk, pytest
292
+ with pytest.raises(ValueError):
293
+ with failproofai_sdk.agent("planner"):
294
+ raise ValueError("boom")
295
+ failproofai_sdk._writer.flush_now()
296
+ ev = {e["type"]: e for e in events()}
297
+ assert ev["error"]["error_type"] == "ValueError"
298
+ assert ev["agent_end"]["outcome"] == "failed" # not "failure"
299
+ ```
300
+
301
+ `failproofai_sdk._writer.flush_now()` drains the queue synchronously. Without it, a fast
302
+ test finishes before the flush cycle and reads an empty directory — a
303
+ flaky-looking failure with a real cause. Note the leading underscore: `_writer` is
304
+ not a public API, so pin your SDK version if your tests depend on it.
305
+
306
+ Assert on `type`, `session_id` and `outcome`. Those are the three that break
307
+ silently in production.
308
+
309
+ **Test the overlap, not just the happy path.** A single run passes even when
310
+ identity is a module global; mixing only shows up once two runs overlap, which is
311
+ production and not your laptop:
312
+
313
+ ```python
314
+ import asyncio
315
+
316
+ def test_two_runs_do_not_mix(events):
317
+ import failproofai_sdk
318
+
319
+ async def run(name):
320
+ async with failproofai_sdk.agent(name, session_id=name):
321
+ await asyncio.sleep(0) # force the interleave
322
+ failproofai_sdk.event.tool_use(tool_name="t", tool_call_id=f"{name}-1")
323
+
324
+ async def both():
325
+ await asyncio.gather(run("a"), run("b"))
326
+
327
+ asyncio.run(both())
328
+ failproofai_sdk._writer.flush_now()
329
+ for e in events():
330
+ assert e["agent_id"] == e["session_id"] # no event crossed over
331
+ ```
332
+
333
+ Without the `await asyncio.sleep(0)` the tasks do not interleave and the test
334
+ passes against a broken implementation, which makes it worthless.
335
+
336
+ **Add one test with an awkward payload**, because this is the failure that costs
337
+ you everything and it is invisible in normal testing:
338
+
339
+ ```python
340
+ def test_unserializable_payload_does_not_kill_the_writer(events):
341
+ import failproofai_sdk, datetime
342
+ with failproofai_sdk.agent("planner"):
343
+ with failproofai_sdk.tool_call("clock") as t:
344
+ t.output = {"at": datetime.datetime.now()} # a real tool does this
345
+ failproofai_sdk._writer.flush_now()
346
+ assert [e["type"] for e in events()] == [
347
+ "agent_start", "tool_use", "tool_result", "agent_end"]
348
+ ```
349
+
350
+ The awkward value is stringified by the SDK writer, and the later `agent_end`
351
+ must still be present. Keep this test because real tool outputs frequently carry
352
+ objects outside JSON's native type set.
File without changes
@@ -0,0 +1,169 @@
1
+ """Suite-wide isolation.
2
+
3
+ This SDK is built on module-level singletons, so state leaks between tests in
4
+ four ways, and every one of them has bitten this package.
5
+
6
+ 1. **At interpreter exit.** Two tests build an `EventWriter` with a long flush
7
+ interval to inspect its queue and never flush it — the point of those tests.
8
+ But every writer registers in `_writer._live_writers`, and
9
+ `_flush_all_at_exit` flushes ALL of them when the process ends, long after
10
+ pytest has torn its fixtures down. Whatever redirection a test applied is
11
+ already undone by then, so `get_base_dir()` resolves to the real spool again.
12
+ One run of the queue-cap tests deposited over 160,000 synthetic events into a
13
+ live spool, where a configured collector would have shipped them to a real
14
+ dashboard as though an agent had emitted them.
15
+
16
+ A fixture cannot fix that, because the write happens after the last fixture
17
+ is gone. So the redirection is applied at IMPORT, straight into `os.environ`
18
+ rather than through `monkeypatch` — pytest undoes monkeypatch at session end,
19
+ and session end is still earlier than the flush.
20
+
21
+ 2. **`_resolver._base_dir` during a test.** Same destination, different clock.
22
+ Every test gets `tmp_path` whether it asked for one or not.
23
+
24
+ 3. **`_environment` and the writer's flush interval.** `configure()` mutates
25
+ process-global state and nothing puts it back.
26
+
27
+ 4. **The identity contextvars.** A leaked `session_id` in production means
28
+ events attributed to the wrong run, accepted at HTTP 200 — the exact silent
29
+ failure that layer exists to prevent. So the leak is asserted and the leaking
30
+ test *fails*. It is repaired afterwards only so the failure stays readable as
31
+ one test rather than cascading through the rest of the run.
32
+
33
+ 5. **The process-wide writer's own background thread.** `_runtime.writer` starts
34
+ at import on a 0.5-second interval, and the spool path is resolved when a
35
+ batch is WRITTEN, not when the event is submitted. So an event queued by one
36
+ test is written wherever `FAILPROOFAI_HOME` points up to half a second later
37
+ — which is a different test's directory, and the events land there with no
38
+ error on either side. It reached CI as a one-leg-in-five failure of
39
+ `test_repeated_flushes_do_not_recreate_or_churn_the_directories`, which
40
+ counts the batch files in its own spool and found seven where it wrote six.
41
+
42
+ Fixed by quiescing that thread for the whole session: nothing in this suite
43
+ depends on it firing (every test that asserts on disk builds its own
44
+ `EventWriter` and calls `flush_now()`), and whatever accumulates is written
45
+ by the exit-time flush into the sandbox from (1), which is then removed.
46
+
47
+ `test_sdk.py` has its own in-file autouse `_reset_environment`; these are
48
+ additive and idempotent with it.
49
+ """
50
+ import atexit
51
+ import os
52
+ import shutil
53
+ import tempfile
54
+
55
+ import pytest
56
+
57
+ # ── (1) survives to interpreter exit ─────────────────────────────────────────
58
+ #
59
+ # `FAILPROOFAI_HOME`, not `AGENTEYE_HOME`: the latter no longer resolves the
60
+ # spool at all (see `_resolver.get_base_dir`), so setting it here would isolate
61
+ # nothing and the exit-time flush described above would land in the developer's
62
+ # REAL `~/.failproofai/custom-agents`.
63
+ #
64
+ # `setdefault`, so a developer who exports `FAILPROOFAI_HOME` to point at a
65
+ # scratch home of their own keeps it. `test_resolver_umbrella.py` deletes this
66
+ # variable per test through monkeypatch, so the resolution rules themselves are
67
+ # still tested against a clean environment.
68
+ #
69
+ # Deliberately NOT deleted by the per-test fixture below: that fixture pins the
70
+ # spool with `set_base_dir(tmp_path)`, but a test which calls
71
+ # `set_base_dir(None)` mid-run would then fall through to the real home. Keeping
72
+ # this set at all times means the floor under every test is still the sandbox.
73
+ _SANDBOX = tempfile.mkdtemp(prefix="failproofai-sdk-tests-")
74
+ os.environ.setdefault("FAILPROOFAI_HOME", _SANDBOX)
75
+
76
+ # Registered before `failproofai_sdk._writer` is imported, so it lands EARLIER in
77
+ # atexit's LIFO order and therefore runs LAST — after the final flush has written
78
+ # into the sandbox we are about to remove.
79
+ atexit.register(shutil.rmtree, _SANDBOX, True)
80
+
81
+ import failproofai_sdk._environment as _environment # noqa: E402
82
+ import failproofai_sdk._resolver as _resolver # noqa: E402
83
+ from failproofai_sdk import _context, _runtime # noqa: E402
84
+ from failproofai_sdk._events import EventNamespace # noqa: E402
85
+
86
+
87
+ #: Long enough that the background loop cannot wake during any plausible run,
88
+ #: finite because `_validated_interval` rejects `inf` (it would raise
89
+ #: OverflowError inside `Event.wait` and kill the thread outright).
90
+ _QUIET_INTERVAL = 86_400.0
91
+
92
+
93
+ class RecordingWriter:
94
+ """A writer that keeps entries in memory. No disk, no flush timing."""
95
+
96
+ def __init__(self) -> None:
97
+ self.entries: list[dict] = []
98
+
99
+ def submit(self, entry: dict) -> None:
100
+ self.entries.append(entry)
101
+
102
+ def last(self) -> dict:
103
+ return self.entries[-1]
104
+
105
+ def types(self) -> list[str]:
106
+ return [e["type"] for e in self.entries]
107
+
108
+
109
+ @pytest.fixture(autouse=True, scope="session")
110
+ def _quiesce_the_process_wide_writer():
111
+ """(5): stop `_runtime.writer`'s thread flushing into whichever test is running.
112
+
113
+ `set_flush_interval`, not a bare attribute write: the loop is already blocked
114
+ in `_wake.wait(0.5)` and would flush once more on the old interval before
115
+ seeing a new value. `set_flush_interval` sets `_wake`, so the loop re-waits on
116
+ the new interval immediately. Done ONCE at session start, when nothing has
117
+ been submitted yet, so the wake it induces drains an empty queue.
118
+
119
+ Not restored afterwards. The only thing left to run is the exit-time flush,
120
+ and it is better served by an interval that cannot fire underneath it.
121
+ """
122
+ _runtime.writer.set_flush_interval(_QUIET_INTERVAL)
123
+
124
+
125
+ @pytest.fixture(autouse=True)
126
+ def _sdk_global_state(tmp_path, monkeypatch):
127
+ """(2) and (3): a private spool per test, and process globals restored."""
128
+ monkeypatch.delenv("AGENTEYE_ENVIRONMENT", raising=False)
129
+
130
+ base_dir = _resolver._base_dir
131
+ environment = _environment._environment
132
+ flush_interval = _runtime.writer._flush_interval
133
+
134
+ _resolver.set_base_dir(tmp_path)
135
+ try:
136
+ yield
137
+ finally:
138
+ _resolver.set_base_dir(base_dir)
139
+ _environment._environment = environment
140
+ _runtime.writer._flush_interval = flush_interval
141
+
142
+
143
+ @pytest.fixture(autouse=True)
144
+ def _no_context_leak():
145
+ """(4): an unbalanced scope fails the test that left it open."""
146
+ yield
147
+ session_id, stack = _context.snapshot()
148
+ if session_id is not None or stack:
149
+ # Repair before failing: without this every subsequent test in the run
150
+ # also fails and the real culprit is impossible to spot.
151
+ _context.restore((None, ()))
152
+ pytest.fail(
153
+ "failproofai_sdk identity contextvars leaked out of this test: "
154
+ f"session_id={session_id!r}, agent_stack={stack!r}. "
155
+ "A scope was entered without being exited."
156
+ )
157
+
158
+
159
+ @pytest.fixture()
160
+ def events(monkeypatch):
161
+ """Swap the process-wide namespace for a recording one.
162
+
163
+ `_scopes` resolves `_runtime.event` at call time precisely so this works.
164
+ Tests must not emit through the real writer: `test_sdk.py` asserts on the
165
+ exact contents of the events directory.
166
+ """
167
+ writer = RecordingWriter()
168
+ monkeypatch.setattr(_runtime, "event", EventNamespace(writer))
169
+ return writer
@@ -0,0 +1,6 @@
1
+ """Per-framework adapter tests.
2
+
3
+ A package (not a bare directory) because `tests/` is one: without an
4
+ `__init__.py` two files called `test_<framework>.py` in different directories
5
+ would collide on the module name under rootdir-based collection.
6
+ """