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,214 @@
1
+ """This package is PUBLIC and its wheel is published to PyPI.
2
+
3
+ It came out of a private monorepo, where naming a live tenant in a fixture or
4
+ leaving an internal hostname in a docstring was harmless. Here it is permanent:
5
+ a PyPI version cannot be recalled or reused, so anything that ships once ships
6
+ forever. The sibling `fp-cloud-cli` hit exactly this during its own move — a real
7
+ customer's slug and company name reached the tree in a bulk copy — which is why
8
+ this tripwire exists on both packages.
9
+
10
+ Customer names are held as SHA-256 digests, never in the clear. A deny-list that
11
+ spells out the name it exists to keep out of a public wheel publishes that name
12
+ just as surely as the fixture did, and this file ships in the sdist. Our OWN
13
+ names stay readable: they are already in LICENSE and pyproject.toml, so there is
14
+ nothing to withhold, and a contributor who trips over one needs to see which it
15
+ was.
16
+
17
+ The match is over SUBSTRINGS of each token, not whole tokens, because the
18
+ original leak was a slug *and* a company name built from it — one name inside a
19
+ longer one. To add an identifier:
20
+
21
+ python3 -c 'import hashlib,sys;print(hashlib.sha256(sys.argv[1].lower().encode()).hexdigest())' NAME
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import functools
27
+ import hashlib
28
+ import pathlib
29
+ import re
30
+
31
+ import failproofai_sdk
32
+
33
+ PKG = pathlib.Path(failproofai_sdk.__file__).resolve().parent
34
+ ROOT = PKG.parent
35
+
36
+ # Our own organisation names — public in this repo already, so in the clear. A
37
+ # fixture must still not use them: they identify a real tenant on a real deployment.
38
+ FORBIDDEN_OWN = {
39
+ "exosphere",
40
+ "exospherehost",
41
+ }
42
+
43
+ # Customer / vendor identifiers, digest → what it is (never the name itself).
44
+ # Kept in step with fp-cloud-cli/tests/test_no_customer_identifiers.py.
45
+ FORBIDDEN_DIGESTS = {
46
+ "140bd3c7a8606c97e18fb1f01c3a94f558eab6e2c5b27a56f9c3f5a940d8e2fd": "a customer's tenant slug",
47
+ }
48
+
49
+ # Internal artefacts from the private monorepo. Unlike the names above these are
50
+ # not confidential so much as WRONG to ship: a dev credential in a public
51
+ # quickstart gets pasted into production, and an internal-only table name in a
52
+ # docstring is an invitation to query something that does not exist for users.
53
+ FORBIDDEN_INTERNALS = {
54
+ "dev-admin-key": "the local-dev admin credential",
55
+ "agenteye-enterprise": "the private customer-release org",
56
+ "clickhouse": "an internal storage detail users never touch",
57
+ "org_ch_secret": "an internal server secret name",
58
+ }
59
+
60
+ # The vocabulary fixtures are supposed to use.
61
+ SANCTIONED_FIXTURE_ORGS = {"acme", "globex", "example", "initech", "umbrella"}
62
+
63
+ _TOKEN = re.compile(r"[a-z0-9]+")
64
+ _URL = re.compile(r"https?://([a-z0-9.-]+)", re.I)
65
+
66
+ # Substring lengths considered when hashing. The floor keeps the scan off two- and
67
+ # three-letter noise; the ceiling bounds the work on long tokens (hex digests, base64).
68
+ _MIN_LEN = 5
69
+ _MAX_LEN = 24
70
+
71
+
72
+ @functools.lru_cache(maxsize=None)
73
+ def _digest(text: str) -> str:
74
+ return hashlib.sha256(text.encode()).hexdigest()
75
+
76
+
77
+ def _hashed_hits(text: str, needles: frozenset[str] | None = None) -> set[str]:
78
+ """Digests from ``needles`` whose plaintext appears anywhere in ``text``.
79
+
80
+ ``text`` need not be lowercased by the caller. Returns digests, not matched
81
+ text: a failure message must not echo the identifier into a public CI log.
82
+ """
83
+ want = FORBIDDEN_DIGESTS.keys() if needles is None else needles
84
+ found = set()
85
+ for token in set(_TOKEN.findall(text.lower())):
86
+ for start in range(len(token)):
87
+ stop = min(len(token), start + _MAX_LEN)
88
+ for end in range(start + _MIN_LEN, stop + 1):
89
+ d = _digest(token[start:end])
90
+ if d in want:
91
+ found.add(d)
92
+ return found
93
+
94
+
95
+ def _sources() -> list[pathlib.Path]:
96
+ out = []
97
+ for base in (PKG, ROOT / "tests"):
98
+ out.extend(p for p in base.rglob("*.py") if "__pycache__" not in p.parts)
99
+ for name in ("README.md", "CHANGELOG.md", "pyproject.toml"):
100
+ p = ROOT / name
101
+ if p.is_file():
102
+ out.append(p)
103
+ for p in (ROOT / "skill").rglob("*"):
104
+ if p.is_file() and p.suffix in {".md", ".yaml", ".yml"}:
105
+ out.append(p)
106
+ return out
107
+
108
+
109
+ def _scannable() -> list[pathlib.Path]:
110
+ """Everything but this file, which names our own orgs to deny them."""
111
+ return [p for p in _sources() if p.name != pathlib.Path(__file__).name]
112
+
113
+
114
+ def _scannable_for_own_names() -> list[pathlib.Path]:
115
+ """As above, minus the one file our own org name legitimately belongs in.
116
+
117
+ `pyproject.toml`'s `authors` email is the published maintainer contact — it
118
+ is on the PyPI project page by design, and `fp-cloud-cli` publishes the same one.
119
+ It is still scanned for customer digests and internal artefacts below; only
120
+ the our-own-name rule is lifted, because that rule exists to keep a real
121
+ tenant out of a FIXTURE, not to redact the maintainer.
122
+ """
123
+ return [p for p in _scannable() if p.name != "pyproject.toml"]
124
+
125
+
126
+ def test_no_real_customer_or_vendor_identifiers():
127
+ hits = []
128
+ for p in _scannable_for_own_names():
129
+ for i, line in enumerate(p.read_text(encoding="utf-8", errors="replace").split("\n"), 1):
130
+ lowered = line.lower()
131
+ for needle in FORBIDDEN_OWN:
132
+ if needle in lowered:
133
+ hits.append(f"{p.relative_to(ROOT)}:{i}: {needle}")
134
+ # Customer digests are checked over EVERY file, pyproject.toml included —
135
+ # the exemption above is only for our own maintainer contact.
136
+ for p in _scannable():
137
+ for i, line in enumerate(p.read_text(encoding="utf-8", errors="replace").split("\n"), 1):
138
+ # Report the location and what class of identifier it is — never the name.
139
+ for digest in _hashed_hits(line):
140
+ hits.append(f"{p.relative_to(ROOT)}:{i}: {FORBIDDEN_DIGESTS[digest]}")
141
+ assert not hits, (
142
+ "real organisation names must not appear in a public package — use a fixture "
143
+ f"name such as {sorted(SANCTIONED_FIXTURE_ORGS)}:\n " + "\n ".join(hits)
144
+ )
145
+
146
+
147
+ def test_no_internal_artefacts_from_the_private_monorepo():
148
+ """Credentials and internal names that came across in the move."""
149
+ hits = []
150
+ for p in _scannable():
151
+ for i, line in enumerate(p.read_text(encoding="utf-8", errors="replace").split("\n"), 1):
152
+ lowered = line.lower()
153
+ for needle, what in FORBIDDEN_INTERNALS.items():
154
+ if needle in lowered:
155
+ hits.append(f"{p.relative_to(ROOT)}:{i}: {what}")
156
+ assert not hits, "internal artefacts in a public package:\n " + "\n ".join(hits)
157
+
158
+
159
+ def test_the_scan_actually_has_files_to_scan():
160
+ """Keeps the assertions above from passing vacuously if the layout moves."""
161
+ files = _sources()
162
+ assert len(files) > 15, (
163
+ f"only {len(files)} files scanned — the walk is not finding the package"
164
+ )
165
+ assert any(p.name == "_writer.py" for p in files)
166
+ assert any(p.parent.name == "tests" for p in files)
167
+
168
+
169
+ def test_the_hashed_scan_matches_substrings_and_only_them():
170
+ """The digests are opaque, so prove the matcher on a planted, invented name.
171
+
172
+ Without this, an off-by-one in the substring window turns the whole hashed
173
+ deny-list into an assertion that passes because it matches nothing.
174
+ """
175
+ planted = frozenset({_digest("quuxcorp")})
176
+ assert _hashed_hits("slug: quuxcorp", planted) == planted # bare token
177
+ assert _hashed_hits('name: "QuuxcorpInc"', planted) == planted # inside a longer name
178
+ assert _hashed_hits("host: quuxcorp-prod.example.com", planted) == planted
179
+ assert not _hashed_hits("slug: acme, name: Globex Corp", planted) # sanctioned fixtures
180
+ assert not _hashed_hits("quux corp", planted) # not one token
181
+
182
+
183
+ def test_no_internal_hostnames_leaked():
184
+ """A customer's deployment hostname identifies them as surely as their name."""
185
+ hits = []
186
+ for p in _scannable():
187
+ for i, line in enumerate(p.read_text(encoding="utf-8", errors="replace").split("\n"), 1):
188
+ for host in _URL.findall(line):
189
+ labels = host.lower().split(".")
190
+ if any(label in FORBIDDEN_OWN for label in labels) or _hashed_hits(host):
191
+ hits.append(f"{p.relative_to(ROOT)}:{i}")
192
+ assert not hits, f"internal/customer hostnames in a public package: {hits}"
193
+
194
+
195
+ def test_this_file_does_not_name_the_customers_it_denies():
196
+ """The deny-list must not restate, in the clear, what it holds as a digest.
197
+
198
+ ``_scannable()`` skips this file so the ``FORBIDDEN_OWN`` literals above do not
199
+ trip the scan on themselves. That exemption is about OUR names, which are public
200
+ in this repo already. It must never extend to a customer's: this file ships in
201
+ the sdist, so a name written here is published exactly like the fixture that
202
+ started all this. It needs its own test precisely because the exemption is what
203
+ blinds the main scan to it.
204
+ """
205
+ src = pathlib.Path(__file__)
206
+ hits = [
207
+ f"{src.name}:{i}: {FORBIDDEN_DIGESTS[digest]}"
208
+ for i, line in enumerate(src.read_text(encoding="utf-8", errors="replace").split("\n"), 1)
209
+ for digest in _hashed_hits(line)
210
+ ]
211
+ assert not hits, (
212
+ "this file names, in the clear, an identifier it exists to keep out of a "
213
+ "public package:\n " + "\n ".join(hits)
214
+ )
@@ -0,0 +1,185 @@
1
+ """Packaging contracts, read from `importlib.metadata` — never from pyproject.
2
+
3
+ Two reasons this file never opens `pyproject.toml`:
4
+
5
+ 1. `tomllib` is 3.11+ and this SDK's floor is 3.10, so parsing it here would
6
+ make the packaging tests skip exactly on the leg that catches 3.10 problems.
7
+ 2. `pyproject.toml` is the *input*. What ships is the wheel's `METADATA`, and
8
+ the failure mode these tests exist to catch — an extra that silently drags
9
+ the wrong `failproofai_sdk` off PyPI — happens at install time, from METADATA.
10
+
11
+ The rules being enforced:
12
+
13
+ * **zero runtime dependencies.** `import failproofai_sdk` must work in an environment
14
+ with nothing else installed, so every `Requires-Dist` carries an
15
+ `extra == "..."` marker.
16
+ * **no self-referential extra.** On public PyPI `failproofai_sdk` is the *CLI*; a
17
+ `Requires-Dist: failproofai_sdk[...]` would install the CLI over this SDK. This is
18
+ also why the adapters ship as extras on one distribution rather than as
19
+ `failproofai_sdk-langgraph` and friends.
20
+ * **no `[all]` extra** — a resolution bomb that exists only for CI's
21
+ convenience.
22
+ * **the extras and the adapter registry agree.** They are two hand-maintained
23
+ lists of the same set, in two files, with no codegen between them.
24
+ """
25
+
26
+ import re
27
+ import os
28
+ import subprocess
29
+ from pathlib import Path
30
+
31
+ import failproofai_sdk
32
+ import sys
33
+ import textwrap
34
+
35
+ import importlib.metadata as metadata
36
+
37
+ import pytest
38
+
39
+ from failproofai_sdk import integrations
40
+
41
+ DIST = "failproofai_sdk"
42
+
43
+
44
+ def requirements() -> list[str]:
45
+ return list(metadata.requires(DIST) or [])
46
+
47
+
48
+ def declared_extras() -> set[str]:
49
+ return set(metadata.distribution(DIST).metadata.get_all("Provides-Extra") or [])
50
+
51
+
52
+ def test_the_distribution_is_installed():
53
+ # Guards every other test in this file: `requires()` returns None for a
54
+ # dist that is not installed, and `for req in []` passes vacuously.
55
+ assert metadata.version(DIST)
56
+ assert requirements()
57
+
58
+
59
+ def test_every_requirement_is_gated_behind_an_extra():
60
+ ungated = [req for req in requirements() if "extra ==" not in req]
61
+ assert ungated == [], (
62
+ "failproofai_sdk must have ZERO runtime dependencies; these would be installed "
63
+ f"unconditionally: {ungated}"
64
+ )
65
+
66
+
67
+ #: Names a requirement here must never be. `failproofai-sdk` is self-reference —
68
+ #: `pip install failproofai-sdk[langchain]` resolving through an extra back onto
69
+ #: this distribution. `agenteye` is the RETIRED distribution: on public PyPI that
70
+ #: name is the CLI, so pinning it would install the CLI over the SDK.
71
+ _FORBIDDEN_REQUIREMENT_NAMES = frozenset({"failproofai-sdk", "agenteye"})
72
+
73
+
74
+ def test_no_requirement_names_the_retired_distribution():
75
+ """Both sides normalized, which is the whole point.
76
+
77
+ This compared a dash-normalized name against `DIST`, which keeps its
78
+ UNDERSCORE — so `"failproofai-sdk" != "failproofai_sdk"` was true for every
79
+ possible input and the assertion could not fail. Negative-controlled: a
80
+ planted `failproofai-sdk[langchain]; extra == "all"` used to pass.
81
+ """
82
+ for req in requirements():
83
+ name = re.split(r"[\s\[<>=!~;(]", req.strip(), maxsplit=1)[0]
84
+ assert name.lower().replace("_", "-") not in _FORBIDDEN_REQUIREMENT_NAMES, (
85
+ f"requirement {req!r} names a distribution that must never appear here: "
86
+ "self-reference resolves an extra back onto this package, and `agenteye` "
87
+ "is the CLI on public PyPI, so either would install something else over "
88
+ "the SDK"
89
+ )
90
+
91
+
92
+ def test_there_is_no_all_extra():
93
+ assert "all" not in declared_extras()
94
+
95
+
96
+ def test_every_extra_maps_to_a_registered_adapter():
97
+ for extra in declared_extras() - {"dev"}:
98
+ # Raises ValueError if the extra is not a name (or alias) the registry
99
+ # knows — i.e. an extra nobody can instrument.
100
+ assert integrations._canonical(extra) in integrations.available()
101
+
102
+
103
+ def test_every_registered_adapter_has_an_extra():
104
+ covered = {integrations._canonical(extra) for extra in declared_extras() - {"dev"}}
105
+ assert covered == set(integrations.available())
106
+
107
+
108
+ def test_every_extra_requirement_pins_a_floor_and_a_ceiling():
109
+ # Without a ceiling, a clean build a year from now pulls the next major, the
110
+ # callback API shifts, and the adapter stops receiving events while raising
111
+ # nothing at all.
112
+ for req in requirements():
113
+ if 'extra == "dev"' in req:
114
+ continue
115
+ assert ">=" in req, f"{req} has no floor"
116
+ assert "<" in req, f"{req} has no ceiling"
117
+
118
+
119
+ IMPORT_GUARD = textwrap.dedent(
120
+ """
121
+ import sys
122
+
123
+ baseline = set(sys.modules)
124
+ import failproofai_sdk
125
+
126
+ added = set(sys.modules) - baseline
127
+ third_party = sorted(
128
+ name for name in added
129
+ if name.split(".")[0] not in sys.stdlib_module_names
130
+ and not name.startswith("failproofai_sdk")
131
+ )
132
+ integrations = sorted(n for n in sys.modules if n.startswith("failproofai_sdk.integrations"))
133
+
134
+ assert not third_party, "import failproofai_sdk pulled in third-party modules: %r" % third_party
135
+ assert not integrations, "import failproofai_sdk imported the adapters: %r" % integrations
136
+ assert failproofai_sdk._writer is not sys.modules["failproofai_sdk._writer"], "_writer is the module"
137
+ print("OK")
138
+ """
139
+ )
140
+
141
+
142
+ def test_importing_the_package_stays_clean(tmp_path):
143
+ """A subprocess, because the assertion is about a *cold* interpreter.
144
+
145
+ In-process, every framework any other test file imported is already in
146
+ `sys.modules` and the check is vacuous.
147
+ """
148
+ # `cwd=tmp_path` is what makes the interpreter cold — away from the source
149
+ # tree, so nothing is importable by accident. PYTHONPATH then puts the
150
+ # package back deliberately, which is what lets this run in an environment
151
+ # where it is not pip-installed (a container running the suite off a mounted
152
+ # checkout). Without it the test does not fail loudly, it fails with
153
+ # ModuleNotFoundError and looks like a packaging bug that is not there.
154
+ env = dict(os.environ, PYTHONPATH=str(Path(failproofai_sdk.__file__).resolve().parents[1]))
155
+ result = subprocess.run(
156
+ [sys.executable, "-c", IMPORT_GUARD],
157
+ capture_output=True,
158
+ text=True,
159
+ cwd=str(tmp_path),
160
+ env=env,
161
+ )
162
+ assert result.returncode == 0, result.stderr
163
+ assert "OK" in result.stdout
164
+
165
+
166
+ def test_instrument_is_lazy_until_it_is_called():
167
+ # `failproofai_sdk.instrument` is a thin wrapper whose import sits *inside* the
168
+ # function body; the guard test above proves the module-level import is
169
+ # absent, this proves the wrapper still exists to be called.
170
+ assert callable(sdk_instrument())
171
+
172
+
173
+ def sdk_instrument():
174
+ import failproofai_sdk
175
+
176
+ return failproofai_sdk.instrument
177
+
178
+
179
+ @pytest.mark.parametrize("name", ["langchain", "crewai", "llama_index", "pydantic_ai"])
180
+ def test_the_registry_points_at_a_module_inside_this_package(name):
181
+ path = integrations._REGISTRY[name]
182
+ assert path.startswith("failproofai_sdk.integrations.")
183
+ # The registry entry is a STRING, imported on demand. If this ever becomes a
184
+ # module object, `import failproofai_sdk` stops being zero-dependency.
185
+ assert isinstance(path, str)
@@ -0,0 +1,178 @@
1
+ """The spool root IS the failproofai umbrella, and no environment can move it out.
2
+
3
+ This file used to assert the opposite, and the reason it flipped is worth
4
+ keeping: the umbrella was previously behind an ``AGENTEYE_SPOOL_TO_FAILPROOFAI``
5
+ opt-in that ALSO required ``~/.failproofai/custom-agents`` to already exist.
6
+ Nothing ever created that directory — not this SDK, not ``failproofaid``, not
7
+ either installer — so the second condition was never satisfied and the opt-in
8
+ never fired once. It was documented, tested, and dead.
9
+
10
+ The rule now: the SDK ships beside ``failproofaid``, which watches BOTH roots,
11
+ so on any host running it the default is a no-op that only changes which
12
+ directory the files appear in. The old root keeps being watched indefinitely, so
13
+ an unupgraded SDK keeps working and batches already spooled there still drain.
14
+
15
+ The case that genuinely breaks is a host running the OLDER
16
+ ``agenteye-collector``, which resolves ``$AGENTEYE_HOME`` or ``~/.agenteye`` and
17
+ nothing else.
18
+
19
+ ``AGENTEYE_HOME`` USED TO BE THAT HOST'S ESCAPE HATCH, AND NO LONGER IS. It sat
20
+ above the default in resolution, so it could aim this SDK anywhere from the
21
+ environment — including as a side effect of an operator exporting it for the
22
+ collector, which is the component that actually reads it. A redirect that
23
+ silent is not a good property for the one variable named after a product this
24
+ package no longer belongs to. Resolution is now exactly `set_base_dir()` then
25
+ the umbrella, and the bridges for that host are listed in
26
+ ``_resolver.get_base_dir``: run ``failproofaid`` (it watches both roots), point
27
+ the COLLECTOR at the umbrella, or call ``set_base_dir()`` explicitly.
28
+ """
29
+ from pathlib import Path
30
+
31
+ import pytest
32
+
33
+ from failproofai_sdk import _resolver
34
+
35
+
36
+ @pytest.fixture(autouse=True)
37
+ def _clean(monkeypatch):
38
+ _resolver.set_base_dir(None)
39
+ monkeypatch.delenv("AGENTEYE_HOME", raising=False)
40
+ monkeypatch.delenv("FAILPROOFAI_HOME", raising=False)
41
+ yield
42
+ _resolver.set_base_dir(None)
43
+
44
+
45
+ # ─────────────────────────────────────────────────────────────────────────────
46
+ # The default
47
+ # ─────────────────────────────────────────────────────────────────────────────
48
+
49
+
50
+ def test_the_default_is_the_failproofai_umbrella(tmp_path, monkeypatch):
51
+ """THE CHANGE. A clean machine writes to the umbrella, with nothing set."""
52
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
53
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
54
+
55
+
56
+ def test_the_umbrella_does_not_have_to_exist_first(tmp_path, monkeypatch):
57
+ """The regression that made the old opt-in dead.
58
+
59
+ Requiring the directory to pre-exist means it can never be the place a first
60
+ batch is written — nothing creates it, so the branch is unreachable. The
61
+ writer mkdirs what it is about to write into, so resolution must not care.
62
+ """
63
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
64
+ assert not (tmp_path / ".failproofai").exists()
65
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
66
+
67
+
68
+ def test_an_existing_legacy_root_does_not_drag_the_default_back(tmp_path, monkeypatch):
69
+ """Presence of ``~/.agenteye`` is not a vote.
70
+
71
+ It exists on any machine that merely ran the old CLI once — it holds that
72
+ CLI's `cli.json` — so treating it as a signal would pin those machines to
73
+ the old path forever despite no collector ever having watched it.
74
+ """
75
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
76
+ (tmp_path / ".agenteye" / "events").mkdir(parents=True)
77
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
78
+
79
+
80
+ # ─────────────────────────────────────────────────────────────────────────────
81
+ # The retired escape hatch — these assert that it is inert, not that it works
82
+ # ─────────────────────────────────────────────────────────────────────────────
83
+
84
+
85
+ def test_agenteye_home_cannot_drag_the_spool_back_to_the_legacy_root(tmp_path, monkeypatch):
86
+ """THE CHANGE. Exporting it does nothing to this SDK any more.
87
+
88
+ This is the assertion that makes the guarantee testable: whatever is in the
89
+ environment, this package writes inside the umbrella. An operator exporting
90
+ ``AGENTEYE_HOME`` for ``agenteye-collector`` — the component that still
91
+ reads it — no longer moves this SDK's spool as a side effect.
92
+ """
93
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
94
+ monkeypatch.setenv("AGENTEYE_HOME", str(tmp_path / ".agenteye"))
95
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
96
+
97
+
98
+ def test_agenteye_home_cannot_point_the_spool_anywhere_else_either(tmp_path, monkeypatch):
99
+ """Not just the legacy root — the variable has no effect at all."""
100
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
101
+ monkeypatch.setenv("AGENTEYE_HOME", str(tmp_path / "somewhere" / "else"))
102
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
103
+
104
+
105
+ def test_an_empty_agenteye_home_is_ignored_rather_than_resolving_to_cwd(tmp_path, monkeypatch):
106
+ """``AGENTEYE_HOME=`` in a CI env file must not spool into the repo.
107
+
108
+ Kept although the variable is no longer read: an empty value was the shape
109
+ most likely to resolve to a relative path, and the test costs nothing.
110
+ """
111
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
112
+ monkeypatch.setenv("AGENTEYE_HOME", "")
113
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
114
+
115
+
116
+ def test_no_environment_variable_can_take_the_spool_outside_the_umbrella(tmp_path, monkeypatch):
117
+ """The guarantee in one line: FAILPROOFAI_HOME MOVES the umbrella, and the
118
+ ``custom-agents`` segment is appended unconditionally, so there is no
119
+ environment in which this SDK writes somewhere that is not inside it."""
120
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
121
+ monkeypatch.setenv("FAILPROOFAI_HOME", str(tmp_path / "relocated"))
122
+ monkeypatch.setenv("AGENTEYE_HOME", str(tmp_path / "hijack"))
123
+ resolved = _resolver.get_base_dir()
124
+ assert resolved == tmp_path / "relocated" / "custom-agents"
125
+ assert resolved.name == "custom-agents"
126
+
127
+
128
+ # ─────────────────────────────────────────────────────────────────────────────
129
+ # Precedence and the umbrella path itself
130
+ # ─────────────────────────────────────────────────────────────────────────────
131
+
132
+
133
+ def test_set_base_dir_beats_every_environment_variable(tmp_path, monkeypatch):
134
+ monkeypatch.setenv("FAILPROOFAI_HOME", str(tmp_path / "env"))
135
+ monkeypatch.setenv("AGENTEYE_HOME", str(tmp_path / "legacy"))
136
+ _resolver.set_base_dir(tmp_path / "explicit")
137
+ assert _resolver.get_base_dir() == tmp_path / "explicit"
138
+
139
+
140
+ def test_set_base_dir_is_the_only_way_to_choose_the_legacy_root(tmp_path, monkeypatch):
141
+ """Still possible, but only as an explicit, visible call.
142
+
143
+ That is the whole trade: the destination stays reachable for anyone who
144
+ genuinely wants it, and reaching it now requires saying so at the call site
145
+ rather than inheriting somebody else's environment.
146
+ """
147
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
148
+ _resolver.set_base_dir(tmp_path / ".agenteye")
149
+ assert _resolver.get_base_dir() == tmp_path / ".agenteye"
150
+
151
+
152
+ def test_failproofai_home_moves_the_umbrella(tmp_path, monkeypatch):
153
+ """Mirrors ``FAILPROOFAI_HOME`` in fp-home.ts; containers rely on it."""
154
+ monkeypatch.setenv("FAILPROOFAI_HOME", str(tmp_path / "elsewhere"))
155
+ assert _resolver.get_base_dir() == tmp_path / "elsewhere" / "custom-agents"
156
+
157
+
158
+ def test_the_retired_opt_in_variable_no_longer_exists():
159
+ """It was the entry point to the dead branch; leaving it would mislead.
160
+
161
+ Anyone who exported it wanted the umbrella and now gets it by default, so
162
+ removing it changes no behaviour for them — only the docs they read.
163
+ """
164
+ assert not hasattr(_resolver, "SPOOL_OPT_IN_ENV")
165
+
166
+
167
+ def test_the_retired_opt_in_variable_has_no_effect_if_someone_still_exports_it(
168
+ tmp_path, monkeypatch
169
+ ):
170
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
171
+ monkeypatch.setenv("AGENTEYE_SPOOL_TO_FAILPROOFAI", "0")
172
+ assert _resolver.get_base_dir() == tmp_path / ".failproofai" / "custom-agents"
173
+
174
+
175
+ def test_the_legacy_root_is_still_spelled_somewhere_findable(tmp_path, monkeypatch):
176
+ """The migration notes and the escape hatch both name it; one definition."""
177
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
178
+ assert _resolver.legacy_agenteye_dir() == tmp_path / ".agenteye"