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,274 @@
1
+ """Framework adapters: the registry behind `failproofai_sdk.instrument()`.
2
+
3
+ import failproofai_sdk
4
+ failproofai_sdk.instrument() # every framework already imported
5
+ failproofai_sdk.instrument("crewai") # exactly one
6
+ failproofai_sdk.uninstrument() # put everything back
7
+
8
+ **This module is stdlib only, and imports no adapter until asked.** The registry
9
+ maps a name to a dotted module path *as a string*; `importlib.import_module`
10
+ runs on demand. That is what keeps `import failproofai_sdk` free of LangChain.
11
+
12
+ Auto-detection reads `sys.modules`, deliberately **not**
13
+ `importlib.util.find_spec`. `find_spec` would report "installed", and acting on
14
+ that means importing a framework the user is not using — 200ms and a pile of
15
+ transitive imports charged to a library that was supposed to be invisible. If
16
+ the framework is not imported, there is nothing to instrument.
17
+
18
+ Writing an adapter
19
+ ------------------
20
+ A module registered here must expose a module-level object named `adapter`
21
+ implementing `failproofai_sdk.integrations._core.Adapter`:
22
+
23
+ name: str # the registry name
24
+ module: str # the framework module auto-detect looks for in sys.modules
25
+ def install(**options) -> None
26
+ def uninstall() -> None
27
+
28
+ `install()` must save the **original attribute object** it replaces — use
29
+ `_core.Patcher`, which also does the "somebody patched on top of us" check — and
30
+ `uninstall()` must restore that saved object. Never re-import to restore: that
31
+ hands back whatever the current value happens to be, which is how two
32
+ instrumentation libraries silently un-patch each other.
33
+
34
+ Every callback the adapter hands to the framework goes through `_core.safe`, and
35
+ every event goes through a `_core.RunTracker`. Adapters own the translation
36
+ table and nothing else.
37
+
38
+ The four names are registered here **now**, before their modules exist, so that
39
+ adding an adapter is one new file rather than an edit to this one.
40
+ """
41
+
42
+ import importlib
43
+ import logging
44
+ import sys
45
+ import threading
46
+ from typing import Any
47
+
48
+ from failproofai_sdk.integrations import _compat, _core
49
+ from failproofai_sdk.integrations._core import Adapter
50
+
51
+ logger = logging.getLogger("failproofai_sdk.integrations")
52
+
53
+ __all__ = ["instrument", "uninstrument", "active", "available"]
54
+
55
+ # name -> dotted module path. A string, imported on demand.
56
+ _REGISTRY: dict[str, str] = {
57
+ "langchain": "failproofai_sdk.integrations.langchain",
58
+ "crewai": "failproofai_sdk.integrations.crewai",
59
+ "llama_index": "failproofai_sdk.integrations.llama_index",
60
+ "pydantic_ai": "failproofai_sdk.integrations.pydantic_ai",
61
+ }
62
+
63
+ # Spellings people actually type. LangGraph is served by the LangChain adapter
64
+ # because LangGraph runs on langchain-core's callback manager.
65
+ _ALIASES: dict[str, str] = {
66
+ "langgraph": "langchain",
67
+ "langchain_core": "langchain",
68
+ "llamaindex": "llama_index",
69
+ "llama-index": "llama_index",
70
+ "pydantic-ai": "pydantic_ai",
71
+ "pydanticai": "pydantic_ai",
72
+ }
73
+
74
+ # name -> the framework modules whose presence in sys.modules means "this
75
+ # framework is in use". Kept here rather than read off `adapter.module` so that
76
+ # detection imports nothing at all, not even our own adapter module.
77
+ _DETECT: dict[str, tuple[str, ...]] = {
78
+ "langchain": ("langchain_core", "langchain", "langgraph"),
79
+ "crewai": ("crewai",),
80
+ "llama_index": ("llama_index", "llama_index.core"),
81
+ "pydantic_ai": ("pydantic_ai",),
82
+ }
83
+
84
+ # Guards _ACTIVE and every install/uninstall. `instrument()` is called from
85
+ # application startup, which in a web server can be several threads at once.
86
+ _LOCK = threading.Lock()
87
+ _ACTIVE: dict[str, Adapter] = {}
88
+
89
+
90
+ def available() -> tuple[str, ...]:
91
+ """Every registry name that can be instrumented, aliases excluded."""
92
+ return tuple(sorted(_REGISTRY))
93
+
94
+
95
+ def active() -> tuple[str, ...]:
96
+ """Names currently instrumented."""
97
+ with _LOCK:
98
+ return tuple(sorted(_ACTIVE))
99
+
100
+
101
+ def _canonical(name: str) -> str:
102
+ key = str(name).strip().lower().replace(" ", "")
103
+ key = _ALIASES.get(key, key)
104
+ if key not in _REGISTRY:
105
+ valid = ", ".join(sorted(set(_REGISTRY) | set(_ALIASES)))
106
+ raise ValueError(
107
+ f"failproofai_sdk: unknown framework {name!r}. Valid names are: {valid}. "
108
+ f"(Call failproofai_sdk.instrument() with no argument to auto-detect.)"
109
+ )
110
+ return key
111
+
112
+
113
+ def _detected() -> list[str]:
114
+ """Registry names whose framework is already imported in this process."""
115
+ found = []
116
+ for name in _REGISTRY:
117
+ modules = _DETECT.get(name, (name,))
118
+ if any(module in sys.modules for module in modules):
119
+ found.append(name)
120
+ return found
121
+
122
+
123
+ def _load(name: str) -> Adapter:
124
+ module = importlib.import_module(_REGISTRY[name])
125
+ adapter = getattr(module, "adapter", None)
126
+ if adapter is None:
127
+ # A module that is itself the adapter is fine too; the attribute is the
128
+ # convention, not the contract.
129
+ adapter = module
130
+ for attribute in ("install", "uninstall"):
131
+ if not callable(getattr(adapter, attribute, None)):
132
+ raise TypeError(
133
+ f"failproofai_sdk: adapter {_REGISTRY[name]!r} does not implement "
134
+ f"{attribute}() — see failproofai_sdk.integrations._core.Adapter."
135
+ )
136
+ return adapter # type: ignore[return-value]
137
+
138
+
139
+ def instrument(framework: str | None = None, **options: Any) -> tuple[str, ...]:
140
+ """Install the adapters. Returns the names newly instrumented.
141
+
142
+ With no argument, instruments every framework already imported in this
143
+ process. Instrumenting something already active is a no-op that returns
144
+ `()`, so calling this from two code paths (or from a reloading dev server)
145
+ cannot double-record.
146
+
147
+ An unknown name raises `ValueError` listing the valid ones — a typo that
148
+ silently records nothing is the worst outcome available. An adapter whose
149
+ `install()` raises is logged and skipped; the others still install, because
150
+ a broken LlamaIndex should not cost you LangGraph. `FAILPROOFAI_SDK_STRICT=1` turns
151
+ that skip back into a raise.
152
+ """
153
+ if framework is None:
154
+ names = _detected()
155
+ if not names:
156
+ # WARNING, not debug. This fires only when somebody explicitly asked
157
+ # for instrumentation and got none — the import-order mistake of
158
+ # calling instrument() above the `import langchain` line — and the
159
+ # result is a process that records nothing at all, with the adapter
160
+ # installed and the docs followed. At debug level the message that
161
+ # names the exact fix was invisible under every default logging
162
+ # config, which made the one mistake that costs you all your
163
+ # telemetry the one mistake we said nothing about.
164
+ logger.warning(
165
+ "failproofai_sdk: instrument() found no supported framework in sys.modules, "
166
+ "so NOTHING was instrumented. Import your framework before calling "
167
+ "instrument(), or name one explicitly: %s.",
168
+ # The names this call would have accepted, rather than one
169
+ # hardcoded example. A reader who is not using CrewAI has to
170
+ # work out for themselves whether the message is a suggestion
171
+ # or a diagnosis, which is a poor use of the one line they get.
172
+ ", ".join(f"instrument({name!r})" for name in sorted(_REGISTRY)),
173
+ )
174
+ else:
175
+ names = [_canonical(framework)]
176
+
177
+ installed: list[str] = []
178
+ with _LOCK:
179
+ for name in names:
180
+ if name in _ACTIVE:
181
+ continue
182
+ adapter = None
183
+ try:
184
+ adapter = _load(name)
185
+ adapter.install(**options)
186
+ except Exception as exc:
187
+ # An install is NOT atomic, so a failure part-way through leaves
188
+ # global state behind. `langchain.install()` sets `_STATE.enabled`,
189
+ # registers an append-only configure hook whose own uninstall
190
+ # comment says "There is no deregister API", builds the tracer and
191
+ # exports an env var — and only THEN probes a capability that can
192
+ # raise (a `_compat.probe` under FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1
193
+ # always does). Catching here left the adapter fully patched and
194
+ # never recorded in `_ACTIVE`, so `active()` denied it existed and
195
+ # `uninstrument()` — which iterates `_ACTIVE` — could never undo it.
196
+ # It recorded for the life of the process and could not be removed.
197
+ if adapter is not None:
198
+ try:
199
+ adapter.uninstall()
200
+ except Exception:
201
+ logger.debug(
202
+ "failproofai_sdk: rollback of a failed %r install did not "
203
+ "complete cleanly",
204
+ name,
205
+ exc_info=True,
206
+ )
207
+ if _core.strict():
208
+ raise
209
+ # `FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1` is documented as the
210
+ # supported way to make a compat problem loud — `_compat.py`'s
211
+ # own docstring calls warn-by-default "only defensible because
212
+ # there is a supported way to make it fail loudly". It was not
213
+ # one: the flag makes `_compat.warn` RAISE, this handler caught
214
+ # it, and the operator got neither behaviour — not the raise the
215
+ # flag promises, and not the best-effort instrumentation the
216
+ # warning text promises ("Instrumenting anyway"). An adapter
217
+ # silently recorded nothing while the log said it was fine.
218
+ if isinstance(exc, _compat.FailproofAICompatWarning) and (
219
+ _compat.strict_integrations()
220
+ ):
221
+ raise
222
+ logger.warning(
223
+ "failproofai_sdk: could not instrument %r; the rest of your process is "
224
+ "unaffected and other adapters still installed. Set "
225
+ "FAILPROOFAI_SDK_STRICT=1 to raise instead.",
226
+ name,
227
+ exc_info=True,
228
+ )
229
+ continue
230
+ _ACTIVE[name] = adapter
231
+ installed.append(name)
232
+ return tuple(installed)
233
+
234
+
235
+ def uninstrument(framework: str | None = None) -> tuple[str, ...]:
236
+ """Reverse `instrument()`. Returns the names removed. Never raises.
237
+
238
+ With no argument, removes everything. An unknown name, or a name that was
239
+ never instrumented, is a no-op — teardown that can fail is teardown people
240
+ stop calling.
241
+ """
242
+ if framework is None:
243
+ with _LOCK:
244
+ names = list(_ACTIVE)
245
+ else:
246
+ try:
247
+ names = [_canonical(framework)]
248
+ except ValueError as exc:
249
+ logger.warning("%s", exc)
250
+ return ()
251
+
252
+ removed: list[str] = []
253
+ with _LOCK:
254
+ for name in names:
255
+ adapter = _ACTIVE.pop(name, None)
256
+ if adapter is None:
257
+ continue
258
+ try:
259
+ adapter.uninstall()
260
+ except Exception:
261
+ logger.warning(
262
+ "failproofai_sdk: %r did not uninstall cleanly; it is no longer "
263
+ "registered, but some patches may remain.",
264
+ name,
265
+ exc_info=True,
266
+ )
267
+ removed.append(name)
268
+ if not _ACTIVE:
269
+ # Nothing is instrumented any more, so a later instrument() starts
270
+ # from a clean slate rather than inheriting a degraded call site or
271
+ # a warning that has "already been shown".
272
+ _core.reset_failures()
273
+ _compat.reset_warnings()
274
+ return tuple(removed)
@@ -0,0 +1,247 @@
1
+ """Version and capability probes for the framework adapters.
2
+
3
+ Extras (`failproofai_sdk[langchain]`) express a *floor*, not enforcement: most users
4
+ already have the framework and will never install an extra. So the real check
5
+ happens here, at `instrument()` time, in three tiers:
6
+
7
+ 1. **framework not importable** -> a hard `ImportError` whose message contains
8
+ the literal install command. Instrumenting is an explicit user action, so
9
+ silently doing nothing is never the right answer.
10
+ 2. **importable but outside the declared range** -> `FailproofAICompatWarning`,
11
+ once, then best-effort. A ceiling exists because without one a clean build a
12
+ year from now pulls the next major, the callback API shifts, and the adapter
13
+ stops receiving events *while raising nothing*.
14
+ 3. **a capability probe fails** -> warn and no-op **that hook only**, never the
15
+ whole adapter.
16
+
17
+ `FAILPROOFAI_SDK_STRICT_INTEGRATIONS=1` promotes every warning here to an exception.
18
+ Warn-by-default is only defensible because there is a supported way to make it
19
+ fail loudly.
20
+
21
+ Why the version comparison is naive
22
+ -----------------------------------
23
+ `import failproofai_sdk` is contractually zero-dependency, so this cannot use
24
+ `packaging`. `parse_version` therefore reads the **leading numeric components
25
+ only** and stops at the first component that is not purely numeric:
26
+
27
+ "1.5.2" -> (1, 5, 2)
28
+ "2.0.0b1" -> (2, 0, 0) # pre-release suffix ignored
29
+ "0.14.23.post1" -> (0, 14, 23) # local/post segments ignored
30
+ "1.2.dev0" -> (1, 2)
31
+
32
+ That means `2.0.0b1` compares **equal** to `2.0.0`, so a pre-release of a major
33
+ we have declared a ceiling against will not be flagged. That is deliberate:
34
+ the alternative is shipping a PEP 440 parser, and being wrong about a release
35
+ candidate is much cheaper than a runtime dependency.
36
+
37
+ Versions are always read with `importlib.metadata.version(dist)` and **never**
38
+ `module.__version__` — that attribute is not guaranteed to exist and several of
39
+ the frameworks we target do not define it.
40
+ """
41
+
42
+ import importlib
43
+ import importlib.metadata
44
+ import logging
45
+ import os
46
+ import threading
47
+ import warnings
48
+ from types import ModuleType
49
+ from typing import Any, Callable
50
+
51
+ logger = logging.getLogger("failproofai_sdk.integrations")
52
+
53
+ __all__ = [
54
+ "FailproofAICompatWarning",
55
+ "parse_version",
56
+ "version_string",
57
+ "version_tuple",
58
+ "require_module",
59
+ "check_version",
60
+ "probe",
61
+ "warn",
62
+ "strict_integrations",
63
+ "set_strict_integrations",
64
+ "reset_warnings",
65
+ ]
66
+
67
+
68
+ class FailproofAICompatWarning(UserWarning):
69
+ """A framework is outside the range this adapter was written against."""
70
+
71
+
72
+ _TRUTHY = frozenset({"1", "true", "yes", "on"})
73
+
74
+
75
+ def _env_flag(name: str) -> bool:
76
+ """Read a boolean env var. Shared with `_core` so both flags parse alike."""
77
+ return os.environ.get(name, "").strip().lower() in _TRUTHY
78
+
79
+
80
+ # Cached, because it is read on every warning and every `safe()` failure, and
81
+ # resettable, because a test that cannot flip the switch cannot test the policy.
82
+ # Same shape as `failproofai_sdk._environment`: a module global that overrides the env
83
+ # var, with None meaning "not decided yet, go look".
84
+ _strict_integrations: bool | None = None
85
+
86
+
87
+ def strict_integrations() -> bool:
88
+ global _strict_integrations
89
+ if _strict_integrations is None:
90
+ _strict_integrations = _env_flag("FAILPROOFAI_SDK_STRICT_INTEGRATIONS")
91
+ return _strict_integrations
92
+
93
+
94
+ def set_strict_integrations(value: bool | None) -> None:
95
+ """Override the flag. `None` re-reads `FAILPROOFAI_SDK_STRICT_INTEGRATIONS`."""
96
+ global _strict_integrations
97
+ _strict_integrations = value
98
+
99
+
100
+ _warned: set[str] = set()
101
+ _warn_lock = threading.Lock()
102
+
103
+
104
+ def reset_warnings() -> None:
105
+ """Forget which warnings have already fired (tests; also `uninstrument()`)."""
106
+ with _warn_lock:
107
+ _warned.clear()
108
+
109
+
110
+ def warn(message: str, *, key: str | None = None) -> None:
111
+ """Warn once per `key`, or raise if strict.
112
+
113
+ Deduplicated because these fire from `install()` *and* from hot callbacks:
114
+ a per-call warning on a chatty framework is its own outage.
115
+ """
116
+ if strict_integrations():
117
+ raise FailproofAICompatWarning(message)
118
+ dedup = key or message
119
+ with _warn_lock:
120
+ if dedup in _warned:
121
+ return
122
+ _warned.add(dedup)
123
+ warnings.warn(message, FailproofAICompatWarning, stacklevel=3)
124
+ logger.warning("%s", message)
125
+
126
+
127
+ def parse_version(text: str) -> tuple[int, ...]:
128
+ """Leading numeric components of a version string. See the module docstring."""
129
+ parts: list[int] = []
130
+ for chunk in str(text).split("."):
131
+ digits = ""
132
+ for ch in chunk:
133
+ if not ch.isdigit():
134
+ break
135
+ digits += ch
136
+ if not digits:
137
+ break
138
+ parts.append(int(digits))
139
+ if len(digits) != len(chunk):
140
+ # A partially numeric component ("0b1", "dev0", "post1") ends the
141
+ # numeric prefix — everything after it is a pre/post/local segment.
142
+ break
143
+ return tuple(parts)
144
+
145
+
146
+ def version_string(dist: str) -> str | None:
147
+ """The installed version of a distribution, or None if it is not installed.
148
+
149
+ `dist` is the *distribution* name (`langchain-core`), which is frequently
150
+ not the module name (`langchain_core`).
151
+ """
152
+ try:
153
+ return importlib.metadata.version(dist)
154
+ except importlib.metadata.PackageNotFoundError:
155
+ return None
156
+ except Exception: # pragma: no cover - a broken METADATA must not break us
157
+ logger.debug("failproofai_sdk: could not read version of %r", dist, exc_info=True)
158
+ return None
159
+
160
+
161
+ def version_tuple(dist: str) -> tuple[int, ...] | None:
162
+ text = version_string(dist)
163
+ return parse_version(text) if text else None
164
+
165
+
166
+ def require_module(module: str, *, dist: str, extra: str) -> ModuleType:
167
+ """Import a framework module or raise with the literal install command.
168
+
169
+ Tier 1. `instrument("langchain")` on a machine without LangChain is a
170
+ mistake the user can fix in one command, so we hand them the command.
171
+ """
172
+ try:
173
+ return importlib.import_module(module)
174
+ except ImportError as exc:
175
+ raise ImportError(
176
+ f"failproofai_sdk: cannot instrument {extra!r} because {module!r} is not importable. "
177
+ f"Install it with: pip install 'failproofai_sdk[{extra}]' "
178
+ f"(or install {dist} directly)."
179
+ ) from exc
180
+
181
+
182
+ def check_version(
183
+ framework: str,
184
+ dist: str,
185
+ *,
186
+ minimum: str | None = None,
187
+ below: str | None = None,
188
+ reason: str | None = None,
189
+ ) -> bool:
190
+ """Tier 2. True when `dist` is inside [minimum, below); warns once if not.
191
+
192
+ Returns True (best effort) for an unknown version too — a framework
193
+ installed from a git checkout has no usable metadata, and refusing to
194
+ instrument it would be a worse answer than trying.
195
+ """
196
+ found = version_string(dist)
197
+ if found is None:
198
+ return True
199
+ got = parse_version(found)
200
+ if not got:
201
+ return True
202
+
203
+ if minimum is not None and got < parse_version(minimum):
204
+ warn(
205
+ f"failproofai_sdk: {dist} {found} is older than the {minimum} this {framework} "
206
+ f"adapter was written against"
207
+ + (f" ({reason})" if reason else "")
208
+ + ". Instrumenting anyway; some events may be missing.",
209
+ key=f"{framework}:{dist}:min",
210
+ )
211
+ return False
212
+ if below is not None and got >= parse_version(below):
213
+ warn(
214
+ f"failproofai_sdk: {dist} {found} is newer than the <{below} this {framework} "
215
+ f"adapter was written against. Instrumenting anyway, but a callback API "
216
+ f"change would make it stop recording silently — please report this.",
217
+ key=f"{framework}:{dist}:max",
218
+ )
219
+ return False
220
+ return True
221
+
222
+
223
+ def probe(framework: str, hook: str, check: Callable[[], Any]) -> bool:
224
+ """Tier 3. Run a capability probe; on failure warn and disable ONE hook.
225
+
226
+ if probe("langchain", "on_interrupt", lambda: langgraph.callbacks.GraphCallbackHandler):
227
+ ...wire it up...
228
+
229
+ A missing capability is never a reason to abandon the whole adapter: the
230
+ other 90% of the events are still correct and still worth having.
231
+ """
232
+ try:
233
+ ok = bool(check())
234
+ except Exception as exc:
235
+ warn(
236
+ f"failproofai_sdk: {framework} capability probe for {hook!r} failed ({exc!r}); "
237
+ f"that hook is disabled, the rest of the adapter is unaffected.",
238
+ key=f"{framework}:{hook}",
239
+ )
240
+ return False
241
+ if not ok:
242
+ warn(
243
+ f"failproofai_sdk: {framework} does not provide {hook!r} in this version; "
244
+ f"that hook is disabled, the rest of the adapter is unaffected.",
245
+ key=f"{framework}:{hook}",
246
+ )
247
+ return ok