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,411 @@
1
+ """`docs/` is the integration surface, so it is code under test.
2
+
3
+ The guide and the runnable code live together — one directory per framework,
4
+ each holding the README somebody reads and the `examples/` they run:
5
+
6
+ docs/
7
+ README.md the index
8
+ _shared/ cosmetics for the examples; never part of the SDK
9
+ manual/ no framework, raw scopes + event.*
10
+ langgraph/ crewai/ llama_index/ pydantic_ai/
11
+ README.md the guide
12
+ examples/*.py runnable, and run before shipping
13
+
14
+ Nothing here checks prose. It checks the three ways a docs tree rots without
15
+ anybody noticing: a framework gains an adapter and never gains a page, a page or
16
+ an example names an API that no longer exists, and an example drifts into
17
+ teaching the manual identity path the scopes replaced.
18
+
19
+ The examples themselves need a framework and an API key, so they cannot run in
20
+ unit CI — which is exactly why they need a guard that does not.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import ast
25
+ import re
26
+ import textwrap
27
+ from pathlib import Path
28
+
29
+ import pytest
30
+
31
+ import failproofai_sdk
32
+
33
+ DOCS = Path(failproofai_sdk.__file__).resolve().parent.parent / "docs"
34
+
35
+ #: adapter registry name -> the directory that documents it.
36
+ #: `langgraph` is the directory for the `langchain` adapter: it is the spelling
37
+ #: people search for, and the adapter serves both.
38
+ ADAPTER_DIRS = {
39
+ "langchain": "langgraph",
40
+ "crewai": "crewai",
41
+ "llama_index": "llama_index",
42
+ "pydantic_ai": "pydantic_ai",
43
+ }
44
+
45
+ #: directory -> the failproofai-sdk extra its guide must name.
46
+ DIR_EXTRA = {
47
+ "langgraph": "langgraph",
48
+ "crewai": "crewai",
49
+ "llama_index": "llamaindex",
50
+ "pydantic_ai": "pydantic-ai",
51
+ }
52
+
53
+ #: directory -> third-party modules its examples may import. `manual/` maps to
54
+ #: the openai client and nothing else: it is the "no framework" path, so
55
+ #: borrowing one would defeat the point of it.
56
+ DIR_IMPORTS = {
57
+ "langgraph": {"langchain_core", "langgraph", "langchain_openai"},
58
+ "crewai": {"crewai"},
59
+ "llama_index": {"llama_index"},
60
+ "pydantic_ai": {"pydantic_ai", "pydantic"},
61
+ "manual": {"openai"},
62
+ }
63
+
64
+ #: stdlib and local helpers every example may import. `_shared` is the trace
65
+ #: printer and is explicitly not part of the SDK surface.
66
+ ALWAYS_ALLOWED = {"failproofai_sdk", "_shared", "os", "sys", "json", "asyncio", "pathlib"}
67
+
68
+ ALL_DIRS = sorted(DIR_IMPORTS)
69
+
70
+
71
+ def _guides() -> list[Path]:
72
+ """The framework guides. `_shared/README.md` documents the trace printer,
73
+ which is cosmetics rather than an integration, so it is not one of these."""
74
+ return sorted((DOCS / d / "README.md") for d in ALL_DIRS)
75
+
76
+
77
+ def _examples() -> list[Path]:
78
+ return sorted(p for d in ALL_DIRS for p in (DOCS / d / "examples").glob("*.py"))
79
+
80
+
81
+ EXAMPLES = _examples()
82
+ EXAMPLE_IDS = [f"{p.parents[1].name}/{p.name}" for p in EXAMPLES]
83
+
84
+
85
+ def _imports(tree: ast.AST) -> set[str]:
86
+ out: set[str] = set()
87
+ for node in ast.walk(tree):
88
+ if isinstance(node, ast.Import):
89
+ out |= {a.name.split(".")[0] for a in node.names}
90
+ elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
91
+ out.add(node.module.split(".")[0])
92
+ return out
93
+
94
+
95
+ # ─────────────────────────────────────────────────────────────────────────────
96
+ # Shape
97
+ # ─────────────────────────────────────────────────────────────────────────────
98
+
99
+
100
+ def test_the_docs_tree_exists():
101
+ assert DOCS.is_dir(), f"{DOCS} is missing"
102
+ assert (DOCS / "README.md").is_file(), "docs/README.md — the index — is missing"
103
+
104
+
105
+ def test_there_is_a_directory_for_every_adapter():
106
+ """An adapter with no guide is one nobody will find."""
107
+ from failproofai_sdk.integrations import _REGISTRY
108
+
109
+ assert set(ADAPTER_DIRS) == set(_REGISTRY), (
110
+ f"adapters {sorted(_REGISTRY)} but guides for {sorted(ADAPTER_DIRS)}"
111
+ )
112
+ for adapter, directory in ADAPTER_DIRS.items():
113
+ assert (DOCS / directory).is_dir(), f"{adapter} has no docs/{directory}/"
114
+
115
+
116
+ def test_every_documented_directory_is_accounted_for():
117
+ """A directory nobody links to is a directory nobody maintains."""
118
+ found = {p.name for p in DOCS.iterdir() if p.is_dir() and p.name != "_shared"}
119
+ assert found == set(ALL_DIRS)
120
+
121
+
122
+ @pytest.mark.parametrize("directory", ALL_DIRS)
123
+ def test_a_directory_has_a_guide_and_examples(directory):
124
+ assert (DOCS / directory / "README.md").is_file(), f"{directory}/README.md missing"
125
+ examples = list((DOCS / directory / "examples").glob("*.py"))
126
+ assert examples, f"{directory}/examples/ has no runnable code"
127
+
128
+
129
+ @pytest.mark.parametrize("directory", ALL_DIRS)
130
+ def test_a_directory_has_a_quickstart(directory):
131
+ """`quickstart.py` is the fixed entry point every guide links to."""
132
+ assert (DOCS / directory / "examples" / "quickstart.py").is_file()
133
+
134
+
135
+ @pytest.mark.parametrize("directory", ALL_DIRS)
136
+ def test_a_directory_has_more_than_a_quickstart(directory):
137
+ """A quickstart alone never shows a multi-event flow, which is the point."""
138
+ assert len(list((DOCS / directory / "examples").glob("*.py"))) >= 2
139
+
140
+
141
+ def test_the_index_links_to_every_directory():
142
+ index = (DOCS / "README.md").read_text(encoding="utf-8")
143
+ for directory in ALL_DIRS:
144
+ assert f"{directory}/" in index, f"docs/README.md never links to {directory}/"
145
+
146
+
147
+ # ─────────────────────────────────────────────────────────────────────────────
148
+ # The guides
149
+ # ─────────────────────────────────────────────────────────────────────────────
150
+
151
+
152
+ @pytest.mark.parametrize("guide", _guides(), ids=lambda p: p.parent.name)
153
+ def test_a_guide_only_names_api_this_package_exports(guide):
154
+ """A rename in the SDK must break this test, not somebody's copy-paste."""
155
+ text = guide.read_text(encoding="utf-8")
156
+ referenced = set(re.findall(r"failproofai_sdk\.([a-z_]+)\s*\(", text)) - {"event"}
157
+ missing = {name for name in referenced if not hasattr(failproofai_sdk, name)}
158
+ assert not missing, f"{guide.parent.name} names failproofai_sdk.{missing}, which does not exist"
159
+
160
+
161
+ @pytest.mark.parametrize("guide", _guides(), ids=lambda p: p.parent.name)
162
+ def test_a_guide_only_names_event_methods_that_exist(guide):
163
+ text = guide.read_text(encoding="utf-8")
164
+ referenced = set(re.findall(r"failproofai_sdk\.event\.([a-z_]+)\s*\(", text))
165
+ missing = {n for n in referenced if not hasattr(failproofai_sdk.event, n)}
166
+ assert not missing, f"{guide.parent.name} names event.{missing}, which does not exist"
167
+
168
+
169
+ @pytest.mark.parametrize("guide", _guides() + [DOCS / "README.md"], ids=lambda p: str(p.parent.name or "index"))
170
+ def test_a_guide_only_names_extras_that_exist(guide):
171
+ """A wrong extra in an install line is a five-minute dead end."""
172
+ try: # Python 3.11+
173
+ import tomllib
174
+ except ModuleNotFoundError:
175
+ import tomli as tomllib
176
+
177
+ manifest = tomllib.loads((DOCS.parent / "pyproject.toml").read_text(encoding="utf-8"))
178
+ extras = set(manifest["project"]["optional-dependencies"])
179
+ named = set(re.findall(r"failproofai-sdk\[([a-z-]+)\]", guide.read_text(encoding="utf-8")))
180
+ assert named <= extras, f"{guide} names non-existent extras: {sorted(named - extras)}"
181
+
182
+
183
+ @pytest.mark.parametrize("directory,extra", sorted(DIR_EXTRA.items()))
184
+ def test_a_framework_guide_names_its_own_extra(directory, extra):
185
+ text = (DOCS / directory / "README.md").read_text(encoding="utf-8")
186
+ assert "pip install" in text, f"{directory} never says how to install it"
187
+ assert f"failproofai-sdk[{extra}]" in text, f"{directory} does not name the {extra!r} extra"
188
+
189
+
190
+ @pytest.mark.parametrize("directory", sorted(DIR_EXTRA))
191
+ def test_a_framework_guide_shows_instrument_and_a_session(directory):
192
+ text = (DOCS / directory / "README.md").read_text(encoding="utf-8")
193
+ assert "failproofai_sdk.instrument(" in text, f"{directory} never shows instrument()"
194
+ assert "failproofai_sdk.session()" in text, f"{directory} never shows a session"
195
+
196
+
197
+ @pytest.mark.parametrize("guide", _guides(), ids=lambda p: p.parent.name)
198
+ def test_a_guide_links_to_the_examples_it_documents(guide):
199
+ """A guide describing a file that does not exist is worse than no guide."""
200
+ text = guide.read_text(encoding="utf-8")
201
+ linked = set(re.findall(r"\(examples/([\w.]+\.py)\)", text))
202
+ on_disk = {p.name for p in (guide.parent / "examples").glob("*.py")}
203
+ assert linked, f"{guide.parent.name} links to none of its examples"
204
+ assert linked <= on_disk, f"{guide.parent.name} links to missing {sorted(linked - on_disk)}"
205
+
206
+
207
+ def test_the_manual_guide_does_not_teach_instrument():
208
+ """`manual/` is the no-adapter path; showing instrument() there is a slip.
209
+
210
+ Scoped to the part of the page that teaches the manual API — the section on
211
+ unsupported frameworks legitimately contrasts the two.
212
+ """
213
+ text = (DOCS / "manual" / "README.md").read_text(encoding="utf-8")
214
+ body = text.split("## Instrumenting an unsupported framework")[0]
215
+ assert "failproofai_sdk.instrument()" not in body
216
+
217
+
218
+ def test_the_index_lists_every_event_type():
219
+ """The event matrix is the index's whole value; a missing row is a silent gap."""
220
+ from failproofai_sdk._events import EventNamespace
221
+
222
+ text = (DOCS / "README.md").read_text(encoding="utf-8")
223
+ methods = [
224
+ n for n in vars(EventNamespace)
225
+ if not n.startswith("_") and callable(getattr(EventNamespace, n))
226
+ ]
227
+ missing = [m for m in methods if m not in text]
228
+ assert not missing, f"docs/README.md does not mention {missing}"
229
+
230
+
231
+ # ─────────────────────────────────────────────────────────────────────────────
232
+ # The examples
233
+ # ─────────────────────────────────────────────────────────────────────────────
234
+
235
+
236
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
237
+ def test_an_example_parses(path):
238
+ ast.parse(path.read_text(encoding="utf-8"))
239
+
240
+
241
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
242
+ def test_an_example_imports_only_its_own_framework(path):
243
+ """Borrowing a second framework turns an example into an install problem."""
244
+ tree = ast.parse(path.read_text(encoding="utf-8"))
245
+ allowed = ALWAYS_ALLOWED | DIR_IMPORTS[path.parents[1].name]
246
+ unexpected = _imports(tree) - allowed
247
+ assert not unexpected, f"{path.parents[1].name}/{path.name} imports {sorted(unexpected)}"
248
+
249
+
250
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
251
+ def test_an_example_calls_api_this_package_exports(path):
252
+ called = {
253
+ node.func.attr
254
+ for node in ast.walk(ast.parse(path.read_text(encoding="utf-8")))
255
+ if isinstance(node, ast.Call)
256
+ and isinstance(node.func, ast.Attribute)
257
+ and isinstance(node.func.value, ast.Name)
258
+ and node.func.value.id == "failproofai_sdk"
259
+ }
260
+ assert called, f"{path.name} never calls failproofai_sdk.*"
261
+ missing = {c for c in called if not hasattr(failproofai_sdk, c)}
262
+ assert not missing, f"{path.name} calls failproofai_sdk.{missing} which does not exist"
263
+
264
+
265
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
266
+ def test_an_example_does_not_thread_identity_by_hand(path):
267
+ """`session_id=` everywhere teaches the path the scopes were built to remove.
268
+
269
+ Checked over the AST rather than the raw text, so an example is free to
270
+ *explain* `session_id=` in its docstring — which the manual quickstart has
271
+ to, since explaining what the scopes replace is its whole job.
272
+ """
273
+ threaded = [
274
+ node
275
+ for node in ast.walk(ast.parse(path.read_text(encoding="utf-8")))
276
+ if isinstance(node, ast.Call)
277
+ for kw in node.keywords
278
+ if kw.arg in ("session_id", "agent_id")
279
+ ]
280
+ assert not threaded, (
281
+ f"{path.parents[1].name}/{path.name} passes session_id/agent_id by hand"
282
+ )
283
+
284
+
285
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
286
+ def test_an_example_opens_a_session(path):
287
+ source = path.read_text(encoding="utf-8")
288
+ assert "failproofai_sdk.session()" in source, f"{path.name} never opens a session"
289
+
290
+
291
+ @pytest.mark.parametrize(
292
+ "path",
293
+ [p for p in EXAMPLES if p.parents[1].name != "manual"],
294
+ ids=[i for i in EXAMPLE_IDS if not i.startswith("manual/")],
295
+ )
296
+ def test_a_framework_example_instruments(path):
297
+ source = path.read_text(encoding="utf-8")
298
+ assert "failproofai_sdk.instrument()" in source, f"{path.name} never instruments"
299
+
300
+
301
+ @pytest.mark.parametrize(
302
+ "path",
303
+ [p for p in EXAMPLES if p.parents[1].name == "manual"],
304
+ ids=[i for i in EXAMPLE_IDS if i.startswith("manual/")],
305
+ )
306
+ def test_a_manual_example_does_not_instrument(path):
307
+ """`manual/` exists to show the path with no adapter."""
308
+ source = path.read_text(encoding="utf-8")
309
+ assert "failproofai_sdk.instrument()" not in source
310
+
311
+
312
+ @pytest.mark.parametrize("path", EXAMPLES, ids=EXAMPLE_IDS)
313
+ def test_an_example_documents_how_to_run_itself(path):
314
+ """The docstring is what somebody reads before running it."""
315
+ tree = ast.parse(path.read_text(encoding="utf-8"))
316
+ doc = ast.get_docstring(tree) or ""
317
+ assert "pip install" in doc, f"{path.name} does not say how to install it"
318
+ expected = f"python docs/{path.parents[1].name}/examples/{path.name}"
319
+ assert expected in doc, f"{path.name} does not show `{expected}`"
320
+
321
+
322
+ def test_the_shared_helper_is_not_importable_from_the_sdk():
323
+ """`_shared` is cosmetics. If it ever became a dependency of the package,
324
+ the zero-dependency promise would be quietly routed around."""
325
+ import failproofai_sdk.integrations as integrations
326
+
327
+ for module in (failproofai_sdk, integrations):
328
+ source = Path(module.__file__).read_text(encoding="utf-8")
329
+ assert "_shared" not in source
330
+
331
+
332
+ # ─────────────────────────────────────────────────────────────────────────────
333
+ # Documented options must exist
334
+ # ─────────────────────────────────────────────────────────────────────────────
335
+ #
336
+ # The guides shipped naming `capture_content` for CrewAI and `session_id` for
337
+ # LlamaIndex. Neither adapter reads either one, so both were silently ignored:
338
+ # `instrument()` passes the same dict to every adapter and unknown keys are
339
+ # dropped by design, which means a reader following the docs got no error and no
340
+ # effect. Nothing checked, because the option lists were prose.
341
+
342
+ #: docs directory -> the adapter module that backs it.
343
+ DIR_ADAPTER = {
344
+ "langgraph": "langchain",
345
+ "crewai": "crewai",
346
+ "llama_index": "llama_index",
347
+ "pydantic_ai": "pydantic_ai",
348
+ }
349
+
350
+
351
+ def _adapter_options(module_name: str) -> set[str]:
352
+ """Every option key an adapter actually reads, from its source.
353
+
354
+ Two spellings, because the adapters use two: `options.get("x")` for the ones
355
+ that read the dict directly, and a dataclass of defaults for LangChain,
356
+ which parses the dict into `_Options` first.
357
+ """
358
+ import failproofai_sdk
359
+
360
+ source = (
361
+ Path(failproofai_sdk.__file__).resolve().parent
362
+ / "integrations"
363
+ / f"{module_name}.py"
364
+ ).read_text(encoding="utf-8")
365
+
366
+ names = set(re.findall(r'options\.get\(\s*["\'](\w+)["\']', source))
367
+
368
+ tree = ast.parse(source)
369
+ for node in ast.walk(tree):
370
+ if isinstance(node, ast.ClassDef) and node.name == "_Options":
371
+ names |= {
372
+ stmt.target.id
373
+ for stmt in node.body
374
+ if isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Name)
375
+ }
376
+ return names
377
+
378
+
379
+ def _documented_options(text: str, adapter_name: str) -> set[str]:
380
+ """Keyword names inside a documented `instrument("<name>", ...)` call."""
381
+ documented: set[str] = set()
382
+ for block in re.findall(r"```python[^\n]*\n(.*?)```", text, re.S):
383
+ try:
384
+ tree = ast.parse(textwrap.dedent(block))
385
+ except SyntaxError: # a fragment, not a whole program
386
+ continue
387
+ for node in ast.walk(tree):
388
+ if not (isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute)):
389
+ continue
390
+ if node.func.attr != "instrument":
391
+ continue
392
+ first = node.args[0] if node.args else None
393
+ if isinstance(first, ast.Constant) and first.value == adapter_name:
394
+ documented |= {kw.arg for kw in node.keywords if kw.arg}
395
+ return documented
396
+
397
+
398
+ @pytest.mark.parametrize("directory,module_name", sorted(DIR_ADAPTER.items()))
399
+ def test_a_guide_only_documents_options_the_adapter_reads(directory, module_name):
400
+ text = (DOCS / directory / "README.md").read_text(encoding="utf-8")
401
+ documented = _documented_options(text, module_name)
402
+ if not documented:
403
+ pytest.skip(f"{directory} documents no instrument() options")
404
+ real = _adapter_options(module_name)
405
+ invented = documented - real
406
+ assert not invented, (
407
+ f"{directory}/README.md documents instrument({module_name!r}, ...) options "
408
+ f"{sorted(invented)} that the adapter never reads. Unknown keys are "
409
+ f"dropped silently, so a reader following this gets no error and no effect. "
410
+ f"Real options: {sorted(real)}"
411
+ )